刚刚学设计模式,不知道理解是否正确,应该有很多不对的地方,请大家指点。
简单工厂模式
问题背景
大众集团要生产汽车,但是不确定要生产哪几个品牌的汽车。
角色扮演
车: VwCar 类、AudiCar 类
工厂: Factory 类
客户端:需要去实例化车的人或者其它代码
适用场景
要生产的产品的数目和类型未知的时候。例如你的项目经理今天突然说:"需要实现一个导出为 PDF 的的功能"。
实现
车
class VwCar
{
}
class AudiCar
{
}
工厂
class Factory
{
//这两个常量用于给客户端一个友好的提示,不要也行
const VW = 1;
const AUDI = 2;
public function produce($type)
{
switch ($type) {
case self::VW:
return new VwCar();
break;
case self::AUDI:
return new AudiCar();
break;
}
}
}
客户端
$factory = new Factory();
$vw = $factory->produce(Factory::VW); //生产大众车
var_dump($vw); //输出 object(VwCar)#3 (0) { }
可以看到,生产了一辆大众车。这个时候,如果还需要生产保时捷,那么增加一条产品线,稍微改改工厂即可。
class Factory
{
//这三个常量用于给客户端一个友好的提示,不要也行
const VW = 1;
const AUDI = 2;
const PORSCHE = 3; // <----增加了一个常量
public function produce($type)
{
switch ($type) {
case self::VW:
return new VwCar();
break;
case self::AUDI:
return new AudiCar();
break;
case self::PORSCHE: // <----增加了一条产品线,用于生产保时捷
return new PorscheCar();
break;
}
}
}
优点
增加产品的时候,只需要修改 Factory 类,增加相应的逻辑即可。
需要改进的地方
在工厂类的内部用了 switch 语句,用于判断 new 什么对象 (生产什么车),这就是耦合的表现。
当你发现在你的代码中使用了大量的条件语句,不妨考虑使用多态来解决问题。
工厂方法模式
为啥叫工厂方法模式,叫啥名不重要,捋清楚逻辑才是关键。
问题背景
基于上文简单工厂模式中提到的需要改进的地方,得想办法解决。如果我还要生宾利、布加迪,我不得不去修改工厂方法中的代码,但是我并不想这样做。
角色扮演
车: VwCar 类、AudiCar 类
总工厂: Factory 抽象类,或者接口也行
子工厂: VwCarFactory 类、 AudiCarFactory 类
客户端:需要去实例化车的人或者其它代码
适用场景
要生产的产品的数目和类型未知的时候。
实现
车
class VwCar
{
}
class AudiCar
{
}
总工厂
abstract class Factory
{
abstract public function produce();
}
子工厂
class VwCarFactory extends Factory
{
public function produce()
{
return new VwCar();
}
}
class AudiCarFactory extends Factory
{
public function produce()
{
return new AudiCar();
}
}
客户端
$factory = new VwCarFactory(); //实例化大众车工场
$vw = $factory->produce(); //生产大众车
var_dump($vw); //输出 object(VwCar)#3 (0) { }
可以看到,生产了一辆大众车,不同于简单工厂的是,这时是使用大众车的工厂去生产大众车,烦人的条件语句没有了。且作为客户端,我不需要关心 $factory->produce() 的具体实现,或者说不需要关心你这车怎么生产的,你把车给我就好。
如果还需要生产保时捷,那么新建一个工厂即可。
class PorscheCarFactory extends Factory
{
public function produce()
{
return new PorscheCar();
}
}
利用多态,消除了烦人的条件语句。
优点
增加产品的时候,只需要增加对应的工厂类。
需要改进的地方
只能横向扩展,无法纵向扩展。比如说,大众集团决定对每种车生产低端车和高端车。
读到这里,你可能感觉我在扯犊子,我隐约感觉到我确实是在扯犊子,可能扯着扯着就整明白了。
抽象工厂模式
问题背景
为了解决上文提到的,只能 "横向" 扩展,无法 "纵向" 扩展的问题。这里引出一个概念,产品族。我并不想针对每一个系列的车都去建一个工厂,毕竟建工厂要很多钱,我希望建一个奥迪车工厂就可以生产低端奥迪和高端奥迪,将来可能还会有中端、顶端等等。
无论是低端奥迪还是高端奥迪,都是奥迪车。
角色扮演
低端车接口: LowEndCar 类 (生产低端车就要有低端车的模板,例如厂家规定低端车不端备八向电动座椅调节)
车: VwLowEndCar 类 (低端大众)
总工厂: Factory 抽象类,或者接口也行
子工厂: VwCarFactory 类
客户端:需要去实例化车的人或者其它代码
适用场景
要生产的产品的数目和类型未知的时候。与工厂方法类似,是高级版的 "工厂方法"。
实现 (基本款)
低端车接口
interface LowEndCar
{
//规定一些低端车必须实现的方法,例如低端车必须不端备八向电动座椅调节
}
大众低端车
class VwLowEndCar implements LowEndCar
{
}
总工厂
abstract class Factory
{
abstract public function produceLowEndCar();
}
大众车的子工厂
class VwCarFactory extends Factory
{
public function produceLowEndCar()
{
return new VwLowEndCar();
}
}
客户端
$factory = new VwCarFactory(); //实例化大众车工厂
$VwLowEndCar = $factory->produceLowEndCar(); //生产大众低端车
var_dump($VwLowEndCar); //输出 object(VwLowEndCar)#3 (0) { }
此时厂家说要生产高端车,没问题,那么需要做几个事情呢?
实现 (生产高端车)
第一,给出高端车的接口。
高端车接口
interface HeightEndCar
{
//规定一些高端车必须实现的方法,例如高端车必须端备座椅加热
}
第二,给出具体的大众高端车。
大众高端车
class VwHeightEndCar implements HeightEndCar
{
}
第三,在总工厂中给出生产高端车的方法。
总工厂
abstract class Factory
{
abstract public function produceLowEndCar();
abstract public function produceHeightEndCar();
}
第四,在具体的大众车工厂中去实现生产高端车的方法。
大众车的子工厂
class VwCarFactory extends Factory
{
public function produceLowEndCar()
{
return new VwLowEndCar();
}
public function produceHeightEndCar() // <----可以生产高端车了
{
return new VwHeightEndCar();
}
}
第五,客户端调用。
客户端
$factory = new VwCarFactory(); //实例化大众车工厂
$VwHeightEndCar = $factory->produceHeightEndCar(); // 生产大众高端车
var_dump($VwHeightEndCar); //输出 object(VwHeightEndCar)#3 (0) { }
看到这里,需要回头想想,这样做有啥好处?
这个时候,如果我们要增加产品线,就很轻松愉悦了。至少不用去修改总工厂的某些具体方法,虽然本例中总工厂中没有任何具体的方法,但是完全可以加入一些,例如无论是高端车低端车,总得给出车的长度吧。
abstract class Factory
{
abstract public function produceLowEndCar();
abstract public function produceHeightEndCar();
public function getWidth()
{
return '获取车的长度';
}
}
这里的 getWidth() ,无论哪一个具体的子工厂都可以共享,岂不是轻松愉悦。
实现 (生产奥迪车)
还是要考虑,要做哪些事情?
第一,给出奥迪车。
class AudiLowEndCar implements LowEndCar
{
}
class AudiHeightEndCar implements HeightEndCar
{
}
第二,给出奥迪车的子工厂。
class AudiCarFactory extends Factory
{
public function produceLowEndCar()
{
return new AudiLowEndCar();
}
public function produceHeightEndCar()
{
return new AudiHeightEndCar();
}
}
第三,客户端调用。
$factory = new AudiCarFactory(); //实例化奥迪车工厂
$AudiLowEndCar = $factory->produceLowEndCar(); // 生产奥迪低端车
$AudiHeightEndCar = $factory->produceHeightEndCar(); // 生产奥迪高端车
var_dump($AudiLowEndCar); //输出 object(AudiLowEndCar)#3 (0) { }
var_dump($AudiHeightEndCar); //输出 object(AudiHeightEndCar)#4 (0) { }
思考
如果现在厂家要生产中端车,那些地方要动刀子?
需要 6 刀
中端车接口
中端大众车去实现中端车接口
中端奥迪车去实现中端车接口
总工厂增加抽象的创建中端车的方法
大众的子工厂去实现总工厂中创建中端车的方法
奥迪的子工厂去实现总工厂中创建中端车的方法