Программирование >>  Перегруженные имена функций и идентификаторы 

1 ... 143 144 145 [ 146 ] 147 148 149 ... 210


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

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



Инкапсуляция и функции - не члены

Приемлемый способом оценки инкапсуляции является количество функций, которые могли бы быть разрушены, если изменяется реализация класса. В этом случае становится ясно, что класс с n методами более инкапсулирован, чем класс с n+1 методами. И это наблюдение поясняет предпочтение в выборе функций, не являющихся ни друзьями, ни методами: если функция f может быть выполнена как метод или как функция, не являющаяся другом, то создание ее в виде метода уменьшило бы инкапсуляцию, тогда как создание ее в виде недруга инкапсуляцию не уменьшит. Так как функциональность здесь не обсуждается (функциональные возможности f доступны классам клиентов независимо от того, где эта f размещена), мы естественно предпочитаем более инкапсулированный проект.

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

Этот анализ применяется к любому виду методов, включая и статические. Добавление статического метода к классу, когда его функциональные возможности могут быть реализованы как не члены и не друзья уменьшают инкапсуляцию точно так же, как это делает добавление нестатического метода. Перемещение свободной функции в класс с оформлением ее в виде статического метода, только для того, чтобы показать, что она соприкасается с этим классом, является плохой идеей. Например, если имеется абстрактный класс для виджетов (Widgets) и затем используется функция фабрики классов, чтобы дать возможность клиентам создавать виджеты, можно использовать следующий общий, но худший способ организовать это:

a design less encapsulated than it could be

class Widget {

... внутренее наполнение Widget; может быть: public, private, или protected



public:

может быть также недругом и не членом static Widget* make(/* params */);

Лучшей идеей является создание вне Widget, что увеличивает совокупную инкапсуляцию системы. Чтобы показать, что Widget и его создание (make) все-таки связаны, используется соответствующее пространство имен (namespace):

более инкапсулированный проект namespace WidgetStuff {

class Widget { ... };

Widget* make( /* params */ );

Увы, у этой идеи имеется своя слабость, когда используются шаблоны.

Синтаксические проблемы

Возможно что вы, как и многие люди, имеете представление относительно синтаксического смысла утверждения, что не методы и не друзья предпочтительнее методов. Возможно, что вы даже купились на аргументы относительно инкапсуляции. Теперь, предположим, что класс Wombat поддерживает функциональные возможности поедания и засыпания. Далее предположим, что функциональные возможности, связанные с поеданием, должны быть выполнены как метод, а засыпание может быть выполнено как член или как не член и не друг. Если вы следуете советам, описанным выше, вы создадите описания подобные этим:

class Wombat { public:

void eat(double tonsToEat);

void sleep(Wombat& w, double hoursToSnooze);

Это привело бы к синтаксическому противоречию для клиентов класса, потому что для

Wombat w; они напишут:



1 ... 143 144 145 [ 146 ] 147 148 149 ... 210

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