设计模式 -- 简单工厂模式

写在前面的话:读书破万卷,编码如有神
--------------------------------------------------------------------
主要内容包括:

  1. 初识简单工厂模式,包括: 定义、结构
  2. 体会简单工厂模式,包括: 场景问题、不用模式的解决方案、使用模式的解决方案
  3. 理解简单工厂模式,包括: 典型疑问、认识简单工厂、简单工厂中方法的写法、可配置的简单工厂、简单工厂的优缺点
  4. 思考简单工厂模式,包括: 简单工厂的本质、何时选用

参考内容:

1、《研磨设计模式》 一书,作者:陈臣、王斌

--------------------------------------------------------------------

1、初识简单工厂                                                                    

1.1、定义

  提供一个创建对象实例的功能,而无须关心具体的实现。被创建的实例的类型可以是接口、抽象类,也可以是具体的类。

1.2、结构和说明

  

说明:

  1. Api : 定义客户端所需要的功能接口
  2. Impl : 具体实现Api的实现类,可能会有多个
  3. Factory : 工厂,选择合适的实现类来创建Api接口对象
  4. Client: 客户端,通过Factory来获取Api接口对象,然后面向Api接口编程

--------------------------------------------------------------------

2、体会简单工厂模式                                                             

2.1、接口回顾

(1)Java中接口的概念

  在Java中接口是一种特殊的抽象类,跟一般的抽象类相比,接口里面的所有方法都是抽象的,接口里面的所属属性都是常量。也就是说,接口里面只有方法定义而没有方法的实现。

(2)接口用来干什么

  通常用接口来定义实现类的外观,也就是实现类的行为定义,用来约束实现类的行为,接口相当于一份契约,根据外部应用需要的功能,约定了实现类应该要实现的功能。

(3)接口的思想

  根据接口的作用和用途,可以总结为 “封装隔离”。 通常提到的封装是指对数据的封装,但是这里的封装是指"对被隔离体的行为的封装",或者是"对被隔离体的职责的封装",而隔离指的是外部调用和内部实现,外部调用只能通过接口进行调用,外部调用是不知道内部具体实现的,也就是说外部调用和内部实现是被接口隔离开的。

(4)使用接口的好处

  只要接口不变,内部实现的变化就不会影响到外部应用,从而使得系统更灵活,具有更好的扩展性和可维护性。

(5)接口和抽象类的选择

  a. 优先选用接口

  b. 在既要定义子类的行为,又要为子类提供公共的功能时应该选用抽象类

2.2、面向接口编程

面向接口编程是Java编程中一个重要规则,在Java程序设计里面,非常讲究层的划分和模块的划分。比如场景的三层结构:

在一个层内部的各个模块交互也要通过接口

既然在Java中需要面向接口编程,那么在程序中到底该如何使用接口,来做到真正的面向接口编程呢?

比如目前有接口: Api , 实现类: ImplA、ImplB  客户端:Client

画一个简单的类图:

2.3、不用模式的解决方案

 1 (1)首先是接口的定义Api,如下:
 2 /**
 3  * 某个接口(通用的、抽象的、非具体的功能)
 4  * @author Administrator
 5  *
 6  */
 7 public interface Api {
 8     /**
 9      * 某个具体的方法定义
10      * @param msg
11      */
12     public void operation(String msg);
13 }
14 (2)具体的实现类ImplA,如下:
15 /**
16  * 具体的实现类A
17  * @author Administrator
18  *
19  */
20 public class ImplA implements Api {
21 
22     @Override
23     public void operation(String msg) {
24         System.out.println("ImplA  , msg = " + msg);
25     }
26 }
27 (3)具体的实现类ImplB,如下:
28 /**
29  * 具体的实现类B
30  * @author Administrator
31  *
32  */
33 public class ImplB implements Api {
34 
35     @Override
36     public void operation(String msg) {
37         System.out.println("ImplB  , msg = " + msg);
38     }
39 }
40 (4)客户端,如下:
41 /**
42  * 客户端
43  * @author Administrator
44  *
45  */
46 public class Client {
47     public static void main(String[] args) {
48         Api api = new ImplA();
49         api.operation("哈哈,嘻嘻");
50         
51         System.out.println("------------------");
52         api = new ImplB();
53         api.operation("嘿嘿,嘎嘎");
54     }
55 }
56 
57 运行结果:
58 ImplA , msg = 哈哈,嘻嘻
59 ------------------
60 ImplB  , msg = 嘿嘿,嘎嘎

