Программирование >>  Поддержка объектно-ориентированного программирования 

1 ... 105 106 107 [ 108 ] 109 110 111 ... 120


Здесь функции-члены образуют интерфейс на слишком низком уровне абстракции. Как правило классы с интерфейсом такого уровня относятся к специфике реализации большого компонента, если они вообще могут к чему-нибудь относиться. В идеале параметр функции из интерфейса должен сопровождаться такой информацией, которой достаточно для его понимания. Можно сформулировать такое правило: надо уметь передавать запросы на обслуживание удаленному серверу по узкому каналу.

Язык С++ раскрывает представление класса как часть интерфейса. Это представление может быть скрытым (с помощью private или protected), но обязательно доступным транслятору, чтобы он мог разместить автоматические (локальные) переменные, сделать подстановку тела функции и т.д. Отрицательным следствием этого является то, что использование типов классов в представлении класса может привести к возникновению нежелательных зависимостей. Приведет ли использование членов типа Y и Z к проблемам, зависит от того, каковы в действительности типы Y и Z. Если это достаточно простые типы, наподобие complex или String, то их использование будет вполне допустимым в большинстве случаев. Такие типы можно считать устойчивыми, и необходимость включать определения их классов будет вполне допустимой нагрузкой для транслятора. Если же Y и Z сами являются классами интерфейса большого компонента (например, типа графической системы или системы обеспечения банковских счетов), то прямую зависимость от них можно считать неразумной. В таких случаях предпочтительнее использовать член, являющийся указателем или ссылкой:

