171
конструирование экземпляров с помощью одной или нескольких производящих функций вполне
оправдано. В частности, при этом скрывается логика выбора производного класса, экземпляр которого
создается на основании информации, предоставленной на момент создания. Иначе говоря,
производящие функции могут послужить средством оптимизации ваших программ.
class Number {
public:
static Number* make(); // Конструирует число по умолчанию
static Number* make(int);
static Number* make(double);
static Number* make(string);
// Например, “-1.23”
};
// В файле .cpp
class Integer : public Number { ... };
class Real : public Number { ... };
class ArbitraryPrecisionNumber : public Number { ... };
Number* Number::make()
{
return
new
Integer(0);
}
Number* Number::make(int x)
{
return
new
Integer(x);
}
Number* Number::make(double x)
{
return
new
Real(x);
}
Number* Number::make(string s)
{
//
Псевдокод
if
(малое целое)
return
new
Integer(atoi(s));
if
(большое целое)
return
new
ArbitraryPrecisionNumber(s);
if
(вещественное)
//
И т.д.
}
Сделать то же с помощью конструкторов не удастся. К тому моменту, когда компилятор начинает
искать конструктор с подходящей сигнатурой, вы уже сообщили ему, экземпляр какого класса
создается. Более того, такая методика отделяет структуру производных классов от аргументов
производящей функции.
Локализованное использование производящих функций
Одно из самых замечательных применений производящих функций — возможность изолирования
кода, изменяемого при переносе программы на другой компьютер или среду. Открытый интерфейс,
выраженный средствами базового класса, остается прежним, а в файле .cpp прячется
специализированный производный класс с кодом, ориентированным на данную платформу.
class Window {
public:
172
static Window* make();
//
Далее следует гомоморфный интерфейс
};
// В window.cpp для ОС Windows
class MS_Window : public Window { ... };
Window* Window::make()
{
return
MS_Window();
}
// или в window.cpp для Mac OS
class Mac_Window : public Window { ... };
Window* Window::make()
{
return
Mac_Window*();
}
Чтобы переключиться с одной платформы на другую, достаточно перекомпилировать и
перекомпоновать файл .cpp. Все клиенты класса
Window
ничего не будут знать о произошедшей
локализации (предполагается, что вы собираетесь создать действительно универсальное, гомоморфное
представление окна в графическом интерфейсе — задача, перед которой дрогнет даже самый
отчаянный проектировщик).
Уничтожающие функции
Уничтожающие функции (junkyard functions) уничтожают экземпляры классов. Идея заключается в
том, чтобы сделать деструктор закрытым или защищенным, а затем предоставить функцию, в которой
инкапсулируется вызов оператора
delete
.
class Grandpa {
protected:
virtual
~Grandpa();
public:
static Grandpa make();
static void destroy(Grandpa*);
};
// В файле grandpa.cpp
void Grandpa::destroy(Grandpa* g)
{
delete
g;
}
Возможно, сейчас это не имеет особого смысла, но в нестандартных схемах управления памятью
уничтожающие функции способны принести очень большую пользу. А пока скажите начальнику и
коллегам, что уничтожающие функции нужны вам ради симметрии. Они косо посмотрят на вас и
отойдут подальше, так что на какое-то время вам будет спокойнее работать.
Снова о двойной передаче: промежуточные базовые классы
Приняв на вооружение производящие функции, мы легко повысим инкапсуляцию двойной передачи.
// В файле grandpa.h
class Grandpa {
public:
//
Производящие функции и гомоморфный интерфейс
Do'stlaringiz bilan baham: |