设计模式之三种工厂模式(最易理解版)

刚刚学设计模式,不知道理解是否正确,应该有很多不对的地方,请大家指点。

简单工厂模式
问题背景
大众集团要生产汽车,但是不确定要生产哪几个品牌的汽车。

角色扮演
车: 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 刀
中端车接口
中端大众车去实现中端车接口
中端奥迪车去实现中端车接口
总工厂增加抽象的创建中端车的方法
大众的子工厂去实现总工厂中创建中端车的方法
奥迪的子工厂去实现总工厂中创建中端车的方法

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值