На этом шаге рассмотрим качество классов и объектов.
Ингаллс (Ingalls) утверждает, что для построения системы должен использоваться минимальный набор неизменяемых и как можно более общих компонент, рассматриваемых в рамках единой модели [Ingalls, D. August 1981. Design Principles Behind Smalltalk. Byte, vol. 6(8), p. 286.]. В объектно-ориентированном проектировании такими компонентами являются классы и объекты, представляющие собой основные абстракции системы, а единство обеспечивается соответствующими механизмами реализации.
Проектирование классов и объектов является итеративным процессом. За исключением самых простых задач сразу дать точное описание класса практически невозможно. Для того чтобы сгладить концептуальные противоречия между первоначальными абстракциями, необходимо время. Уточнение абстракций связано с дополнительными затратами на перекомпиляцию, согласование и модификацию системы. Следовательно, необходимо стараться с самого начала создать как можно более точные абстракции.
Для оценки качества классов и объектов, выделяемых в системе, можно предложить следующие пять показателей:
Термин связанность заимствован из теории структурного проектирования, но в более вольном толковании его можно использовать и в объектно-ориентированном проектировании. Стевенс (Stevens), Майерс (Myers) и Константин (Соnstantine) определяют связанность как силу связей между отдельными модулями. Сильная связанность усложняет систему, поскольку тесно связанные модули гораздо сложнее воспринимать и модифицировать. Сложность можно уменьшить, разрабатывая системы с как можно более слабыми связями между модулями [Stevens, W., Myers, G., and Constantine, L. 1979. Structured Design. In: Classics in Software Engineering. New York, NY: Yourdon Press, p. 209.].
Пример неправильного подхода к проблеме связанности привел Пейдж-Джонс (Page-Jones), описав модульную стереосистему, источник питания которой размещен в одной из звуковых колонок [Page-Jones, M. 1988. The Practical Guide to Structured Systems Design. En- glewood Cliffs, NJ: Yourdon Press, p. 59.].
Наряду со связанностью между модулями, в объектно-ориентированном анализе и проектировании существенную роль играют связанность между классами и объектами. Однако существует определенное противоречие между связанностью и наследованием. С одной стороны, желательно, чтобы классы были как можно слабее связаны между собой. С другой стороны, механизм наследования — тесно связывающий суперкласс с его подклассами — помогает использовать сходство между абстракциями.
Понятие связности также заимствовано из структурного проектирования. Грубо говоря, связность — это глубина взаимодействия между элементами отдельного модуля (а в объектно-ориентированном проектировании — между элементами отдельного класса или объекта). Наименее желательной является случайная связность, когда в одном классе или модуле собираются несвязанные друг с другом абстракции.
Представьте себе, например, класс, соединяющий абстракции собак и космических аппаратов. Наиболее желательной является функциональная связность, при которой все элементы класса или модуля тесно взаимодействуют, обеспечивая четко определенное поведение. Так, например, класс Dog является функционально связным, если его семантика описывает поведение собаки.
Достаточность означает, что класс или модуль содержат достаточно много характеристик абстракции, чтобы обеспечить целенаправленное и эффективное взаимодействие. В противном случае компоненты системы становятся бессмысленными. Например, разрабатывая класс Set (Множество), целесообразно включить в него операцию удаления элемента из множества, но все окажется бесполезным, если пренебречь операцией добавления элемента в множество. На практике нарушение требования достаточности обнаруживается очень быстро. Это случается всякий раз, когда создается клиент, который должен использовать данную абстракцию.
Полнота означает, что интерфейс класса или модуля описывает все существенные свойства абстракции. В то время как достаточность подразумевает минимальный интерфейс, полный интерфейс по определению охватывает все аспекты абстракции. Полным называется класс или модуль, интерфейс которого гарантирует все необходимые средства для взаимодействия с клиентами. Полнота является субъективным фактором и часто преувеличивается. Реализуя все осмысленные операции над конкретной абстракцией, класс перегружает пользователя. Многие высокоуровневые операции являются избыточными, поскольку их можно реализовать с помощью операций более низкого уровня. Это приводит нас к понятию элементарности.
Элементарными называются операции, для эффективной реализации которых необходимо иметь доступ к внутреннему представлению абстракции. Например, добавление элемента в множество является элементарной операцией, поскольку для реализации операции Add необходим доступ к внутреннему представлению класса Set. С другой стороны, операция добавления четырех элементов в множество не является элементарной, так как ее можно эффективно реализовать с помощью элементарной операции Add, не имея доступа к внутреннему представлению класса.
Конечно, эффективность тоже понятие субъективное. Операция, которая требует прямого доступа к структуре данных, примитивна по определению. Операция бесспорно является элементарной, только если ее невозможно реализовать, не имея доступа к внутреннему представлению абстракции. Операция, которую можно реализовать с помощью существующих элементарных операций, но ценой значительно больших вычислительных затрат, также является кандидатом на включение в число элементарных.
На следующем шаге рассмотрим некоторые особенности разработки интерфейса класса - выбор операций.