zoukankan      html  css  js  c++  java
  • 工厂方法模式

    1.简单工厂模式

         将对象的创建和使用分离,也就是在客户端与生产之间搭建一个中间类,这个类通过控制产品类的创建返回给客户端一个期望的值。

    首先,为所有的产品创建一个接口或者抽象类。

    【代码清单-1】

     1 package com_1.huawei.product;
     2 /**
     3  * 产品接口
     4  * @author Administrator
     5  *
     6  */
     7 public interface IProduct 
     8 {
     9     void work();
    10 }

    【代码清单-2】---具体产品类

     1 package com_1.huawei.product.Impl;
     2 
     3 import com_1.huawei.product.IProduct;
     4 
     5 public class WasherProduct implements IProduct {
     6 
     7     @Override
     8     public void work() {
     9         System.out.println("Washer is working");
    10     }
    11 
    12 }
    13 ============================
    14 package com_1.huawei.product.Impl;
    15 
    16 import com_1.huawei.product.IProduct;
    17 
    18 public class IceboxProduct implements IProduct {
    19 
    20     @Override
    21     public void work() {
    22         System.out.println("Icebox is working");
    23     }
    24 
    25 }

    【代码清单-3】---工厂类

     1 package com_1.huawei.factory;
     2 
     3 import com_1.huawei.product.IProduct;
     4 import com_1.huawei.product.Impl.IceboxProduct;
     5 import com_1.huawei.product.Impl.WasherProduct;
     6 
     7 /**
     8  * 工厂类
     9  * @author Administrator
    10  *
    11  */
    12 public class Factory
    13 {
    14     public static final int Washer = 0;
    15     public static final int Icebox = 1;
    16     
    17     public static IProduct create(int type)
    18     {
    19         if(type == Washer)
    20         {
    21             return new WasherProduct();
    22         }else if(type == Icebox)
    23         {
    24             return new IceboxProduct();
    25         }else
    26         {
    27             return null;
    28         }
    29         
    30     }
    31 }

    简单工厂模式的优点:

      1- 客户端可以免除创建一个工厂类的实例,并且可以直接通过工厂类的静态方法来获得自己想要的产品,降低了客户端和工厂类的耦合,实现了

    对象的创建和使用分离。

      2- 工厂类只是去调用产品实现类去获得产品的实例,自己并没有去创建。

      但是,当我们要去创建具体的产品对象时,还是需要到客户端去修改相关的代码,然后重新编译,违背了开闭原则。所以,借助于xml来适应变化的需求。

    添加一个type.xml文件:
    1 <thingtype>
    2     <type>Washer</type>
    3 </thingtype>

    以后,就只需修改xml文件中的值了,不用再修改客户端的代码。

    【代码清单-4】

     1 package utils;
     2 
     3 import java.io.File;
     4 import java.io.IOException;
     5 
     6 import javax.xml.parsers.DocumentBuilder;
     7 import javax.xml.parsers.DocumentBuilderFactory;
     8 import javax.xml.parsers.ParserConfigurationException;
     9 import javax.xml.soap.Node;
    10 
    11 import org.w3c.dom.Document;
    12 import org.w3c.dom.NodeList;
    13 import org.xml.sax.SAXException;
    14 
    15 import com_1.huawei.factory.Factory;
    16 /**
    17  * 解析xml文件
    18  * @author Administrator
    19  *
    20  */
    21 public class XMLUtil 
    22 {
    23     public static int getType()
    24     {
    25         
    26         try {
    27             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    28             DocumentBuilder builder = factory.newDocumentBuilder(); 
    29             Document document = builder.parse(new File("thingtype.xml"));
    30             NodeList list = document.getElementsByTagName("type");
    31             org.w3c.dom.Node node = list.item(0).getFirstChild();
    32             String type = node.getNodeValue().trim();
    33             if(type.equals("Washer"))
    34             {
    35                 return Factory.Washer;
    36             }else if(type.equals("Icebox"))
    37             {
    38                 return Factory.Icebox;
    39             }
    40         } catch (ParserConfigurationException e) {
    41             // TODO Auto-generated catch block
    42             e.printStackTrace();
    43         }catch (SAXException e) {
    44             // TODO Auto-generated catch block
    45             e.printStackTrace();
    46         } catch (IOException e) {
    47             // TODO Auto-generated catch block
    48             e.printStackTrace();
    49         }
    50         return -1;
    51     }
    52 }
    View Code

     但是如果要增添产品类型,比如WasherB...这就要引入下面的工厂方法。

    2.工厂方法

    在简单工厂的设计思路上,我们可以用类似的方法,定义一个工厂接口类,让一种特定的产品由一个特定的Factory去创建。

    这样上面的Factory就变成了下面的3个类。

    【代码清单-5】

     1 package com_1.huawei.gcff;
     2 
     3 import com_1.huawei.product.IProduct;
     4 
     5 public interface IFactory 
     6 {
     7     public IProduct create();
     8 }
     9 =====================
    10 package com_1.huawei.gcff.Impl;
    11 
    12 import com_1.huawei.gcff.IFactory;
    13 import com_1.huawei.product.IProduct;
    14 import com_1.huawei.product.Impl.IceboxProduct;
    15 
    16 public class IceboxFactory implements IFactory {
    17 
    18     @Override
    19     public IProduct create() {
    20         return new IceboxProduct();
    21     }
    22 
    23 }
    24 =========================
    25 package com_1.huawei.gcff.Impl;
    26 
    27 import com_1.huawei.gcff.IFactory;
    28 import com_1.huawei.product.IProduct;
    29 import com_1.huawei.product.Impl.WasherProduct;
    30 
    31 public class WasherFactory implements IFactory {
    32 
    33     @Override
    34     public IProduct create() {
    35         return new WasherProduct();
    36     }
    37 
    38 }

    package com_1.huawei.gcff;

    import com_1.huawei.gcff.Impl.WasherFactory;
    import com_1.huawei.product.IProduct;

    public class Main {

    public static void main(String[] args)
    {
    IFactory factory = new WasherFactory();
    IProduct product = factory.create();
    product.work();
    }

    }

     这样就可以在客户端指定工厂去生产指定的产品了。不过如果需求改变时,还是你需要在客户端修改代码,所以升级为:

    【代码清单-6】

    1 <?xml version = "1.0" encoding = "utf-8">
    2 
    3 <factory>
    4     <type>WasherFactory</type>
    5 </faxtory>
     1       //客户端代码改为:    
     2                 IFactory factory;
     3         IProduct product;
     4         factory = (IFactory) XMLUtil.getType2();
     5         product = factory.create();
     6         product.work();          
     7 ====================================
     8 public static Object getType2()
     9     {
    10 
    11         try {
    12             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    13             DocumentBuilder builder = factory.newDocumentBuilder(); 
    14             Document document = builder.parse(new File("factory.xml"));
    15             NodeList list = document.getElementsByTagName("type");
    16             Node node = list.item(0).getFirstChild();
    17             String mFactory = "com_1.huawei.gcff.Impl." + node.getNodeValue();
    18             Class mclass = Class.forName(mFactory);
    19             Object mObject = mclass.newInstance();
    20             return mObject;
    21         } catch (Exception e) {
    22             e.printStackTrace();
    23         }
    24     
    25         return null;
    26         
    27     }          


    1.工厂模式的缺点:

          可以看出工厂方法的加入,使得对象的数量成倍增加。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。我们可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子互为)兄弟的使用简单工厂模式来实现。

    2.工厂方法和简单工厂的区别:

          从上面创建产品对象的代码可以看出:简单工厂是把创建产品的职能都放在一个工厂类里面,而工厂方法则把不同的产品放在实现了工厂接口的不同工厂类里面,这样就算其中一个工厂类出了问题,其他工厂类也能正常工作,互相不受影响。以后增加新产品,也只需要新增一个实现工厂接口工厂类,不用修改已经有的代码。

    3.抽象工厂

    1.引入

      工厂方法模式有一个问题就是:类的创建依赖工厂类,也就是说,当要扩展程序时,必须对工厂类进行修改,这就违背了闭包原则,所以引入了下面的抽象工厂模式。创建多个工厂类,这样当有新的功能时,直接增加新的工厂类就可以了,不需要修改之前的代码。

    2.概念

         在于创建一系列互相关联活相互依赖的对象。类似于在工厂方法的基础上引进了分类管理的概念。工厂方法用来创建一个产品,他没有分类的概念,而抽象工厂则用于创建一系列产品,所以产品分类成了抽象工厂的重点。

    3.举例

    【代码清单-7】---产品接口

     1 package com.huawei.lwx349758.product;
     2 
     3 /**
     4  * Washer(洗衣机)产品接口
     5  * @author Administrator
     6  *
     7  */
     8 public interface Washer {
     9 
    10 }
    11 
    12 =============================
    13 package com.huawei.lwx349758.product;
    14 
    15 /**
    16  * Icebox(冰箱)接口
    17  * @author Administrator
    18  *
    19  */
    20 public interface Icebox {
    21 
    22 }

    【代码清单-8】---产品实现类

     1 package com.huawei.lwx349758.product.Impl;
     2 
     3 import com.huawei.lwx349758.product.Washer;
     4 /**
     5  * Washer产品实现类A
     6  * @author Administrator
     7  *
     8  */
     9 public class WasherA implements Washer 
    10 {
    11     public WasherA()
    12     {
    13         System.out.println("WasherA is created!");
    14     }
    15 }
    16 ===================================
    17 package com.huawei.lwx349758.product.Impl;
    18 
    19 import com.huawei.lwx349758.product.Washer;
    20 /**
    21  * Washer产品实现类B
    22  * @author Administrator
    23  *
    24  */
    25 public class WasherB implements Washer 
    26 {
    27     public WasherB()
    28     {
    29         System.out.println("WasherB is created!");
    30     }
    31 }
    32 ===================================
    33 package com.huawei.lwx349758.product.Impl;
    34 
    35 import com.huawei.lwx349758.product.Icebox;
    36 /**
    37  * Icebox产品实现类A
    38  * @author Administrator
    39  *
    40  */
    41 public class IceboxA implements Icebox 
    42 {
    43     public IceboxA()
    44     {
    45         System.out.println("IceboxA is created!");
    46     }
    47 }
    48 
    49 ====================================
    50 package com.huawei.lwx349758.product.Impl;
    51 
    52 import com.huawei.lwx349758.product.Icebox;
    53 /**
    54  * Icebox产品实现类B
    55  * @author Administrator
    56  *
    57  */
    58 public class IceboxB implements Icebox 
    59 {
    60     public IceboxB()
    61     {
    62         System.out.println("IceboxA is created!");
    63     }
    64 }

    【代码清单-9】---抽象工厂类(接口)

     1 package com.huawei.lwx349758.factory;
     2 
     3 import com.huawei.lwx349758.product.Icebox;
     4 import com.huawei.lwx349758.product.Washer;
     5 /**
     6  * 抽象工厂类,生产Washer Icebox
     7  * @author Administrator
     8  *
     9  */
    10 public interface Factory 
    11 {
    12     Washer createWasher();
    13     Icebox createIcebox();
    14 }

    【代码清单-10】---工厂实现类(具体工厂类)

     1 package com.huawei.lwx349758.factory.Impl;
     2 
     3 import com.huawei.lwx349758.factory.Factory;
     4 import com.huawei.lwx349758.product.Icebox;
     5 import com.huawei.lwx349758.product.Washer;
     6 import com.huawei.lwx349758.product.Impl.IceboxA;
     7 import com.huawei.lwx349758.product.Impl.WasherA;
     8 
     9 public class FactoryA implements Factory {
    10 
    11     @Override
    12     public Washer createWasher() {
    13         return new WasherA();
    14     }
    15 
    16     @Override
    17     public Icebox createIcebox() {
    18         return new IceboxA();
    19     }
    20 }
    package com.huawei.lwx349758.factory.Impl;
    
    import com.huawei.lwx349758.factory.Factory;
    import com.huawei.lwx349758.product.Icebox;
    import com.huawei.lwx349758.product.Washer;
    import com.huawei.lwx349758.product.Impl.IceboxB;
    import com.huawei.lwx349758.product.Impl.WasherB;
    
    public class FactoryB implements Factory {
    
        @Override
        public Washer createWasher() {
        return new WasherB();
        }
    
        @Override
        public Icebox createIcebox() {
            return new IceboxB();
        }
    }

    【代码清单-11】---测试类

     1 package com.huawei.lwx349758.test;
     2 
     3 import com.huawei.lwx349758.factory.Factory;
     4 import com.huawei.lwx349758.factory.Impl.FactoryA;
     5 import com.huawei.lwx349758.product.Icebox;
     6 import com.huawei.lwx349758.product.Washer;
     7 /**
     8  * 测试类
     9  * @author Administrator
    10  *
    11  */
    12 public class Main 
    13 {
    14     public static void main(String[] args)
    15     {
    16         Factory factory = new FactoryA();
    17         Washer washer = factory.createWasher();
    18         Icebox icebox = factory.createIcebox();
    19     }
    20 }

    总结:

      

        1.简单工厂:由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。

        2.工厂方法:有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。

        3.抽象工厂:提供一个创建一系列相关活相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。当抽象工厂模式中只有一个产品族时,就退化为工厂方法了。

      优点:

        1-良好的封装性,代码结构清晰。 调用一个产品对象,只要知道这个产品对象的类名就可以了,不需要知道这个创建对象的过程,减少了代码间的耦合度。

        2-扩展性很好。 在增加产品类的时候,只需要修改一下具体的工厂类或者增加一个工厂类,就可以实现业务扩展。

        3-屏蔽产品类。 产品类的实现如何变化,调用者都不需要关心,它只关心产品的调用接口。只要接口保持不变,系统中的上层模块就不会发生变化,因为产品类的实例化工作是有工厂类负责,一个产品对象具体由哪一个产品生成是由工厂决定的。

    使用案例:

      数据库开发时,如果使用JDBC连接数据库,数据库从Mysql切换到Oracle的时候,需要改动的地方就是切换一下驱动的名称,其他都不需要修改,这就是工厂方法模式的一个直接案例。

    4.应用

    4.1 --替代单例模式:

    单例模式的核心是要求在内存中只有一个对象,通过工厂方法模式也可以在内存中只生产一个对象。

    【代码清单-12】

     1 package com.huawei.singleton;
     2 /**
     3  * 通过工厂方法模式来实现单例模式,
     4  * @author Administrator
     5  *
     6  */
     7 public class Singleton 
     8 {
     9     //不允许通过new产生一个对象
    10     private Singleton()
    11     {
    12         
    13     }
    14     public void doSomething()
    15     {
    16         //业务处理
    17     } 
    18 
    19 }

    【代码清单-13】

     1 package com.huawei.singleton;
     2 
     3 import java.lang.reflect.Constructor;
     4 
     5 /**
     6  * 通过反射方式创建一个单例对象
     7  * @author Administrator
     8  *
     9  */
    10 public class SingletonFactory 
    11 {
    12     private static Singleton singleton;
    13     
    14     static 
    15     {
    16         try
    17         {
    18             Class cl = Class.forName(Singleton.class.getName());
    19             //获得无参构造
    20             Constructor constructor = cl.getDeclaredConstructor();
    21             
    22             //设置无参构造是可访问的
    23             constructor.setAccessible(true);
    24             
    25             //产生一个实例对象
    26             singleton = (Singleton) constructor.newInstance();
    27             
    28         }catch(Exception e)
    29         {
    30             //异常处理
    31         }
    32     }
    33     
    34     public static Singleton getSingleton()
    35     {
    36         return singleton;
    37         
    38     }
    39 
    40 }

    通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证了内存中的对象唯一。
    以上通过工厂方法模式创建了一个单例对象,该框架可以继续扩展,在一个项目中可以产生一个单例构造器,所有需要产生
    单例的类都遵循一定的规则(构造方法私有化),然后通过扩展改框架,只要输入一个类型就可以获得唯一的实例。

    4.2--延迟初始化:

    一个对象被消费完毕后,并不立刻释放,工厂类保持其初始化状态,等待再次被使用。

    【代码清单-14】

     1 package com.huawei.lazy_init;
     2 
     3 /**
     4  * 抽象产品类
     5  * @author Administrator
     6  *
     7  */
     8 public abstract class Product 
     9 {
    10     //产品类的公共方法
    11     public void method1()
    12     {
    13         //业务处理
    14     }
    15     
    16     //抽象方法
    17     public abstract void method2();
    18     
    19 }

    【代码清单-15】:具体产品类

     1 package com.huawei.lazy_init;
     2 
     3 public class ProductAImpl extends Product {
     4 
     5     @Override
     6     public void method2() {
     7         //业务处理
     8         System.out.println("productA is created!");
     9     }
    10 
    11 }
    12 
    13 ====================================
    14 package com.huawei.lazy_init;
    15 
    16 public class ProductBImpl extends Product {
    17 
    18     @Override
    19     public void method2() {
    20         //业务处理
    21         System.out.println("productA is created!");
    22     }
    23 
    24 }

    【代码清单-16】---工厂类

     1 package com.huawei.lazy_init;
     2 
     3 import java.util.HashMap;
     4 import java.util.Map;
     5 
     6 /**
     7  * 该类负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次
     8  * 被重用的对象保留。
     9  * @author Administrator
    10  *
    11  */
    12 public class ProductFactory 
    13 {
    14     public static final Map<String,Product> prMap = new HashMap();
    15     
    16     public static synchronized Product createProduct(String type) throws Exception
    17     {
    18         Product product  = null;
    19         //如果Map中已经有这个对象
    20         if(prMap.containsKey(type))
    21         {
    22             product = prMap.get(type);
    23         }else
    24         {
    25             if(type.equals("productA"))
    26             {
    27                 product = new ProductAImpl();
    28             }else
    29             {
    30                 product = new ProductBImpl();
    31             }
    32             
    33             //同时把对象放到缓存容器中
    34             prMap.put(type, product);
    35         }
    36         return product;
    37         
    38     }
    39 
    40 }

       延迟加载框架是可以扩展的,例如限制某一个产品类的最大实例化数量,可以通过判断Map中已经有的数量来实现,例如:JDBC连接数据库,都会要求设置一个MaxConnection最大连接数量,改数量就是内存中最大的实例化的数量。

    延迟加载还可以使用在一个对象初始化比较复杂的情况,例如硬件访问,设计多方面的交互,则可以通过延迟加载降低对象的产生和销毁带来的复杂性。

  • 相关阅读:
    【Codeforces Round #645 (Div. 2) F】 Tasty Cookie
    【Codeforces Round #645 (Div. 2) E】 Are You Fired?
    【Educational Codeforces Round 88 (Rated for Div. 2) C】 Mixing Water
    20191114-4 Beta发布用户使用报告
    20191114-3 Beta阶段贡献分配
    beta发布
    20191107-1 每周例行报告
    beta 2/2 阶段中间产物提交入口
    beta week 2/2 Scrum立会报告+燃尽图 07
    beta week 2/2 Scrum立会报告+燃尽图 06
  • 原文地址:https://www.cnblogs.com/lthIU/p/5816292.html
Copyright © 2011-2022 走看看