class X { Y* a;

Z& b; ...

При этом способе определение X отделяется от определений Y и Z, т.е. теперь определение X зависит только от имен Y и Z. Реализация X, конечно, будет по-прежнему зависеть от определений Y и Z, но это уже не будет оказывать неблагоприятного влияния на пользователей X.

Вышесказанное иллюстрирует важное утверждение: У интерфейса, скрывающего значительный объем информации (что и должен делать полезный интерфейс), должно быть существенно меньше зависимостей, чем у реализации, которая их скрывает. Например, определение класса X можно транслировать без доступа к определениям Y и Z. Однако, в определениях функций-членов класса X, которые работают со ссылками на объекты Y и Z, доступ к определениям Y и Z необходим. При анализе зависимостей следует рассматривать раздельно зависимости в интерфейсе и в реализации. В идеале для обоих видов зависимостей граф зависимостей системы должен быть направленным нецикличным графом, что облегчает понимание и тестирование системы. Однако, эта цель более важна и чаще достижима для реализаций, чем для интерфейсов.

Отметим, что класс определяет три интерфейса:

class X { private:

доступно только для членов и друзей protected:

доступно только для членов и друзей, а также для членов и друзей производных классов public:

общедоступно

Члены должны образовывать самый ограниченный из возможных интерфейсов. Иными словами, член должен быть описан как private, если нет причин для более широкого доступа к нему; если же таковые есть, то член должен быть описан как protected, если нет дополнительных причин задать его как public. В большинстве случаев плохо задавать все данные, представляемые членами, как public. Функции и классы, образующие общий интерфейс, должны быть спроектированы таким образом, чтобы представление класса совпадало с его ролью в проекте как средства представления понятий. Напомним, что друзья являются частью общего интерфейса.

Отметим, что абстрактные классы можно использовать для представления понятия упрятывания более высокого уровня ($$1.4.6, $$6.3, $$13.3).



12.5 Свод правил

В этой главе мы коснулись многих тем, но, как правило, избегали давать настоятельные и конкретные рекомендации по рассматриваемым вопросам. Это отвечает моему убеждению, что нет единственно верного решения . Принципы и приемы следует применять способом, наиболее подходящим для конкретной задачи. Здесь требуются вкус, опыт и разум. Тем не менее, можно предложить свод правил, которые разработчик может использовать в качестве ориентиров, пока не приобретет достаточно опыта, чтобы выработать лучшие. Этот свод правил приводится ниже.

Он может служить отправной точкой в процессе выработки основных направлений проекта конкретной задачи, или же он может использоваться организацией в качестве проверочного списка. Подчеркну еще раз, что эти правила не являются универсальными и не могут заменить собой размышления.

Нацеливайте пользователя на применение абстракции данных и объектно-ориентированного программирования.

- Постепенно переходите на новые методы, не спешите.

- Используйте возможности С++ и методы объектно-ориентированного программирования только по мере надобности.

Добейтесь соответствия стиля проекта и программы.

Концентрируйте внимание на проектировании компонента.

Используйте классы для представления понятий.

- Используйте общее наследование для представления отношений есть .

- Используйте принадлежность для представления отношений имеет .

- Убедитесь, что отношения использования понятны, не образуют циклов, и что число их минимально.

- Активно ищите общность среди понятий области приложения и реализации, и возникающие в результате более общие понятия представляйте как базовые классы.

Определяйте интерфейс так, чтобы открывать минимальное количество требуемой информации:

- Используйте, всюду где это можно, частные данные и функции-члены.

- Используйте описания public или protected, чтобы отличить запросы разработчика производных классов от запросов обычных пользователей.

- Сведите к минимуму зависимости одного интерфейса от других.

- Поддерживайте строгую типизацию интерфейсов.

- Задавайте интерфейсы в терминах типов из области приложения. Дополнительные правила можно найти $$11 .5.



ГЛАВА 13. ПРОЕКТИРОВАНИЕ БИБЛИОТЕК

Проект библиотеки - это проект языка, (фольклор фирмы Bell Laboratories)

... и наоборот. - А. Кениг

Эта глава содержит описание различных приемов, оказавшихся полезными при создании библиотек для языка С++. В частности, в ней рассматриваются конкретные типы, абстрактные типы, узловые классы, управляющие классы и интерфейсные классы. Помимо этого обсуждаются понятия обширного интерфейса и структуры области приложения, использование динамической информации о типах и методы управления памятью. Внимание акцентируется на том , какими свойствами должны обладать библиотечные классы, а не на специфике языковых средств, которые используются для реализации таких классов, и не на определенных полезных функциях, которые должна предоставлять библиотека.

13.1 Введение

Разработка библиотеки общего назначения - это гораздо более трудная задача, чем создание обычной программы. Программа - это решение конкретной задачи для конкретной области приложения, тогда как библиотека должна предоставлять возможность решение для множества задач, связанных с многими областями приложения. В обычной программе позволительны сильные допущения об ее окружении, тогда как хорошую библиотеку можно успешно использовать в разнообразных окружениях, создаваемых множеством различных программ. Чем более общей и полезной окажется библиотека, тем в большем числе окружений она будет проверяться, и тем жестче будут требования к ее корректности, гибкости, эффективности, расширяемости, переносимости, непротиворечивости, простоте, полноте, легкости использования и т.д. Все же библиотека не может дать вам все, поэтому нужен определенный компромисс. Библиотеку можно рассматривать как специальный, интересный вариант того, что в предыдущей главе мы называли компонентом. Каждый совет по проектированию и сопровождению компонентов становится предельно важным для библиотек, и, наоборот, многие методы построения библиотек находят применение при проектировании различных компонентов.

Было бы слишком самонадеянно указывать как следует конструировать библиотеки. В прошлом оказались успешными несколько различных методов, а сам предмет остается полем активных дискуссий и экспериментов. Здесь только обсуждаются некоторые важные аспекты этой задачи и предлагаются некоторые приемы, оказавшиеся полезными при создании библиотек. Не следует забывать, что библиотеки предназначены для совершенно разных областей программирования, поэтому не приходится рассчитывать, что какой-то один метод окажется наиболее приемлемым для всех библиотек. Действительно, нет никаких причин полагать, что методы, оказавшиеся полезными при реализации средств параллельного программирования для ядра многопроцессорной операционной системы, кажутся наиболее приемлемыми при создании библиотеки, предназначенной для решения научных задач, или библиотеки, представляющей графический интерфейс.

Понятие класса С++ может использоваться самыми разными способами, поэтому разнообразие стилей программирования может привести к беспорядку. Хорошая библиотека для сведения такого беспорядка к минимуму обеспечивает согласованный стиль программирования, или, по крайней мере, несколько таких стилей. Этот подход делает библиотеку более предсказуемой , а значит позволяет легче и быстрее изучить ее и правильно использовать. Далее описываются пять архитипичных классов, и обсуждаются присущие им сильные и слабые стороны: конкретные типы ($$1 3.2), абстрактные типы ($$13.3), узловые классы ($$13.4), интерфейсные классы ($$13.8), управляющие классы ($$13.9). Все эти виды классов относятся к области понятий, а не являются конструкциями языка. Каждое понятие воплощается с помощью основной конструкции - класса. В идеале надо иметь минимальный набор простых и ортогональных видов классов, исходя из которого можно построить любой полезный и разумно-определенный класс. Идеал нами не достигнут и, возможно, недостижим вообще. Важно понять, что любой из перечисленных видов классов играет свою роль при проектировании библиотеки и, если рассчитывать на общее применение, никакой из них не является по своей сути лучше других.



1 ... 105 106 107 [ 108 ] 109 110 111 ... 120

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика