Ключевые слова:lib, gcc, share, (найти похожие документы)
From: Андрей Киселев <kis_an at mail.ru>
Newsgroups: http://gazette.linux.ru.net/
Date: Mon, 25 Dec 2003 14:31:37 +0000 (UTC)
Subject: Создание библиотек многократного использования
Оригинал: http://gazette.linux.ru.net/lg81/tougher.html
Создание библиотек многократного использования
Автор: Rob Tougher
Перевод: Андрей Киселев
_________________________________________________________________
1. Введение
]2. Библиотека должна быть простой в использовании
2.1 Простота
2.2 Непротиворечивость
2.3 Интуитивность
3. Тщательное тестирование
4. Детализация сообщений об ошибках
5. Заключение
1. Введение
-------------
Библиотеки расширяют возможности разработчиков программного
обеспечения. Они содержат код, который разработчики могут использовать
в своих проектах. Программные библиотеки, предназначенные для Linux,
обычно доступны как в исходных текстах, так и в виде бинарных файлов.
Хорошо проработанная библиотека:
* проста в использовании
* безупречна в работе
* дает подробную информацию об ошибках, возникающих во время
выполнения
В этой статье описываются все вышеупомянутые принципы создания
библиотек и приводятся примеры на C++
Эта статья для вас?
Прежде чем заняться разработкой новой библиотеки задайте себе пару
вопросов:
* Понадобится ли кому-нибудь (включая и вас самого) эта библиотека?
* Если да, то не существует ли подобная библиотека?
Нет смысла разрабатывать библиотеку, если она никому не нужна или,
если подобная библиотека уже существует.
2. Библиотека должна быть простой в использовании.
----------------------------------------------------
Создание любой библиотеки должно начинаться с тщательной проработки
интерфейса. Интерфейсы, написанные на процедурно-ориентированных
языках, подобных C, представляют собой функции. В
объектно-ориентированных языках, таких как C++ или Python, интерфейсы
могут быть как функциями так и классами.
Основное правило при проработке интерфейса:
* Чем проще, тем лучше
Как разработчику, мне часто приходится сталкиваться с необходимостью
поиска правильного баланса между функциональностью и простотой в
использовании. Вышеприведенное правило помогает мне в этом.
Придерживайтесь следующих рекомендаций и успех вам будет обеспечен.
2.1 Простота
----------------
Чем сложнее библиотека, тем труднее ею пользоваться.
* Простота не хуже воровства!
Недавно я столкнулся с библиотекой, написанной на C++, которая
содержала один единственный класс. В этом классе было определено 150
методов. 150 методов! Разработчик, по всей видимости, был ветераном
языка C и использовал класс C++ как обычный сишный модуль. Класс
получился настолько сложным, что разобраться в нем оказалось очень
непростой задачей.
Избегайте построения сложных интерфейсов в своих разработках, тогда
они будут легче восприниматься.
2.2 Непротиворечивость.
---------------------------
Непротиворечивый интерфейс воспринимается и запоминается намного
легче. Запомнив правила работы с интерфейсом, пользователи довольно
легко применяют их при работе со всеми классами и методами в
библиотеке, даже если ранее пользоваться ими не приходилось.
Давайте рассотрим пример использования методов, предоставляющих доступ
к скрытым (приватным) полям класса:
class point
{
public:
int get_x() { return m_x; }
int set_x ( int x ) { m_x = x; }
int y() { return m_y; }
private:
int m_x, m_y;
};
Видите несоответствия? Доступ к полю m_x выполняется через метод с
именем "get_x()", а к полю m_y -- через "y()". Такая несогласованность
вынуждает пользователя всякий раз обращаться к определениям методов
перед тем как использовать их.
Еще один пример неудачной реализации интерфейса:
class DataBase
{
public:
recordset get_recordset ( const std::string sql );
void RunSQLQuery ( std::string query, std::string connection );
std::string connectionString() { return m_connection_string; }
long m_sError;
private:
std::string m_connection_string;
};
Вы можете самостоятельно найти ошибки? По-крайней мере, я заметил
следующие:
* Разработчик не старался придерживаться единого стиля именования.
* Для обозначения строки с SQL-запросом используются два различных
имени -- sql и query
* Не определен метод доступа к полю m_sError
* В списке аргументов метода get_recordset() отсутствует параметр
connection
Вот пересмотренная версия класса с исправленными ошибками:
class database
{
public:
recordset get_recordset ( const std::string sql );
void run_sql_query ( std::string sql );
std::string connection_string() { return m_connection_string; }
long error() { return m_error; }
private:
std::string m_connection_string;
long m_error;
};
Разрабатывайте интерфейсы настолько непротиворечивыми, насколько это
возможно - такие интерфейсы запоминаются намного легче.
2.3 Интуитивность
---------------------
Подходите к разработке интерфейса с точки зрения пользователя, а не с
точки зрения внутреннего устройства.
Самый простой способ разработки интуитивно понятного интерфейса
состоит в том, чтобы попробовать записать код, который будет
использовать библиотечные вызовы прежде, чем фактически переходить к
написанию кода библиотеки. Это позволяет взглянуть на будущую
библиотеку с точки зрения пользователя.
Давайте разберем небольшой пример. Недавно, размышляя о создании
криптографической библиотеки, основанной на OpenSSL, я написал такой
код:
crypto::message msg ( "My data" );
crypto::key k ( "my key" );
// blowfish algorithm
msg.encrypt ( k, crypto::blowfish );
msg.decrypt ( k, crypto::blowfish ):
// rijndael algorithm
msg.encrypt ( k, crypto::rijndael );
msg.decrypt ( k, crypto::rijndael ):
Это помогло мне взглянуть на будущую библиотеку глазами пользователя.
Если я решусь на создание такой библиотеки, то эти идеи будут для меня
отправной точкой при ее реализации.
3. Тщательное тестирование
----------------------------
Код библиотеки должен быть безупречен. Хорошо, пусть не безупречен,
но, по крайней мере, он должен быть настолько близок к безупречному,
насколько это возможно. Пользователю необходима уверенность в том, что
библиотека безошибочно выполняет возложенные на нее задачи.
* Зачем пользоваться библиотекой, если она работает с ошибками?
При создании своих библиотек я стараюсь автоматизировать процесс
отладки. Для каждой библиотеки создается соответствующее тестовое
приложение, которое проверяет ее функциональность.
А теперь допустим, что я решил написать криптографическую библиотеку,
которую упоминал выше. Тогда тестовое приложение, для ее проверки,
будет выглядеть примерно так:
#include "crypto.hpp"
int main ( int argc, int argv[] )
{
//
// 1. Зашифровать, расшифровать и проверить
//
crypto::message msg ( "Hello there" );
crypto::key k ( "my key" );
msg.encrypt ( k, crypto::blowfish );
msg.decrypt ( k, crypto::blowfish );
if ( msg.data() != "Hello there" )
{
// Ошибка!
}
//
// 2. Зашифровать по одному алгоритму,
// расшифровать по другому алгоритму
// и проверить.
//
// и т.д....
}
В процессе разработки, время от времени, я запускаю тестовое
приложение, чтобы убедиться, что библиотека не содержит ошибок.
4. Детализация сообщений об ошибках
-------------------------------------
Пользователь всегда должен предупреждаться о ситуациях, когда
библиотека не может выполнить тот или иной запрос.
* Предупреждайте пользователя всегда, когда возникают проблемы
Библиотеки, написанные на C++, как правило, используют механизм
исключений для передачи сообщения об ошибке. Рассмотрим следующий
пример:
#include <string>
#include <iostream>
class car
{
public:
void accelerate() { throw error ( "Could not accelerate" ); }
};
class error
{
public:
Error ( std::string text ) : m_text ( text ) {}
std::string text() { return m_text; }
private:
std::string m_text;
};
int main ( int argc, int argv[] )
{
car my_car;
try
{
my_car.accelerate();
}
catch ( error& e )
{
std::cout << e.text() << "\n";
}
}
Класс car использует ключевое слово throw для возбуждения
исключительной ситуации и передачи сообщения об ошибке в вызвавшую
функцию. Вызывающая функция "ловит" исключение с помощью конструкции
try ... catch и обрабатывает его.
5. Заключение
---------------
В этой статье я постарался рассказать о важных правилах написания
хорошего кода библиотек. Надеюсь, что мне удалось это сделать в
достаточной степени, чтобы вы могли воспользоваться ими при создании
своих библиотек.
Rob Tougher
Copyright (C) 2002, Rob Tougher.
Copying license http://www.linuxgazette.com/copying.html
Published in Issue 81 of Linux Gazette, August 2002