设计模式笔记(四): 工厂模式

new是创建对象实例时最常用的方法(不是唯一方法,反射也可以创建实例),但是一旦涉及到new,就会涉及到具体实现类,而非接口,这就可能带来一些问题。例如下述代码:

Car car = new Car();
Wheel wheel = null;
if (type.equal("A")) {
  wheel = new AWheel();
}  else if (type.equal("B")) {
  wheel = new BWheel();
} else if (type.equal("C")) {
  wheel = new CWheel();
}
car.setWheel(wheel);

很简单的代码,创建一个汽车对象和轮子对象,然后根据type来选择使用哪种轮子。这里代码是有问题的,如果随着时间的变化,A轮子不生产了,也就是A轮子不能使用了,那我们就不得不修改代码,将A相关的代码移除,但有很多其他客户端也是使用这样的代码,这意味着其他客户端的代码也必须得改。换句话说,这里一系列的判断是会“变化”的部分。比较好的方法是将其封装起来,单独放到一个地方去,以后如果有变化,只需改动一处即可。这就是简单工厂模式。

简单工厂模式

我们换个例子,现在来看看一个披萨的例子。如下代码所示:

//Pizza抽象类
public abstract class Pizza {

    protected String name;

    public abstract void bake();
    public abstract void cut();

    @Override
    public String toString() {
        return "Pizza{" +
                "name='" + name + '\'' +
                '}';
    }
}

//A类型Pizza
public class APizza extends Pizza {

    public APizza() {
        this.name = "A Pizza";
    }

    @Override
    public void bake() {
        System.out.println("A bake");
    }

    @Override
    public void cut() {
        System.out.println("A cut");
    }
}

//B类型Pizza
public class BPizza extends Pizza {

    public BPizza() {
        this.name = "B Pizza";
    }

    @Override
    public void bake() {
        System.out.println("B bake");
    }

    @Override
    public void cut() {
        System.out.println("B cut");
    }
}

//Pizza商店
public class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza = null;
        if ("A".equals(type)) {
            pizza = new APizza();
        } else if ("B".equals(type)) {
            pizza = new BPizza();
        }
        if (pizza != null) {
            pizza.bake();
            pizza.cut();
        }
        return pizza;
    }
}

//测试类
public class Main {

    public static void main(String[] args) {
        PizzaStore store = new PizzaStore();
        Pizza pizza = store.orderPizza("A");

    }
}

运行测试类,可以看到如下结果:

A bake
A cut

符合我们的预期,但是如果我们有很多个商店,而且这些商店订购披萨的逻辑都是根据类型来判断(注意各个商店卖的Pizza不一定相同,所以往往不能抽到基类中去),即上述的if-else if ..结构。那么如果现在不打算生产A类型的Pizza了,取而代之的是C类型Pizza,我们就不得不到每个和A类型Pizza相关的商店代码里更改。

现在用简单工厂模式来改写一下代码,将if-else结构抽到工厂类里。如下代码所示:

//简单工厂
public class SimplePizzaFactory {

    public Pizza createPizza(String type) {
        
        if ("A".equals(type)) {
            return new APizza();
        } else if ("B".equals(type)) {
            return new BPizza();
        }
        return null;
    }
}

//修改后的Pizza商店
public class PizzaStore {

    private SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza = this.factory.createPizza(type);
        if (pizza != null) {
            pizza.bake();
            pizza.cut();
        }
        return pizza;
    }
}

其余类不做修改,这样做的好处是将变化的部分抽到一个单独的类中,Pizza商店类仅仅依赖工厂的实现,而不依赖Pizza的具体实现,可以简单理解成Pizza商店不再自己生产Pizza,而是需要什么样的Pizza就到一个工厂中去“取货”即可。

现在会过来分析一下如果需求变化了会发生什么?如果某个Pizza商店不需要A类型Pizza了(可能是因为不好卖),Pizza该怎么做呢?在我们的代码里,他什么也不用做!不需要修改任何代码即可,这是因为生产Pizza的责任交由Pizza工厂来管理了,客人需要什么样的Pizza,商店直接去Pizza工厂拿就行了,不关心Pizza具体是怎样的。

不过这种方法也有局限性,例如现在有很多商店,这些商店可能想要一些自己的特色,不想到工厂里获取(注意,工厂也可以有很多种,商店可以任意选择到哪个工厂获取),该怎么办?一种解决方案是利用工厂方法模式

工厂方法模式

工厂方法模式和简单工厂的区别是,生产产品的地方不是类,而是在一个方法里,如下代码所示:

public abstract class PizzaStore {



