设计模式

设计模式--工厂方法模式

2017-11-01  本文已影响95人  w1992wishes

设计原则:

要依赖抽象,不要依赖具体类

目录

本文的结构如下:

一、前言

简单工厂模式虽然简单,但存在一个很严重的问题。当系统中需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,这必定要修改工厂类的源代码,将违背“开闭原则”,如何实现增加新产品而不影响已有代码?工厂方法模式应运而生,本文将介绍第二种工厂模式——工厂方法模式。

二、什么是工厂方法模式

工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

三、为什么要用该模式

3.1、官方解释

在简单工厂模式中只提供一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它需要知道每一个产品对象的创建细节,并决定何时实例化哪一个产品类。简单工厂模式最大的缺点是当有新产品要加入到系统中时,必须修改工厂类,需要在其中加入必要的业务逻辑,这违背了“开闭原则”。

此外,在简单工厂模式中,所有的产品都由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性,而工厂方法模式则可以很好地解决这一问题。

在工厂方法模式中,不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。

3.2、举个例子

还是用蛋糕店的例子说明一下。

你的蛋糕店很火热,每天前来买蛋糕的人络绎不绝,碰到休息日,更是火爆到不行,排队的人都排到了“金拱门”的门口,忙碌的你富有冲劲,决定掏开腰包,在另一个火爆地段--一所大学的门口再开一家分店,并且因为大学旁边学生较多,你打算做一些改良,让新开的分店蛋糕的口味更适合年轻人。忙碌富裕的你决定再找个程序猿来帮忙设计代码,但你就是葛朗台,你开的价钱太低,你只给10RMB,没有人肯干这个活,心好的我再次被你请来。

你给我说了你的想法,我心里一阵窃喜,这可以用上次get的简单工厂模式啊,于是我是这样设计的:

/**
 * Created by w1992wishes on 2017/10/31.
 */
public class SimpleCakeFacroty {
    public static Cake createCake(String location, String type){
        Cake cake;
        if ("center".equalsIgnoreCase(location)){
            if ("cheese".equals(type)) {
                cake = new CenterCheeseCake();
            } else if ("fruit".equals(type)) {
                cake = new CenterFruitCake();
            } else if ("cream".equals(type)) {
                cake = new CenterCreamCake();
            } else {
                cake = new CenterDefaultCake();
            }
        }else if("college".equalsIgnoreCase(location)){
            if ("cheese".equals(type)) {
                cake = new CollegeCheeseCake();
            } else if ("fruit".equals(type)) {
                cake = new CollegeFruitCake();
            } else if ("cream".equals(type)) {
                cake = new CollegeCreamCake();
            } else {
                cake = new CollegeDefaultCake();
            }
        }else if("other".equalsIgnoreCase(location)){
            if ("cheese".equals(type)) {
                cake = new OtherCheeseCake();
            } else if ("fruit".equals(type)) {
                cake = new OtherFruitCake();
            } else if ("cream".equals(type)) {
                cake = new OtherCreamCake();
            } else {
                cake = new OtherDefaultCake();
            }
        }
        return cake;
    }
}

所以CakeStore是这样的:

public class CakeStore {
    public Cake orderCake(String location, String type) {
        Cake cake;
        cake = SimpleCakeFacroty.createCake(location, type);
        cake.bake();
        cake.box();
        return cake;
    }
}

一个上午的功夫,我认真写出这段代码,带着满满的成就感把它交给了你,你拍了拍手上的面粉,结果后只瞥了一眼,就喷着口水对我说:“你写的代码就是一堆狗屎。”

没有任何犹豫,你再次辞退了我,但你的职业精神我很敬佩,在我垂头丧气离开之前,你没有给我10RMB的报酬,而是咬着牙对我说:

虽然你用了简单工厂模式,实现了蛋糕的创建和消费分离,但是这里却存在严重问题:

  1. 大量的if...else...相互嵌套,逻辑复杂,代码不直观,导致维护和测试都很困难,这真是最糟糕的代码;
  2. 扩展不灵活,必须修改静态工厂方法的业务逻辑,违反了“开闭原则”。
  3. 工厂方法和具体的蛋糕类严重耦合,而且具体的蛋糕类特别多,严重违背了“要依赖抽象,不要依赖具体”的设计原则。
  4. ......

