From: Андрей Киселев <kis_an at mail.ru>
Newsgroups: http://gazette.linux.ru.net/
Date: Mon, 25 Dec 2003 14:31:37 +0000 (UTC)
Subject: Создание библиотек многократного использования
Создание библиотек многократного использования
Автор: 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++ как обычный сишный модуль. Класс
получился настолько сложным, что разобраться в нем оказалось очень
непростой задачей.
Избегайте построения сложных интерфейсов в своих разработках, тогда
они будут легче восприниматься.
Непротиворечивый интерфейс воспринимается и запоминается намного
легче. Запомнив правила работы с интерфейсом, пользователи довольно
легко применяют их при работе со всеми классами и методами в
библиотеке, даже если ранее пользоваться ими не приходилось.
Давайте рассотрим пример использования методов, предоставляющих доступ
к скрытым (приватным) полям класса:
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()". Такая несогласованность
вынуждает пользователя всякий раз обращаться к определениям методов
перед тем как использовать их.
Вы можете самостоятельно найти ошибки? По-крайней мере, я заметил
следующие:
* Разработчик не старался придерживаться единого стиля именования.
* Для обозначения строки с SQL-запросом используются два различных
имени -- sql и query
* Не определен метод доступа к полю m_sError
* В списке аргументов метода get_recordset() отсутствует параметр
connection
Вот пересмотренная версия класса с исправленными ошибками:
Разрабатывайте интерфейсы настолько непротиворечивыми, насколько это
возможно - такие интерфейсы запоминаются намного легче.
2.3 Интуитивность
---------------------
Подходите к разработке интерфейса с точки зрения пользователя, а не с
точки зрения внутреннего устройства.
Самый простой способ разработки интуитивно понятного интерфейса
состоит в том, чтобы попробовать записать код, который будет
использовать библиотечные вызовы прежде, чем фактически переходить к
написанию кода библиотеки. Это позволяет взглянуть на будущую
библиотеку с точки зрения пользователя.
Давайте разберем небольшой пример. Недавно, размышляя о создании
криптографической библиотеки, основанной на OpenSSL, я написал такой
код:
crypto::message msg ( "My data" );
crypto::key k ( "my key" );
Это помогло мне взглянуть на будущую библиотеку глазами пользователя.
Если я решусь на создание такой библиотеки, то эти идеи будут для меня
отправной точкой при ее реализации.
Код библиотеки должен быть безупречен. Хорошо, пусть не безупречен,
но, по крайней мере, он должен быть настолько близок к безупречному,
насколько это возможно. Пользователю необходима уверенность в том, что
библиотека безошибочно выполняет возложенные на нее задачи.
* Зачем пользоваться библиотекой, если она работает с ошибками?
При создании своих библиотек я стараюсь автоматизировать процесс
отладки. Для каждой библиотеки создается соответствующее тестовое
приложение, которое проверяет ее функциональность.
А теперь допустим, что я решил написать криптографическую библиотеку,
которую упоминал выше. Тогда тестовое приложение, для ее проверки,
будет выглядеть примерно так:
#include "crypto.hpp"
int main ( int argc, int argv[] )
{
//
// 1. Зашифровать, расшифровать и проверить
//
crypto::message msg ( "Hello there" );
crypto::key k ( "my key" );
//
// 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;
};
Класс 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