[关闭]
@w1992wishes 2017-11-01T16:52:12.000000Z 字数 7013 阅读 784

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

设计模式 创建型模式 工厂模式


设计原则:

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

目录

本文的结构如下:

一、前言

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

二、什么是工厂方法模式

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

三、为什么要用该模式

3.1、官方解释

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

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

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

3.2、举个例子

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

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

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

  1. /**
  2. * Created by w1992wishes on 2017/10/31.
  3. */
  4. public class SimpleCakeFacroty {
  5. public static Cake createCake(String location, String type){
  6. Cake cake;
  7. if ("center".equalsIgnoreCase(location)){
  8. if ("cheese".equals(type)) {
  9. cake = new CenterCheeseCake();
  10. } else if ("fruit".equals(type)) {
  11. cake = new CenterFruitCake();
  12. } else if ("cream".equals(type)) {
  13. cake = new CenterCreamCake();
  14. } else {
  15. cake = new CenterDefaultCake();
  16. }
  17. }else if("college".equalsIgnoreCase(location)){
  18. if ("cheese".equals(type)) {
  19. cake = new CollegeCheeseCake();
  20. } else if ("fruit".equals(type)) {
  21. cake = new CollegeFruitCake();
  22. } else if ("cream".equals(type)) {
  23. cake = new CollegeCreamCake();
  24. } else {
  25. cake = new CollegeDefaultCake();
  26. }
  27. }else if("other".equalsIgnoreCase(location)){
  28. if ("cheese".equals(type)) {
  29. cake = new OtherCheeseCake();
  30. } else if ("fruit".equals(type)) {
  31. cake = new OtherFruitCake();
  32. } else if ("cream".equals(type)) {
  33. cake = new OtherCreamCake();
  34. } else {
  35. cake = new OtherDefaultCake();
  36. }
  37. }
  38. return cake;
  39. }
  40. }

所以CakeStore是这样的:

  1. public class CakeStore {
  2. public Cake orderCake(String location, String type) {
  3. Cake cake;
  4. cake = SimpleCakeFacroty.createCake(location, type);
  5. cake.bake();
  6. cake.box();
  7. return cake;
  8. }
  9. }

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

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

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

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

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

四、模式的结构

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

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

五、代码示例

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

  1. /**
  2. * Created by w1992wishes on 2017/11/01.
  3. */
  4. public abstract class CakeStore {
  5. public Cake orderCake(String type) {
  6. Cake cake;
  7. cake = createCake(type);
  8. cake.bake();
  9. cake.box();
  10. return cake;
  11. }
  12. protected abstract Cake createCake(String type);
  13. }

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

  1. /**
  2. * Created by w1992wishes on 2017/11/1.
  3. */
  4. public class CenterCakeStore extends CakeStore {
  5. @Override
  6. protected Cake createCake(String type) {
  7. Cake cake = null;
  8. if ("cheese".equals(type)) {
  9. cake = new CenterCheeseCake();
  10. } else if ("fruit".equals(type)) {
  11. cake = new CenterFruitCake();
  12. } else if ("cream".equals(type)) {
  13. cake = new CenterCreamCake();
  14. }
  15. return cake;
  16. }
  17. }
  18. /**
  19. * Created by w1992wishes on 2017/11/1.
  20. */
  21. public class CollegeCakeStore extends CakeStore {
  22. @Override
  23. protected Cake createCake(String type) {
  24. Cake cake = null;
  25. if ("cheese".equals(type)) {
  26. cake = new CollegeCheeseCake();
  27. } else if ("fruit".equals(type)) {
  28. cake = new CollegeFruitCake();
  29. } else if ("cream".equals(type)) {
  30. cake = new CollegeCreamCake();
  31. }
  32. return cake;
  33. }
  34. }

抽象产品:

  1. /**
  2. * Created by w1992wishes on 2017/11/01.
  3. */
  4. public abstract class Cake {
  5. void prepare(){
  6. System.out.println("step 1......");
  7. System.out.println("step 2......");
  8. System.out.println("step 3......");
  9. System.out.println("step 4......");
  10. }
  11. void bake(){
  12. System.out.println("bake");
  13. }
  14. void box(){
  15. System.out.println("box");
  16. }
  17. }

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

  1. * Created by w1992wishes on 2017/10/31.
  2. */
  3. public class CenterCheeseCake extends Cake {
  4. public CenterCheeseCake(){
  5. name = "center cheese cake";
  6. }
  7. @Override
  8. public void bake(){
  9. System.out.println("不用烘箱,我要用火烤!");
  10. }
  11. }
  12. /**
  13. * Created by w1992wishes on 2017/11/1.
  14. */
  15. public class CollegeFruitCake extends Cake {
  16. public CollegeFruitCake(){
  17. name = "center fruit cake";
  18. }
  19. @Override
  20. public void box(){
  21. System.out.println("不用圆盒子打包,我爱国,用五角星盒子!");
  22. }
  23. }

最后客户端:

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

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

六、优点和缺点

6.1、工厂方法模式的优点

6.2、工厂方法模式的缺点

七、适用环境

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

八、模式应用

JDBC中的工厂方法:

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

九、模式扩展

十、总结

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注