статье. Благодарю также Джека Ривеса (Jack Reeves), Херба Шутера (Herb Sutter), Дейва Смаллберга (Dave Smallberg), Андрея Александреску (Andrei Alexandrescu), Брюса Екела (Bruce Eckel), Брайна Страуструпа (Bjarne Stroustrup), и Андрю Коунига) Andrew Koenig за комментарии перед публикацией. В заключение, большая благодарность Аделе Новак (Adela Novak) за организации семинаров C++ в Люцерне (Швейцарии), которая вели к многочасовым дискуссиям, позволившим мне написать начальный проект этой статьи.
// более инкапсулированный проект namespace WidgetStuff {class Widget {…}; Widget* make(/* params */);}; Эндрю Коунинг (Andrew Koenig) подчеркивает, что первый вариант (в котором make является статическим методом класса) позволяет написать шаблонную функцию, которая может вызывать make и не знать порождаемый ею тип: template‹typename T› void doSomething(/* params */) { // вызвать функцию фабрики для класса T T *pt = T::make(/* params */); … } Это не возможно в проекте, использующем пространство имен, потому что нет никакого способа, чтобы идентифицировать пространство имен, содержащего тип, внутри шаблона. То есть, нет способа выяснить чем является??? в псевдокоде, приведенном ниже: template‹typename T› void doSomething(/* params */) { // нельзя узнать какое T содержит пространство имен! T *pt =???::make(/* params */); … } Для функций фабрики и подобных функций, которые могут быть заданы с одинаковыми именами, это означает, что максимальная инкапсуляция класса, и максимальное использование шаблонов имеют разногласия. В таких случаях, Вы должны решить, что является более важным и следовать этому. Однако, для статических методов классов – с именами определяемыми классом, проблема шаблонов перестает возникать, и инкапсуляция может снова быть главенствующей.
Заметки и ссылки
[1] Scott Meyers. Effective C++: 50 Specific Ways to Improve Your Programs and Designs, First Edition (Addison-Wesley, 1991), Item 19. [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.Об авторе
Scott Meyers – известный авторитет по C++; он обеспечивает консультативные услуги клиентам по всему миру. Он автор Effective C++, Second Edition (Addison-Wesley, 1998), More Effective C++ (Addison-Wesley, 1996), и Effective C++ CD (Addison-Wesley, 1999). Скотт получил его Ph. D. в области Информатики в Университета Броуна (Brown University) в 1993.Примечания. Первая, из перечисленных выше книг переведена на русский язык: Скотт Мейерс. Эффективное использование C++. 50 рекомендаций по улучшению ваших программных проектов: Пер. с англ. – М.: ДМК, 2000. – 240 с.: ил. Статья, на мой взгляд, показывает, что не все так гладко в чистых методах объектно-ориентированного проектирования, если приходится прибегать к ухищрениям, присущим чисто процедурному программированию. Конечно, эффект от использования будет очевиден лишь при разработке достаточно больших программных систем, когда программу приходится развивать и модифицировать, а не создавать заново. Но статья намекает, что чисто объектные языки и методы могут оказаться в этом случае весьма неудобными. А значит: прощай Java и C#? Или их ждет ревизия? С другой стороны оказывается, что инкапсуляция лучше всего удается процедурным языкам!? Ведь любую структуру данных можно окружить внешними функциями и через них осуществлять доступ. А методы в классе вообще не нужны!? Постараюсь чуть позже высказаться более подробно по этому поводу. А пока… Вместо этого текста скоро появится ссылка на соответствующий материал.А.Л.
Шаблоны и функции фабрики в контексте пространства имен
(Врезка)
В основной статье, я утверждаю, что статические методы классов должны быть реализованы как внешние функции всякий раз, когда это возможно, потому что это увеличивает инкапсуляцию класса. Здесь я рассматриваю две возможных реализации для функций фабрики: // менее инкапсулированный проект class Widget { … public: static Widget* make(/* params */); };// более инкапсулированный проект namespace WidgetStuff {class Widget {…}; Widget* make(/* params */);}; Эндрю Коунинг (Andrew Koenig) подчеркивает, что первый вариант (в котором make является статическим методом класса) позволяет написать шаблонную функцию, которая может вызывать make и не знать порождаемый ею тип: template‹typename T› void doSomething(/* params */) { // вызвать функцию фабрики для класса T T *pt = T::make(/* params */); … } Это не возможно в проекте, использующем пространство имен, потому что нет никакого способа, чтобы идентифицировать пространство имен, содержащего тип, внутри шаблона. То есть, нет способа выяснить чем является??? в псевдокоде, приведенном ниже: template‹typename T› void doSomething(/* params */) { // нельзя узнать какое T содержит пространство имен! T *pt =???::make(/* params */); … } Для функций фабрики и подобных функций, которые могут быть заданы с одинаковыми именами, это означает, что максимальная инкапсуляция класса, и максимальное использование шаблонов имеют разногласия. В таких случаях, Вы должны решить, что является более важным и следовать этому. Однако, для статических методов классов – с именами определяемыми классом, проблема шаблонов перестает возникать, и инкапсуляция может снова быть главенствующей.