工厂模式的个人小结

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

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

工厂模式是一种常见的 设计模式 了。当我们实例化一个具体类时往往会第一时间想到用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


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

查看所有标签

猜你喜欢:

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

深入浅出SQL(中文版)

深入浅出SQL(中文版)

贝里 编 / O‘Reilly Taiwan公司 / 东南大学 / 2009-6 / 98.00元

你将从《深入浅出SQL(中文版)》学到什么?在如今的世界,数据就是力量,但是成功的真正秘诀却是管理你的数据的力量。《深入浅出SQL(中文版)》带你进入SQL语言的心脏地带,从使用INSERT和SELECT这些基本的查询语法到使用子查询(subquery)、连接(join)和事务(transaction)这样的核心技术来操作数据库。到读完《深入浅出SQL(中文版)》之时,你将不仅能够理解高效数据库设......一起来看看 《深入浅出SQL(中文版)》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具