    public Pizza orderPizza(String type) {

        Pizza pizza = createPizza(type);

        if (pizza != null) {
            pizza.bake();
            pizza.cut();
        }
        return pizza;
    }

    protected abstract Pizza createPizza(String type);
}

//注意这里的createPizza(String type);是抽象方法。

public class NYPizzaStore extends PizzaStore {

    @Override
    protected Pizza createPizza(String type) {
        if (type.equals("A")) {
            return new APizza();
        }
        return null;
    }
}

public class CHPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        if (type.equals("B"))
            return new BPizza();
        return null;
    }
}

//测试类
public class Main {

    public static void main(String[] args) {
        PizzaStore pizzaStore = new NYPizzaStore();
        pizzaStore.orderPizza("A");
    }
}

可以看到上述代码不再使用单独的工厂类了,具体的生产Pizza的责任交由每个商店自己处理,自己想要干嘛就干嘛,而且自己负责。

抽象工厂模式

现在各个商店还不满足,他们发现客人对原料的要求很高,所以他们想自己去拿原料,各地的原料的质量又不一样,那到哪拿呢?怎么拿呢?这种情况可以用抽象工厂模式解决,引入一个抽象工厂,其子类是具体的工厂,然后各个商店决定从哪个工厂拿材料。如下代码所示:

public interface PizzaFactory {

    void createSourceA();
    void createSourceB();
    void createSourceC();
}

public class APizzaFactory implements PizzaFactory {
    @Override
    public void createSourceA() {
        System.out.println("A create source A");
    }

    @Override
    public void createSourceB() {
        System.out.println("A create source B");
    }

    @Override
    public void createSourceC() {
        System.out.println("C create source C");
    }
}

//B工厂的代码和A工厂差不多,不贴了。

//修改后的商店
public class NYPizzaStore extends PizzaStore {

    @Override
    protected Pizza createPizza(String type) {

        PizzaFactory pizzaFactory = new APizzaFactory();

        if (type.equals("A")) {
            return new APizza(pizzaFactory);
        }
        return null;
    }
}

//修改后的A类型Pizza
public class APizza extends Pizza {

    private PizzaFactory pizzaFactory;

    public APizza(PizzaFactory pizzaFactory) {
        this.pizzaFactory = pizzaFactory;
        this.name = "A Pizza";
    }

    @Override
    public void bake() {
        this.pizzaFactory.createSourceA();
        this.pizzaFactory.createSourceB();
        this.pizzaFactory.createSourceC();
        System.out.println("A bake");
    }

    @Override
    public void cut() {
        System.out.println("A cut");
    }
}

//测试类
public class Main {

    public static void main(String[] args) {
        PizzaStore pizzaStore = new NYPizzaStore();
        pizzaStore.orderPizza("A");

    }
}

这样一来,各个商店可以自己决定从哪个工厂拿原料来生成Pizza了。

小结

工厂模式主要有三种:

  1. 简单工厂(常见的静态工厂也属于简单工厂)。
  2. 工厂方法。
  3. 抽象工厂。

简单工厂很简单直观,就是将生产产品的逻辑抽离到一个单独的类中做管理,需要的时候就去拿。但是有局限性,即不够灵活。

工厂方法的灵活性比简单工厂要高,通过继承,子类可以自行决定生产的逻辑。

抽象工厂和工厂方法的区别不是那么明显,比较直观的区别是工厂方法是通过继承来做的,抽象工厂主要是通过组合来实现。抽象工厂的缺点是扩展需要新增子类,而工厂方法仅需要实现方法的具体逻辑即可,不过抽象工厂的结构会更加明显,易于阅读。总之,这两种方式效果差不多,具体选择哪种,还得看实际情况决定。

下面是工厂模式的定义:

工厂方法模式:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例推迟到子类

抽象工厂模式: 抽象工厂模式提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。

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

推荐阅读更多精彩内容

  • 1.简述 顾名思义,工厂就是生产产品的嘛!工厂模式(Factory Pattern)的定义也差不多就是这个意思,提...
    MrTrying阅读 3,488评论 1 7
  • 每日一文 反以观往,覆以验来;反以知古,覆以知今;反以知彼,覆以知己。动静虚实之理,不合来今,反古而求之。 简单工...
    summer_lz阅读 278评论 0 0
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,922评论 1 15
  • 每个人不是天生就是什么都要会的,所以学习才是我们的天赋。蓝小雨的人情做透好用。 我们必要掌握的技巧都有哪些呢! 1...
    简单做事阅读 199评论 0 0
  • 每一个IT公司产品部和研发部永远是一对欢喜冤家,基本上都是貌合神离的存在。 产品、研发、用户三者之间就像家庭一样存...
    水墨道人阅读 956评论 0 3