Я читаю о различных шаблонах, доступных там. В настоящее время я нахожусь на шаблоне абстрактной фабрики, и я думаю, что у меня есть хорошее представление о нем. Мои ресурсы, кроме Википедии:
http://www.tutorialspoint.com/design_pattern/abstract_factory_pattern.htm
http://www.oodesign.com/abstract-factory-pattern.html
https://github.com/domnikl/DesignPatternsPHP/tree/master/Creational/AbstractFactory
Я использую Apple и ее продукты для создания образца абстрактной фабрики в качестве примера. Я понимаю, что дублирование кода — плохой дизайн, поэтому я и пишу это. Мой код до сих пор:
abstract class AbstractAppleFactory {
abstract public function createiPod( $capacity, $type, $color, $engraving );
abstract public function createiPhone( $capacity, $type, $color, $antenna );
abstract public function createComputer( $type, $HDCapacity, $CPU, $ram );
}
class iPodFactory extends AbstractAppleFactory {
public function createiPod( $capacity, $type, $color, $engraving ) {
$class = 'iPod' . $type;
return new $class( $capacity, $color, $engraving);
}
public function createiPhone( $capacity, $type, $color, $antenna ){ /* no implementation necessary */}
public function createComputer( $type, $HDCapacity, $CPU, $ram ){ /* no implementation necessary */}
}
interface iPlayer {
public function play();
public function stop();
public function fastForward();
public function rewind();
}
abstract class iPod implements iPlayer {
protected $capacity;
protected $color;
protected $engraving;
public function __construct( $capacity, $color, $engraving = null ) {
$this->capacity = $capacity;
$this->color = $color;
$this->engraving = $engraving;
}
}
class iPodClassic extends iPod {
public function play() {/* implementation goes here */}
public function stop() {/* implementation goes here */}
public function fastForward() {/* implementation goes here */}
public function rewind() {/* implementation goes here */}
}
class iPodShuffle extends iPod {
public function play() {/* implementation goes here */}
public function stop() {/* implementation goes here */}
public function fastForward() {/* implementation goes here */}
public function rewind() {/* implementation goes here */}
}
и т. д. и т. д. Здесь слишком много кода для размещения. Я знаю, что лучше организовать в каталогах и пространствах имен. Это не то, что я изучаю прямо сейчас. Я изучаю шаблоны и концепции ООП.
Рассматриваемый раздел:
class iPodFactory extends AbstractAppleFactory {
public function createiPod( $capacity, $type, $color, $engraving ) {
$class = 'iPod' . $type;
return new $class( $capacity, $color, $engraving);
}
public function createiPhone( $capacity, $type, $color, $antenna ){ /* no implementation necessary */}
public function createComputer( $type, $HDCapacity, $CPU, $ram ){ /* no implementation necessary */}
}
Из-за наследования / абстракции я вынужден реализовать два ненужных метода на несвязанной фабрике. createiPhone()
а также createComputer()
, Я правильно делаю абстрактный шаблон фабрики? Опять же, «дублирование кода — это плохой дизайн!» Что может быть лучше?
Я думаю, что вы сделали большую ошибку. Абстрактная фабрика предназначена для создания абстракции для создания семейства продуктов, чтобы вы могли легко поменять ее на другое семейство.
F.E. Apple => Samsung 🙂
interface ComputerAbstractFactory {
/**
* @return Tablet
*/
function createTablet();
/**
* @return Phone
*/
function createPhone();
/**
* @return Computer
*/
function createComputer();
}
class AppleComputerFactory implements ComputerAbstractFactory {}
class SamsungComputerFactory implements ComputerAbstractFactory {}
class IPad implements Tablet {}
class GalaxyTab implements Tablet {}
...
Использование абстрактного шаблона фабрики имеет смысл, когда вы хотите переключаться между обеими компаниями.
Ваш код должен зависеть только от абстракций (принципы SOLID) ComputerAbstractFactory, Tablet, Phone, Computer.
Таким образом, если вы решаете (например, с помощью какого-либо переключателя конфигурации), какого производителя следует использовать, вполне достаточно, чтобы вы внедрили выбранную реализацию ComputerAbstractFactory в свой бизнес.
код, и все готово — все работает, создаются планшеты, телефоны и т. д.
Вам нужно решить, какую абстракцию вы хотите создать в своем приложении, чтобы вызывающий код не был связан с конкретными реализациями этой абстракции. Я считаю, что IPlayer — это то, что вы хотите, где IPodClassic IPodShuffle — это конкретные реализации.
Если я прав, вы должны создать IPlayerFactory
class IPlayerFactory {
/**
* @return IPlayer
*/
function create() {...}
}
О дублировании, которое вы упомянули. Если iPodFactory расширяет AbstractAppleFactory, он должен реализовывать все свои методы (кстати, если AbstractAppleFactory не имеет никакой реализации, это должен быть интерфейс)
Вы сломали принцип Лискова из твердых принципов. Короткая версия — если вызывающий код зависит от AbstractAppleFactory и он получает iPodFactory в качестве конкретной реализации для работы, он будет прерываться при вызове createiPhone или createComputer — поэтому абстракция не работает в этом случае.
Вы на правильном пути, но я думаю, что ваш интерфейс неправильный.
Прежде всего, не используйте abstract
класс, если вам не нужно создавать метод, который будет использоваться на дочерних классах. Используйте interface
будет лучше.
Это было бы моим решением:
<?php
interface AppleFactoryInterface
{
function create($data);
}
А вот и iPhoneFactory:
<?php
class iPhoneFactory implements AppleFactoryInterface
{
public function create($data)
{
$klass = sprintf('iPhone'.$data['type']);
$instance = new $klass;
$instance->setFromArray($data);
return $instance;
}
}
Как видите, у меня меньше кода, который лучше понять.