原型模式
平行继承层次的出现是工厂方法模式带来的一个问题。一个避免这种依赖的方法是使用PHP的clone关键词复制已存在的具体产品。然后,具体产品类本身便成为它们自己生成的基础。这便是原型模式。使用该模式我们可以用组合代替继承。这样的转变促使了代码运行时的灵活性,并减少了必须创建的类。
以下展示一个抽象工厂和工厂方法模式的类图:
你可以看到,我们依赖继承来组合工厂生成terrain家族产品,但这需要一个大型的继承体系,并且相对来说不那么灵活。当你不想平行的继承体系而需要最大化运行时的灵活性时,可以使用抽象工厂模式的强大变形——原型模式。
- 实现
简单地创建一个保存具体产品的工厂类,并在初始化时就加入这种做法。
class Sea
{
}
class EarthSea extends Sea
{
}
class MarsSea extends Sea
{
}
class Plains
{
}
class EarthPlains extends Plains
{
}
class MarsPlains extends Plains
{
}
class Forest
{
}
class EarthForest extends Forest
{
}
class MarsForest extends Forest
{
}
class TerrainFactory
{
private $sea;
private $forest;
private $plains;
public function __construct(Sea $sea, Plains $plains, Forest $forest)
{
$this->sea = $sea;
$this->forest = $forest;
$this->plains = $plains;
}
public function getSea()
{
return clone $this->sea;
}
public function getForest()
{
return clone $this->forest;
}
public function getPlains()
{
return clone $this->plains;
}
}
$factory = new TerrainFactory(new EarthSea(), new EarthPlains(), new EarthForest());
print_r($factory->getSea());
可以看到,我们加载了一个带有产品对象实例的具体的TerrainFactory对象。当客户端代码调用getSea()时返回在初始化时缓存的Sea对象的一个副本。我们不仅仅省掉了一些类,还增加额外的灵活性。如创建一个有类似地球海洋和森林以及火星平原的星球:
$factory = new TerrainFactory(new EarthSea(), new MarsPlains(), new EarthForest());
因此原型模式使我们可利用组合所提供的灵活性,不过我们得到的可不止这个。因为在运行时保存和克隆对象,所以当生成新产品时,可以重新设定对象状态。
class Sea
{
private $navigability = 0;
function __construct($navigability)
{
$this->navigability = $navigability;
}
}
要记住但是如果产品对象引用了其他对象,那你应该实现__clone()方法来保证你得到的是深复制。