工厂模式的个人小结

栏目: 后端 · 发布时间: 9年前

内容简介:工厂模式的个人小结

工厂模式是一种常见的 设计模式 了。当我们实例化一个具体类时往往会第一时间想到用new。当子类需要被分成不同的情况去实例化时,我们一般想到的就是写一些if判断,而这种处理方式会给我们今后代码的维护带来很多麻烦。如何将实例化具体类的代码从应用中抽离,或者封装起来,使它们不会干扰应用的其他部分?

首先我们从披萨店的设计中看看工厂模式为什么需要出现?

假设你有一个披萨店,原来你要生产一个披萨代码你可能是这样写:

Pizza orderPizza(){
    // 创建pizza
    Pizza pizza = new Pizza();
    // 对披萨的加工
    ...
    return pizza;
}

但是,当你需要更多口味的披萨时,你可能会考虑将代码改写一下:

Pizza orderPizza(String type){
    // 创建pizza
    Pizza pizza;
    if ("cheese".equals(type)) {
        pizza = new CheesePizza();
    } else if ("greek".equals(type)) {
        pizza = new GreekPizza();
    }
    // 对披萨的加工
    ...
    return pizza;
}

随着生意的发展,问题又来了。披萨的种类需求越来越多,需要新增或删除披萨的种类时往往需要去修改上面的代码。

让我们回到OO设计原则,看看怎么改动这段代码比较好,最基本的单一原则。将创建披萨的部分抽离出来,看成一个新对象,这个新对象只管如何创建对象。我们称这个对象为“工厂”。

简单工厂方法

简单工厂方法与其说是工厂模式的一种,更不如说它其实就是我们的一种编程习惯。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

public class SimplePizzaFactory {
    public Pizza createPizza(String type){
        Pizza pizza = null;
        if ("cheese".equals(type)) {
            pizza = new CheesePizza();
        } else if ("greek".equals(type)) {
            pizza = new GreekPizza();
        }
        return pizza;
    }
}

这样,我们的披萨店在生产披萨的时候代码就改写成这样了

public class PizzaStore {
    SimplePizzaFactory factory;
    public PizzaStore(SimplePizzaFactory factory){
        this.factory = factory;
    }
    public Pizza orderPizza(String type){
        Pizza pizza = factory.createPizza(type);
        return pizza;
    }
    public static void main(String args[]){
        PizzaStore pizzaStore = new PizzaStore(new SimplePizzaFactory());
        Pizza pizza = pizzaStore.orderPizza("cheese");
        System.out.println(pizza.toString());
    }
}

简单工厂方法仅仅是遵守了单一原则,将创建对象的责任承担了下来,如果有新增披萨种类我们还是只能在createPizza方法中加判断条件,这还是违背了开闭原则(对扩展开放,对修改关闭)的。有没有解决办法?答案是有的,工厂方法模式出现了。

工厂方法模式

工厂类定义成了接口,而每新增的披萨类型,就增加该披萨类型对应工厂类的实现,这样工厂的设计就可以扩展了,而不必去修改原来的代码。工厂方法模式通过让子类决定该创建的对象是什么,把类的实例化推迟到子类,来达到将对象创建的过程封装的目的。根据这个思想,我们开两家专门卖纽约口味的和芝加哥口味披萨的分店,让他们分别为我们总店提供想要的披萨吧。

先把总店变成抽象类

public abstract class PizzaStore {
    abstract Pizza createPizza(String type);
    public Pizza orderPizza(String type){
        Pizza pizza = createPizza(type);
        return pizza;
    }
}

然后我们来开两家分店

芝加哥分店

public class ChicagoPizzaStore extends PizzaStore {
    @Override
    Pizza createPizza(String type) {
        if ("cheese".equals(type)) {
            return new ChicagoStyleCheesePizza();
        } else{
            return null;
        }
    }
}

纽约分店

public class NYPizzaStore extends PizzaStore {
    @Override
    Pizza createPizza(String type) {
        if ("cheese".equals(type)) {
            return new NYStyleCheesePizza();
        } else{
            return null;
        }
    }
}

披萨也要分口味