2.4、有何问题

重点看看客户端代码中的下面两行:

1 Api api = new ImplA();
2 api = new ImplB();

我们会发现在客户端调用的时候,客户端不但知道了接口,而且还知道具体的实现类:ImplA、ImplB。接口的思想是"封装隔离",而实现类ImplA、ImplB应该是被接口Api封装并同客户端隔离开的,换句话说, 客户端根本就不应该知道具体的实现是ImplA、ImplB

把这个问题描述一下:在Java编程中,出现只知接口而不知实现,该肿么办?

2.5、使用简单工厂来解决问题

 1 /**
 2  * 某个接口(通用的、抽象的、非具体的功能)
 3  * @author Administrator
 4  *
 5  */
 6 public interface Api {
 7     /**
 8      * 某个具体的方法定义
 9      * @param msg
10      */
11     public void operation(String msg);
12 }
13 
14 /**
15  * 具体的实现类A
16  * @author Administrator
17  *
18  */
19 public class ImplA implements Api {
20 
21     @Override
22     public void operation(String msg) {
23         System.out.println("ImplA  , msg = " + msg);
24     }
25 }
26 
27 /**
28  * 具体的实现类B
29  * @author Administrator
30  *
31  */
32 public class ImplB implements Api {
33 
34     @Override
35     public void operation(String msg) {
36         System.out.println("ImplB  , msg = " + msg);
37     }
38 }
39 
40 /**
41  * 工厂类,用来创建Api对象
42  */
43 public class Factory {
44     /**
45      * 具体创建Api对象的方法
46      * @param condition 选择条件
47      * @return 创建好的Api对象
48      */
49     public static Api createApi(int condition){
50         Api api = null;
51         if(condition == 1){
52             api = new ImplA();
53         }else if(condition == 2){
54             api = new ImplB();
55         }
56         return api;
57     }
58 }
59 
60 /**
61  * 客户端
62  * @author Administrator
63  *
64  */
65 public class Client {
66     public static void main(String[] args) {
67         //重要改变,没有new Impl()了,取而代之Factory.createApi();
68         Api api = Factory.createApi(1);
69         api.operation("哈哈,嘻嘻");
70         
71         System.out.println("------------------");
72         api = Factory.createApi(2);
73         api.operation("嘿嘿,嘎嘎");
74     }
75 }
76 
77 运行结果:
78 ImplA  , msg = 哈哈,嘻嘻
79 ------------------
80 ImplB  , msg = 嘿嘿,嘎嘎

--------------------------------------------------------------------

3、理解简单工厂                                                                  

3.1、典型疑问

  可能会有人认为,上面示例中的简单工厂看起来不就是把客户端里面的"new ImplA()、new ImplB()"移动到简单工厂里面吗? 不还是一样通过new一个实现类来得到接口吗? 把"new ImplA()、new ImplB()"放到客户端和放到简单工厂里面有什么不同吗?

ps: 理解这个问题的重点就在于理解简单工厂所处的位置。

简单工厂的位置是位于封装体内的,也就是简单工厂是跟接口和具体的实现在一起的,算是封装体内部的一类,所以简单工厂知道具体的实现类是没有关系的。重新整理一下简单工厂的结构图,如下图所示:

3.2、认识简单工厂

(1)简单工厂模式的功能

  可以用来创建的接口、抽象类或者是普通类的实例。

(2)静态工厂

  通常把简单工厂类实现成一个工具类,直接使用静态方法就可以了,也就是说简单工厂的方法通常都是静态的,所以也称为静态工厂。

(3)万能工厂

  一个简单工厂可以包含很多用来构造东西的方法,这些方法可以创建不同的接口、抽象类或者是类实例。一个简单工厂理论上可以构造任何东西。所以又称之为“万能工厂”

(4)简单工厂创建对象的范围

  虽然从理论上将,简单工厂什么都能创建,但对于简单工厂可创建对象的范围,通常不要太大,最好是控制在一个独立的组件级别或者一个模块级别。

(5)简单工厂的调用顺序示意图

