Head First 设计模式学习——简单工厂方法-工厂方法模式-抽象工厂模式

设计模式是进阶高级开发的必经之路。
工厂方法模式(Factory Method Pattern)是《Head First 设计模式》介绍的第四个模式,只要你认真读完本篇博文,一定能很好地掌握此模式。

定义

工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的

分析

程序要运行,需要创建各种对象。而我们之前为了松耦合、高可用做的努力,最后在main方法里,都需要实例化具体的类对象才行,比如:

Noodles noodle= new BeefNoodles();//实例化牛肉面
Beverage beverage = new MilkyTea();//原味奶茶
 
 
  • 1
  • 2

我们之前提倡的原则:面向接口编程,不面向实现编程。可一旦new 具体对象,程序立马有了瑕疵。假设需要实例化多种子类对象,调用方法里就要写各种new,调用类里就需要引入所有依赖的子类:

import BeefNoodles;//引入所有以来的实现类型
import DuckLegNoodles;
public order(){
    Noodles noodle = createNoodle("牛肉面");
    noodle.cost();//计算价格
    noodle.send();//上菜
}
public createNoodle(面类型){
    Noodles noodle;
    //写在客户端代码里的实例化
    if(牛肉面){
        noodle = new BeefNoodles();//牛肉面一份
    }else (鸭腿面){
        noodle = new DuckLegNoodles();//鸭腿面一份
    }
}
noodle.doSomething();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

若是以后需要的对象类型发生变化,此段代码必定要频繁修改,造成隐患。根据第一原则:把变化的部分抽取、封装。客户端的逻辑可以简化为:获取对象——对象做些事情。我们把获取对象的逻辑交给一个专门负责此类事情的高手去做:简单工厂SimpleFactory。

老将出马,一个顶俩

public class SimpleFactory{
    public Noodles getNoodles(参数){//可以为static方法
        //创建对象的逻辑让SimpleFactory具体负责。
        Noodles noodle;
        if(牛肉面){
            noodle = new BeefNoodles();//牛肉面一份
        }else (鸭腿面){
            noodle = new DuckLegNoodles();//鸭腿面一份
        }
        return noodle;
    }
}

//客户端调用
Noodles noodle = SimpleFactory.getNoodles(参数);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

目前来看,我们只是把代码换了个位置,然后从客户端调用,并没有特别出彩的设计。不过有一点:客户端已经不需要依赖各种具体实现了,因为不管什么面都是面(超类型),客户端获得引用直接调用即可。但是,如果现在新增一个店也需要卖面条。按照原来的方式,它需要写一段实例化面条的代码,并具有同样的风险。现在,有了老司机:SimpleFactory,它只需将它的需求告诉简单工厂,自然就能获得自己想要的对象。这样,以后不管多个客户端,都可以复用简单工厂。
这种把创建对象的逻辑封装起来并提供简单的对外接口,称为简单工厂模式(其实算不上一个模式,毕竟实在很简单)。

客户太多,老将扛不住

快乐的时光总是短暂的。

面条太好吃,大家想开加盟店,开到北京上海,天津广州云南去。中国幅员辽阔,各地的口味不尽相同,为了适应当地风情,需要多款面条产品,兰州牛肉拉面,北京牛肉擀面,天津牛肉炒面。。。(有没有饿),大家向SimpleFactory请求拉面,传入品种标识,SimpleFactory里的getNoodles()方法不断增长:

    public Noodles getNoodles(参数){//可以为static方法
        //创建对象的逻辑让SimpleFactory具体负责。
        Noodles noodle;
        if(牛肉面){
            noodle = new BeefNoodles();//牛肉面一份
        }else if(鸭腿面){
            noodle = new DuckLegNoodles();//鸭腿面一份
        }else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}else if(){}....
        return noodle;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这肯定不是个好办法,毕竟一个工厂产能有限,要开发生产太多品类,也不利用积累,分散了精力。只能多开几个工厂,把加盟店划分区域,一个区域一个工厂,负责此区域内加盟店的面条需求。

//客户端调用
Noodles noodle = BeijingFactory.getNoodles(参数);//北京工厂
Noodles noodle = LanzhouFactory.getNoodles(参数);//兰州工厂
...
 
 
  • 1
  • 2
  • 3
  • 4

本着抽象、封装的原则,我们可以把SimpleFactory抽象成接口,因为工厂都是负责做面条而已。只不过不同工厂做的步骤、原料不一样(实现不同,但上层并不关心)

