Рис. 8.13. Иерархия некоторых классов C++ Builder
Рис. 8.14. Фрагмент схемы иерархии
8.11. АЛЬТЕРНАТИВНЫЙ ПРОЕКТ ГРАФИЧЕСКОГО ИНТЕРФЕЙСА
При развитии программ постоянно возникает проблема увеличения функциональных возможностей одного объекта за счет функциональных возможностей другого. Актуальнейшая проблема программирования — написание гибких программ, приспособленных для модификации и развития.
Вначале надо ввести всего одно понятие, предложенное Александром Усовым: контейнер-менеджер, или просто контейнер. Следует отметить, что здесь не идет речь о контейнере C++. Итак, контейнер — это класс, который позволяет объединять (агрегировать) в себе самые разные классы объектов, в том числе и другие контейнеры. Одной из наиболее сложных задач проектирования является агрегация разнородных элементов в новое единое целое. Контейнер — один из механизмов решения проблемы гибкой агрегации.
Простейший контейнер — это список ссылок на объекты. Далее если воспользоваться механизмом сообщений, то… всех этих затруднений можно избежать! Ни строчки нового кода! Сообщения, приходящие контейнеру, проецируются на принадлежащие ему объекты. Но допустима и более сложная логика обработки запросов, перед тем как они попадут к объекту-обработчику.
Сообщения, которые может обрабатывать класс, образуют его интерфейс. При использовании таких контейнеров нет нужды объявлять поля класса private или protected либо еще как-нибудь, поскольку их вообще не должно быть видно (исходные тексты класса больше не надо поставлять вместе с его кодом). Для всех разработчиков, использующих данный класс, достаточно знать его типы и структуры сообщений, т. е. сообщения обеспечивают максимальную защиту полей объектов и при этом не требуют накладных расходов.
Сообщения позволяют увеличить виртуализацию кода, что положительно сказывается на снижении его объема. Сообщения в отличие от вызова процедуры проще перехватить, дабы выполнить над ними предварительную обработку, например фильтрацию или сортировку. Наконец, сообщения позволяют максимально увеличить производительность системы, что недостижимо при вызове процедур.
Контейнеры бывают двух типов: однородные (динамические) и разнородные (статические).
Однородный контейнер может включать произвольное множество объектов одного класса либо классов, производных от данного класса. Логика работы такого контейнера предельно проста, например распределять поступающие сообщения по всем включенным в него объектам. Поскольку включенные в него объекты принадлежат одному классу, то, следовательно, они имеют единый интерфейс, но тогда становится совершенно неважно, сколько объектов включено в контейнер в любой момент времени, т. е. это число произвольно. Логика работы такого контейнера с включенными в него объектами одинакова и не зависит от конкретного объекта. Типичный представитель такого контейнера — список (например, строк). При добавлении (удалении) новых объектов (строк) логика работы самого контейнера остается неизменной.
Напротив, контейнер разнородных элементов может состоять из объектов самых разных классов. Его можно представить как схему, где каждый элемент (объект) имеет свою смысловую (функциональную) нагрузку. События, поступающие на такой контейнер, не транслируются примитивно на все объекты, а распределяются между ними по заданной схеме. Для данного типа контейнера применимо понятие "конструирование".
Другим отличием контейнера от множественного наследования является то, что можно произвольно во время работы или проектирования включать новые или исключать старые объекты, например, для того чтобы обеспечить их перенос из одного контейнера в другой. При этом состояние объектов остается тем же самым, мы просто меняем ссылки у контейнеров. Можно динамически подгружать новые логические схемы работы контейнера или изменять старые, что для множественного наследования, наверное, недостижимо в принципе. Следовательно, контейнер может гибко реализовывать полиморфизм в наиболее общем смысле!
Отметим еще раз, что взаимосвязь между объектами осуществляется посредством сообщений. Но здесь сообщения — специальный класс. Именно этот класс несет ответственность за полиморфизм свойств, но никак не классы основной иерархии. В таком случае у нас есть возможность объявить некоторый класс-сообщение и создать набор полиморфных классов-наследников, которые будут обрабатываться объектами основной иерархии классов.
Читать дальше