工厂方法模式

场景

(本例子来自于Head First 设计模式)
假如我们现在经营着一家披萨店,那么我们需要提供多种不同口味的披萨供用户选择,那么我们可能会这样做:

enum PizzaEnum{
    CHEESE("奶酪"),
    VEGGIE("蔬菜");

    public String getItem() {
        return item;
    }

    PizzaEnum(String item) {

        this.item = item;
    }

    private String item;
    static PizzaEnum fromItem(String item){
        for (PizzaEnum pizzaEnum : PizzaEnum.values()){
            if (pizzaEnum.item.equals(item))
                return pizzaEnum;
        }
        throw new IllegalArgumentException("没有找到对应口味的披萨");
    }
}

这个枚举类就代表披萨的不同口味了,那么有了不同口味我们还需要有制作的流程:

class Pizza{
    void prepare(){
        System.out.println(name()+" 准备制作");
    }
    void bake(){
        System.out.println(name()+" 开始烘烤");
    }
    private String name(){
        return this.getClass().getName();
    }
}

这样就可以把自己的披萨卖给用户了,当然用户是可以自己挑选那种口味的披萨,所以还需要让用户来挑:

class PizzaStore{
    void orderPizza(PizzaEnum item){
        Pizza pizza;
          switch (item){
            case CHEESE:
                pizza =  new CheesePizza();
            case VEGGIE:
                pizza =  new VeggiePizza();
            default:
                pizza =  new Pizza();
        pizza.prepare();
        pizza.bake();
        System.out.println("披萨制作完成!");
    }
}

这里CheesePizzaVeggiePizza就代表制作不同口味的披萨了,那么我们的披萨店整体就大致完成了,可以开始售卖了.
如果某一天我们的厨师有了新想法,想要做一种新口味的披萨要怎么办呢?肯定就是在orderPizza里面添加新的条件,再添加对应的类;但是这种方式是不是比较麻烦呢?每次有新口味就需要在订披萨的方法里面添加新的条件,这样是不是违反了设计程序的单一职责呢,这个类理应来说只负责做披萨就可以了,具体要做哪种口味的其实我们可以交给其他方法或类来做,这就是工厂方法发挥的时候了,我们定义一个方法:

    Pizza createPizza(PizzaEnum item) {
        switch (item){
        case CHEESE:
            return new CheesePizza();
        case VEGGIE:
            return new VeggiePizza();
        default:
            return new Pizza();
        }
    }

可以看出,这个方法的功能就是根据参数返回对应的一个披萨的实例,而orderPizza就不用操心要哪种口味的,只需利用一个抽象类表达所有口味的披萨即可,而且有新口味的话只需要在createPizza方法中添加新的条件即可.
口味的问题解决了,那么如果某一天有其他地方的人想加盟我们的披萨店,且他们自己可能由于位置原因有他们口味的披萨,就像中国四川一般喜欢吃辣,云南较为清淡,为了适应不同的地方,我们需要做不同口味的披萨,那要怎么做?
其实我们可以按照前面学习的装饰者模式,设置一个基础类作为抽象类,有基本的功能,不同地方都继承自这个类,有口味需求的话只需要重写相应的方法即可,那么总结起来我们可以把这个场景写成下面这样:


/**
 * 抽象披萨店,只具有返回不同披萨的抽象方法和订披萨的基本方法
 */
abstract class PizzaStore{
    abstract Pizza createPizza(PizzaEnum item);
    void orderPizza(PizzaEnum item){
        Pizza pizza = createPizza(item);
        pizza.prepare();
        pizza.bake();
        System.out.println("披萨制作完成!");
    }
}

/**
 * 纽约开的披萨店,可以看出纽约店有他们自己口味的披萨
 */
class NYPizzaStore extends PizzaStore{

    @Override
    Pizza createPizza(PizzaEnum item) {
        switch (item){
        case CHEESE:
            return new NYCheesePizza();
        case VEGGIE:
            return new NYVeggiePizza();
        default:
            return new Pizza();
        }
    }
}

/**
 * 华盛顿的店也是同样道理
 */
class WSPizzaStore extends PizzaStore{

    @Override Pizza createPizza(PizzaEnum item) {
        switch (item){
        case CHEESE:
            return new WSCheesePizza();
        case VEGGIE:
            return new WSVeggiePizza();
        default:
            return new Pizza();
        }
    }
}

/**
 * 这就是我们的披萨类
 */
class Pizza{
    void prepare(){
        System.out.println(name()+" 准备制作");
    }
    void bake(){
        System.out.println(name()+" 开始烘烤");
    }
    private String name(){
        return this.getClass().getName();
    }
}

/**
 * 作为学习,这些类里面就没有写其他东西了
 */
class NYCheesePizza extends Pizza{
}
class NYVeggiePizza extends Pizza{
}
class WSVeggiePizza extends Pizza{

}
class WSCheesePizza extends Pizza{

}

/**
 * 披萨的口味
 */
enum PizzaEnum{
    CHEESE("奶酪"),
    VEGGIE("蔬菜");

    public String getItem() {
        return item;
    }

    PizzaEnum(String item) {

        this.item = item;
    }

    private String item;
    static PizzaEnum fromItem(String item){
        for (PizzaEnum pizzaEnum : PizzaEnum.values()){
            if (pizzaEnum.item.equals(item))
                return pizzaEnum;
        }
        throw new IllegalArgumentException("没有找到对应口味的披萨");
    }
}

来做下实验:

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("请输入您想去的城市:WS/NY");
        String name = in.nextLine();
        PizzaStore pizzaStore;
        if (name.equals("WS")){
            pizzaStore = new WSPizzaStore();
        }else {
            pizzaStore = new NYPizzaStore();
        }
        System.out.println("请输入您想吃的披萨类型:cheese/veggie");
        String item = in.nextLine();
        pizzaStore.orderPizza(PizzaEnum.fromItem(item));
    }

结果:



看到结果跟我们所期望的一样,接下来正式介绍一下工厂方法

定义

工厂方法模式(FACTORY METHOD)是一种常用的对象创建型设计模式,此模式的核心精神是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。它的核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品

定义里面提到了依赖注入,相关的还有一个叫做控制反转(IoC)的东西,都简单介绍下:

  • 依赖注入(DI):Dependency Injection,其实与工厂方法定义很像,组件不负责查找资源或其他依赖的对象,这个工作交给IoC容器来做,由容器来返回需要的对象
  • 控制反转(Ioc):Inversion of Control,指的就是上面的容器,本来创建对象的工作是组件自己实例化的,现在我们利用IoC容器,将这个事情交给固定的容器来负责,组件将更多精力放在其他的逻辑上面,管理起来更方便,不会时常出现一些忘记初始化的错误了

Spring的核心思想就是依赖注入和控制反转,通过定义Bean将实例化Bean的操作交给配置文件来管理,其他类需要的时候配置文件将bean的实例传给需要的类即可,关于这方面后续会有相应的笔记记录

优缺点

优点

  • 我们在创建实例时不用关心具体创建了哪种实例,怎么创建的,只需把参数传入工厂即可
  • 很明显,当我们有新的种类添加进来时只需要在具体工厂里面添加相应的条件即可,满足"开闭原则"

缺点

  • 有新的种类加入时需要添加新的类,数目会较多
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容