public interface NoodlesFactory{
    Noodles getNoodles(参数);
}

class BeijingFactory implements NoodlesFactory{
    public Noodles getNoodles(参数){
        Noodles noodle;
        //根据参数,专门生产北京地区风味的面条
        return noodle;
    }
}

class LanzhouFactory implements NoodlesFactory{
    public Noodles getNoodles(参数){
        Noodles noodle;
        //根据参数,专门生产兰州地区面条
        return noodle;
    }
}

//客户端:
持有一个面条工厂引用:
class BeijingStory{
    NoodlesFactory noodlesFactory;
    public BeijingStory(){
        noodlesFactory = new BeijingFactory();
    }
    //下单
    public order(){
        //运行时才能知道是哪个工厂来生产自己需要的面条
        Noodles noodle = noodlesFactory.getNoodles("牛肉面");
        noodle.cost();//计算价格
        noodle.send();//上菜
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

兰州、广州店同样如此,找一个能做本地口味面条的工厂(实现NoodlesFactory接口),就能继续用订单系统,不用改动太多。此时把简单工厂升级,变成了工厂方法模式。可以看到,工厂方法模式是在简单工厂上的进一步抽象、封装。让下层有更大、更多的实现自由,提高设计弹性,保留复用(工厂可以供应好几个门店)。

升级——抽象工厂模式

简单工厂方法——>工厂方法模式——>抽象工厂模式 是一套顶尖功法,不用自宫,也可成功。

本着诚信经营、真材实料的原则,所有门店生意都很好。但世界永远在变,不可能永远只卖面条,别人想吃黄焖鸡、瓦罐煨汤等国家著名连锁小吃的时候,就不来店里去别家了。为了解决这个问题,门店需要增加商品种类。按照之前的工厂方法模式,我们可以新增黄焖鸡工厂YellowChickFactory和瓦罐煨汤工厂CrockSoupFactory:

public interface YellowChickFactory{
    YellowChick getYellowChick(String size);
}
class BeijingYellowChickFactory{
    public YellowChick getYellowChick(String size){
        //生产北京地区的黄焖鸡,大份、中份、小份等
        return new BeijingYellowChick(size);
    }
}
//CrockSoupFactory 代码略...
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这样设计,遵循了我们之前的原则,带来一定的好处。不过也有不足之处:毕竟面条不是时时刻刻都需要,在有黄焖鸡订单时,黄焖鸡工厂生产,意味着面条工厂就处停工状态(综合来说是这样,毕竟总客户资源某种程度上看是固定的),而面条工厂开工,则黄焖鸡工厂停工。

两个工厂的生产效率极大浪费。此时我们想到,能不能把连个工厂结合起来,即能生产面条又能做黄焖鸡,甚至瓦罐汤?

当然可以,我们可以抽象一个总的工厂接口即抽象工厂FoodFactory,里面有生产面条、黄焖鸡、瓦罐汤和其他商品的接口。各地区的工厂只需要实现此接口,实现自己的生产流程,就能满足所有的需求了:

public interface FoodFactory{
    Noodles getNoodles(参数);
    YellowChick getYellowChick(String size);
    //瓦罐汤和其他类似,略
}

class BeijingFactory implements FoodFactory{
   public Noodles getNoodles(参数){
        Noodles noodle;
        ///...
        //根据参数,专门生产北京地区面条
        return noodle;
    }
    public YellowChick getYellowChick(String size){
        //生产北京地区的黄焖鸡,大份、中份、小份等
        return new BeijingYellowChick(size);
    }
}
//兰州、广州的工厂同样如此,只不过具体的实现逻辑不通
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

门店调用:

//北京门店:
持有一个抽象工厂引用:
class BeijingStory{
    FoodFactory foodFactory;
    public BeijingStory(){
        foodFactory= new BeijingFactory();
    }
    //面条下单
    public noodlesOrder(){
        Noodles noodle = foodFactory.getNoodles("牛肉面");
        ...
    }
    //黄焖鸡下单
    public noodlesOrder(){
        YellowChick chick= getYellowChick.getNoodles("小份");
        ...
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

此时:我们在工厂方法模式的基础上,合并多个工厂,并抽象出一个抽象工厂。此抽象工厂包含了一组生产对象的方法,子类实现此接口,来满足系统的调用。此模式就是:抽象工厂模式

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值