怎么解决这个问题呢?工厂方法模式正好合适。具体代码呢?先等介绍完工厂方法模式的结构再看啦。

四、模式的结构

在工厂方法模式结构图中包含如下几个角色:

与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类。

五、代码示例

首先定义一个抽象工厂,这个抽象工厂有一个抽象方法用于生产具体产品:

/**
 * Created by w1992wishes on 2017/11/01.
 */
public abstract class CakeStore {
    public Cake orderCake(String type) {
        Cake cake;
        cake = createCake(type);
        cake.bake();
        cake.box();
        return cake;
    }

    protected abstract Cake createCake(String type);
}

在抽象工厂中声明了工厂方法但并未实现工厂方法,具体产品对象的创建由其子类负责,客户端针对抽象工厂编程,可在运行时再指定具体工厂类,具体工厂类实现了工厂方法,不同的具体工厂可以创建不同的具体产品。

/**
 * Created by w1992wishes on 2017/11/1.
 */
public class CenterCakeStore extends CakeStore {
    @Override
    protected Cake createCake(String type) {
        Cake cake = null;
        if ("cheese".equals(type)) {
            cake = new CenterCheeseCake();
        } else if ("fruit".equals(type)) {
            cake = new CenterFruitCake();
        } else if ("cream".equals(type)) {
            cake = new CenterCreamCake();
        }
        return cake;
    }
}
/**
 * Created by w1992wishes on 2017/11/1.
 */
public class CollegeCakeStore extends CakeStore {
    @Override
    protected Cake createCake(String type) {
        Cake cake = null;
        if ("cheese".equals(type)) {
            cake = new CollegeCheeseCake();
        } else if ("fruit".equals(type)) {
            cake = new CollegeFruitCake();
        } else if ("cream".equals(type)) {
            cake = new CollegeCreamCake();
        }
        return cake;
    }
}

抽象产品:

/**
 * Created by w1992wishes on 2017/11/01.
 */
public abstract class Cake {
    void prepare(){
        System.out.println("step 1......");
        System.out.println("step 2......");
        System.out.println("step 3......");
        System.out.println("step 4......");
    }
    void bake(){
        System.out.println("bake");
    }
    void box(){
        System.out.println("box");
    }
}

具体产品有自己独有的bake(),box()方法:

 * Created by w1992wishes on 2017/10/31.
 */
public class CenterCheeseCake extends Cake {
    public CenterCheeseCake(){
        name = "center cheese cake";
    }
    @Override
    public void bake(){
        System.out.println("不用烘箱,我要用火烤!");
    }
}
/**
 * Created by w1992wishes on 2017/11/1.
 */
public class CollegeFruitCake extends Cake {
    public CollegeFruitCake(){
        name = "center fruit cake";
    }
    @Override
    public void box(){
        System.out.println("不用圆盒子打包,我爱国,用五角星盒子!");
    }
}

最后客户端:

public class Client {
    public static void main(String[] args) {
        //这里可通过引入配置文件更改
        CakeStore cakeStore = new CenterCakeStore();
        Cake cake = cakeStore.orderCake("cheese");
    }
}

这样一改,往后想再新开一个蛋糕店,只需继承自CakeStore新增生产具体的Cake,而不需改动源代码,这样就符合了“开闭原则”;而且客户端更换蛋糕店可以通过配置来完成,同样不需修改源代码。

六、优点和缺点

6.1、优点

6.2、缺点

七、适用环境

在以下情况下可以使用工厂方法模式:

八、模式应用

JDBC中的工厂方法:

Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://loc
alhost:1433; DatabaseName=DB;user=sa;password=");
Statement statement=conn.createStatement();
ResultSet rs=statement.executeQuery("select * from UserInfo");

九、模式扩展

十、总结

上一篇下一篇

猜你喜欢

热点阅读