3.3、简单工厂中方法的写法

  仔细分析会发现,简单工厂方法的内部主要实现的功能是"选择合适的实现类"来创建实例对象,既然要实现选择,那么就需要选择的条件或者是选择的参数,选择条件或者是参数的来源通常有以下几种:

  1. 来源于客户端,由Client来传入参数
  2. 来源于配置文件,从配置文件获取用于判断的值
  3. 来源于程序运行期间的某个值,比如从缓存中获取某个运行期的值

3.4、可配置的简单工厂

通过配置文件的方式来实现简单工厂

  1 /**
  2  * 某个接口(通用的、抽象的、非具体的功能)
  3  * @author Administrator
  4  *
  5  */
  6 public interface Api {
  7     /**
  8      * 某个具体的方法定义
  9      * @param msg
 10      */
 11     public void operation(String msg);
 12 }
 13 
 14 
 15 /**
 16  * 具体的实现类A
 17  * @author Administrator
 18  *
 19  */
 20 public class ImplA implements Api {
 21 
 22     @Override
 23     public void operation(String msg) {
 24         System.out.println("ImplA  , msg = " + msg);
 25     }
 26 }
 27 
 28 /**
 29  * 具体的实现类B
 30  * @author Administrator
 31  *
 32  */
 33 public class ImplB implements Api {
 34 
 35     @Override
 36     public void operation(String msg) {
 37         System.out.println("ImplB  , msg = " + msg);
 38     }
 39 }
 40 
 41 
 42 import java.io.IOException;
 43 import java.io.InputStream;
 44 import java.util.Properties;
 45 
 46 /**
 47  * 工厂类,用来创建Api对象
 48  */
 49 public class Factory {
 50 
 51     /**
 52      * 具体创建Api对象的方法,根据配置文件的参数来创建接口
 53      * @return
 54      */
 55     public static Api createApi(){
 56         //直接读取配置文件来获取需要创建实例的类
 57         Properties p = new Properties();
 58         InputStream in = null;
 59         try{
 60             in = Factory.class.getResourceAsStream("factory.properties");
 61             p.load(in);
 62         }catch(IOException e){
 63             System.out.println("找不到配置文件!");
 64             e.printStackTrace();
 65         }finally{
 66             try{
 67                 in.close();
 68             }catch(IOException e){
 69                 e.printStackTrace();
 70             }
 71         }
 72         
 73         //利用反射区创建
 74         Api api = null;
 75         
 76         try {
 77             api = (Api)Class.forName(p.getProperty("ImplClass")).newInstance();
 78         } catch (InstantiationException e) {
 79             e.printStackTrace();
 80         } catch (IllegalAccessException e) {
 81             e.printStackTrace();
 82         } catch (ClassNotFoundException e) {
 83             e.printStackTrace();
 84         }
 85         return api;
 86     }
 87 }
 88 
 89 /**
 90  * 客户端
 91  * @author Administrator
 92  *
 93  */
 94 public class Client {
 95     public static void main(String[] args) {
 96         //重要改变,没有new Impl()了,取而代之Factory.createApi();
 97         Api api = Factory.createApi();
 98         api.operation("哈哈,嘻嘻");
 99     }
100 }
101 
102 配置文件factory.properties
103 ImplClass=Factory.ImplA
104 
105 
106 运行结果:
107 ImplA  , msg = 哈哈,嘻嘻

--------------------------------------------------------------------

4、思考简单工厂模式                                                                   

4.1、简单工厂模式的本质:选择实现

4.2、简单工厂的优缺点

优点:

  1. 帮助封装
  2. 解耦

缺点:

  1. 可能增加客户端使用的复杂度
  2. 不方便扩展子工厂

4.3、何时选用简单工厂 

  1. 如果想要完全封装隔离具体的实现,让让外部只能通过接口来操作封装体,那么可以选用简单工厂,让客户端通过工厂来获取相应的接口,而无须关系具体的实现
  2. 如果想要把对外创建对象的职责集合管理和控制,可以选用简单工厂,一个简单工厂可以创建很多的、不相关的对象,可以把对外创建对象的职责集中到一个工厂来,从而实现集中管理和控制
原文地址:https://www.cnblogs.com/xinhuaxuan/p/6419745.html