Однако урок этой статьи должен быть ясен. Стандартная мудрость, несмотря на использование "недругов" и не методов улучшает инкапсуляцию класса, и предпочтительнее для таких функций над методами, потому что делает решение проще, когда надо проектировать и разрабатывать классы с интерфейсами, которые являются полными и минимальными (или близкими к минимальному). Возражения, связанные с неестественностью, возникающей в результате изменения синтаксиса вызова, являются совершенно необоснованными, а склонность к "недругам" и не методам ведет к пакующим стратегиям для организации интерфейсов класса, которые минимизируют клиентские зависимости при трансляции, сохраняя доступ к максимальному числу дополнительных функций.
Пришло время, чтобы отказаться от традиционной, но неточной идеи, связанной с тем, что означает "быть объектно-ориентированным". Действительно ли Вы истинный сторонник инкапсуляции? Если так, я знаю, что вы ухватитесь за "недружественные" внешние функции с пылом, которого они заслуживают.
Благодарности
Заметки и ссылки
[2] Scott Meyers. Effective C++, Second Edition (Addison-Wesley, 1998).
[3] The algorithm remains unchanged in current printings of Effective C++, because I'd have to also add the requisite reasoning (this article), and making such a substantial change to a book already in production simply isn't practical.
[4] Effective C++, Item 34.
[5] Erich Gamma et al. Design Patterns, Elements of Reusable Object-Oriented Software (Addison-Wesley, 1995). Also known as the GOF or ''Gang of Four'' book.
[6] James O. Coplien. Advanced C++: Programming Styles and Idioms (Addison-Wesley, 1991).
[7] Herb Sutter. ''Sutter's Mill: What's in a Class?'' C++ Report, March 1998.
[8] Herb Sutter. Exceptional C++ (Addison-Wesley, 1999), Items 31-34.
[9] John Lakos. Large-Scale C++ Software Design (Addison-Wesley, 1996).
[10] Effective C++, Item 18.
[11] Jack Reeves. ''(B)leading Edge: How About Namespaces?,'' C++ Report, April 1999.
[12] Jack Reeves. Personal
communication.
Об авторе
Примечания. Первая, из перечисленных выше книг переведена на русский язык:Скотт Мейерс. Эффективное использование C++. 50 рекомендаций по улучшению ваших программных проектов: Пер. с англ. М.: ДМК, 2000. 240 с.: ил.
Статья, на мой взгляд, показывает, что не все так гладко в чистых методах объектно-ориентированного проектирования, если приходится прибегать к ухищрениям, присущим чисто процедурному программированию. Конечно, эффект от использования будет очевиден лишь при разработке достаточно больших программных систем, когда программу приходится развивать и модифицировать, а не создавать заново. Но статья намекает, что чисто объектные языки и методы могут оказаться в этом случае весьма неудобными. А значит: прощай Java и C#? Или их ждет ревизия?
С другой стороны оказывается, что инкапсуляция лучше всего удается процедурным языкам!? Ведь любую структуру данных можно окружить внешними функциями и через них осуществлять доступ. А методы в классе вообще не нужны!? Постараюсь чуть позже высказаться более подробно по этому поводу. А пока Вместо этого текста скоро появится ссылка на соответствующий материал.
А.Л.
Шаблоны и функции фабрики в контексте пространства имен
// менее инкапсулированный проект
class Widget {
public:
static Widget* make(/* params */);
};
// более инкапсулированный проект
namespace WidgetStuff {class Widget {}; Widget* make(/* params */);};
Эндрю Коунинг (Andrew Koenig) подчеркивает, что первый вариант (в котором make является статическим методом класса) позволяет написать шаблонную функцию, которая может вызывать make и не знать порождаемый ею тип:
templatetypename T
void doSomething(/* params */) {
// вызвать функцию фабрики для класса T
T *pt = T::make(/* params */);
}
Это не возможно в проекте, использующем пространство имен, потому что нет никакого способа, чтобы идентифицировать пространство имен, содержащего тип, внутри шаблона. То есть, нет способа выяснить чем является??? в псевдокоде, приведенном ниже:
templatetypename T
void doSomething(/* params */) {
// нельзя узнать какое T содержит пространство имен!
T *pt =???::make(/* params */);
}
Для функций фабрики и подобных функций, которые могут быть заданы с одинаковыми именами, это означает, что максимальная инкапсуляция класса, и максимальное использование шаблонов имеют разногласия. В таких случаях, Вы должны решить, что является более важным и следовать этому. Однако, для статических методов классов с именами определяемыми классом, проблема
шаблонов перестает возникать, и инкапсуляция может снова быть главенствующей.