В этой статье рассмотрим пример использования закрытого конструктора, то есть снабдим его спецификатором доступа "private".
Если конструктор использует спецификатор доступа "private", то объект не будет создан при помощи оператора "new", так как попытка его объявления закончиться фатальной ошибкой:
"Call to private <имя класса>::__construct() from invalid context"
Однако это вовсе не означает, что объект такого класса вообще невозможно создать. Можно воспользоваться конструктором при помощи открытого метода данного класса.
Создадим класс "byCoordinat" с закрытым конструктором, моделирующий точку в двумерной декартовой системе координат.
class byCoordinat {
private $_cooX, $_cooY;
private function __construct ($_coox, $_cooy) {
$this->_cooX = $_coox;
$this->_cooY = $_cooy;
}
public function coo_X() { return $this->_cooX; }
public function coo_Y() { return $this->_cooY; }
public function coo_new ($_coox, $_cooy) { return new byCoordinat($_coox, $_cooy); }
}
Как видно, класс "byCoordinat" содержит дополнительный метод "coo_new()", который и вызывает конструктор.
$_Class_byCoordinat = byCoordinat::coo_new(1, 10);
echo $_Class_byCoordinat->coo_X() .' | '. $_Class_byCoordinat->coo_Y(); # Результат: 1 | 10;
Зачем может потребоваться инициализация объекта в обход оператора "new"?
Главным образом, для увеличения гибкости приложения. Пусть, например, в большом вычислительном приложении создаются десятки тысяч точек в двумерной декартовой системе координат. В какой-то момент принимается решение добавить новый класс "myCoordinat" для трёхмерной системы координат, конструктор которого принимает три координаты: $_cooX, $_cooY и $_cooZ. Просто заменить класс "byCoordinat" новым нельзя, часть точек в проекте остаётся двумерной, а двумерные и трёхмерные точки следует рассматривать как несовместимые типы, то есть их классы должны различаться. Если для создания класса на протяжении всего проекта использовался оператор "new", то пришлось бы просмотреть и отредактировать все точки создания объектов "byCoordinat". Если же используется фабричный метод, можно переопределить только метод "coo_new()".
Приёмы и алгоритмы, связанные с возвращением объектов при помощи одного из методов, называются "Фабричными":
class byCoordinat {
private $_cooX, $_cooY;
private function __construct ($_coox, $_cooy) {
$this->_cooX = $_coox;
$this->_cooY = $_cooy;
}
public function coo_X() { return $this->_cooX; }
public function coo_Y() { return $this->_cooY; }
public function coo_new($_coox, $_cooy, $_cooz = 'false') {
if ($_cooz == 'false') {
return new byCoordinat($_coox, $_cooy);
} else {
return new myCoordinat($_coox, $_cooy, $_cooz);
}
}
}
Как видно из примера, в метод "coo_new" добавлен третий необязательный параметр для координаты "Z". Это достаточно просто сделать, а на существующих вызовах наличие ещё одного, необязательного параметра не как не отразится, так как для создания двумерных точек используется только первые два параметра.
Если параметр "$_cooz" не задаётся разработчиком, он получает значение "false", а метод "coo_new()" создаёт объект класса "byCoordinat". Если же параметр задан, то создаётся объект класса "myCoordinat", и далее класс "byCoordinat" не участвует в работе объекта.
Предоставленный приём сокрытия создания объекта также является примером инкапсуляции, только на более высоком уровне абстракции. В полной мере, достоинства данного приёма можно оценить при использовании интерфейсов и наследования.
Комментировать
А как Вы из метода coo_new() получите доступ к конструктору myCoordinat? или он публичным будет?
Да, в данном примере конструктор myCoordinat должен быть открыт. Но можно переписать код, сделав конструктор закрытым:
class myCoordinat {
private $_cooX, $_cooY, $_cooz;
// конструктор класса myCoordinat закрыт
private function __construct ($_coox, $_cooy, $_cooz) {
$this->_cooX = $_coox;
$this->_cooY = $_cooy;
$this->_cooZ = $_cooz;
}
public function coo_X() { return $this->_cooX; }
public function coo_Y() { return $this->_cooY; }
public function coo_Z() { return $this->_cooZ; }
// создаём метод zoo_new() для доступа к закрытому конструктору
public function zoo_new ($_coox, $_cooy, $_cooz) {
return new myCoordinat($_coox, $_cooy, $_cooz);
}
}
class byCoordinat {
private $_cooX, $_cooY;
private function __construct ($_coox, $_cooy) {
$this->_cooX = $_coox;
$this->_cooY = $_cooy;
}
public function coo_X() { return $this->_cooX; }
public function coo_Y() { return $this->_cooY; }
public function coo_new($_coox, $_cooy, $_cooz = 'false') {
if ($_cooz == 'false') {
return new byCoordinat($_coox, $_cooy);
} else {
// вызываем закрытый конструктор через метод zoo_new() класса myCoordinat
return myCoordinat::zoo_new($_coox, $_cooy, $_cooz);
}
}
}
Далее, проверяем работу скрипта:
$_Class_byCoordinat = byCoordinat::coo_new(1, 10, 4);
echo $_Class_byCoordinat->coo_X() .' | '.
$_Class_byCoordinat->coo_Y() .' | '.
$_Class_byCoordinat->coo_Z();
// Результат: 1 | 10 | 4;
Спасибо! Отличная статья =) Долго не мог разобраться зачем в паттернах, типа singleton используют закрытый конструктор =)