public class ChicagoStyleCheesePizza extends CheesePizza {
    @Override
    public String toString() {
        return super.toString()+" of Chicago";
    }
}
public class NYStyleCheesePizza extends CheesePizza  {
    @Override
    public String toString() {
        return super.toString()+" of NY";
    }
}

点个芝士披萨看看吧

public class PizzaTest {
    public static void main(String args[]){
        PizzaStore nyStore = new NYPizzaStore();
        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println(pizza.toString());
        PizzaStore chicagoStore = new ChicagoPizzaStore();
        pizza = chicagoStore.orderPizza("cheese");
        System.out.println(pizza.toString());
    }
}

执行结果截图如下:

工厂模式的个人小结

抽象工厂模式

工厂方法模式能够封装具体类型的实例化,相当于说搭了一个框架,让子类决定要如何实现,说白了也是一些简单工厂方法的集合。我们在用的时候只需要关心是哪个店出来的披萨就好,不需要关心具体实现。但是还有一个我们不想看到的问题,当披萨的不同口味的分店越来越多时,同样会多很多新的类。披萨店依赖于所有的披萨对象,因为它直接创建这些披萨对象。对具体类的依赖过多这恰恰不遵守了OO设计原则的依赖倒置原则(要依赖抽象,不要依赖具体类)。依赖倒置原则,很抽象,暂时不用理会。披萨店原本依赖于很多具体的披萨对象,披萨的口味不一样也就是做法、原料有略微不同而已,现在这些不同口味的披萨我们都可以抽象成披萨,也就是说披萨店依赖于抽象出来的披萨(Pizza),至于具体的披萨对象,它们则依赖抽象出来的披萨(因为它们都实现了Pizza接口),这样一来依赖就倒置了。我们可以将我们的工厂拆分成四个角色:抽象的工厂和具体的工厂;抽象的产品和具体的产品。

下面看具体的一个例子吧

抽象出一个披萨类

public abstract class Pizza {
    String name; // 名称
    String material; // 材料
    String practice; // 做法
    abstract void prepare();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "[name:"+name+";material:"+material
                +";practice:"+practice+"]";
    }
}

抽象出一个工厂接口,负责做披萨的材料和做法

public interface PizzaFactory {
    public String getMaterial();
    public String getPractice();
}

纽约风味披萨的实现

public class NYPizzaFactory implements PizzaFactory {
    @Override
    public String getMaterial() {
        return "纽约的面粉";
    }

    @Override
    public String getPractice() {
        return "纽约的做法";
    }

}

芝士披萨

public class CheesePizza extends Pizza {
    PizzaFactory factory;
    public CheesePizza(PizzaFactory factory){
        this.factory = factory;
    }

    void prepare() {
        System.out.println(name+"准备中...");
        material = factory.getMaterial();
        practice = factory.getPractice();
    }
}

负责做纽约披萨的分店,总店的定义还是没变

public class NYPizzaStore extends PizzaStore {
    @Override
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        PizzaFactory factory = new NYPizzaFactory();
        if ("cheese".equals(type)) {
            pizza = new CheesePizza(factory);
            pizza.setName("纽约口味的芝士披萨");
        }
        return pizza;
    }
}

准备待续,买个披萨吧

public class PizzaTest {
    public static void main(String args[]){
        PizzaStore nyStore = new NYPizzaStore();
        Pizza pizza = nyStore.orderPizza("cheese");
        pizza.prepare();
        System.out.println(pizza.toString());
    }
}

执行结果如图所示:

工厂模式的个人小结

抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦。

无论是简单工厂方法,工厂方法模式,还是抽象工厂模式,它们的作用都是为了解耦,在平时工作的使用中根据需求灵活地使用,而不必拘束于只使用一种。

以上就是我对《Head First 设计模式》中关于工厂模式的一些理解及参考书上的例子自己写的一些例子。记录一下方便今后复习。详细代码见 Github


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Practical Vim, Second Edition

Practical Vim, Second Edition

Drew Neil / The Pragmatic Bookshelf / 2015-10-31 / USD 29.00

Vim is a fast and efficient text editor that will make you a faster and more efficient developer. It’s available on almost every OS, and if you master the techniques in this book, you’ll never need an......一起来看看 《Practical Vim, Second Edition》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具