zoukankan      html  css  js  c++  java
  • 设计模式 -- 简单工厂模式

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

    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. 如果想要把对外创建对象的职责集合管理和控制,可以选用简单工厂,一个简单工厂可以创建很多的、不相关的对象,可以把对外创建对象的职责集中到一个工厂来,从而实现集中管理和控制
  • 相关阅读:
    算法题
    AIO和NIO的理解
    Redis面试考点
    对MVVM的理解
    Vuex状态管理模式
    vue 的computed 和 watch 两者的区别
    vue之组件通信
    vue生命周期钩子函数
    angularjs 运行时报错ERROR in node_modules/rxjs/internal/types.d.ts(81,44): error TS1005: ';' expected. node_modules/rxjs/internal/types.d.ts(81,74): error TS1005: ';' expected. node_modules/rxjs/internal/t
    浅入不深出--vuex的简单使用
  • 原文地址:https://www.cnblogs.com/xinhuaxuan/p/6419745.html
Copyright © 2011-2022 走看看