设计模式之禅【工厂模式】

真刀实枪之工厂方法模式

  • 从女娲造人开始说起
    • 东汉《风俗通》记录了这样一则神话故事:“开天辟地,未有人民,女娲抟黄土做人。”开天辟地之初,大地上并没有生物,只有苍茫大地,纯粹而洁净的自然环境,寂静而又寂寞,于是女娲决定创造一个新物种,即人类来增加世界的繁荣。
    • 别忘了女娲是神仙,没有办不到的事情,造人的过程是这样的:首先,女娲采集黄土捏成人的形状,然后放到八卦炉中烧制,最后放到大地上生长,工艺过程没有错,但是意外会随时发生:
      • 第一次烤泥人,感觉应该烤熟了,往地上一放,哇,没烤熟!于是一个白人诞生了!
      • 第二次烤泥人,上一次没烤熟,这次多烤了一会儿,放到世上一看,嘿,熟过头了,黑人诞生了!
      • 第三次烤泥人,一边烧制,一边查看,直到表皮微黄,嘿,刚刚好,于是黄色人种出现了!
  • 分析
    • 造人中涉及的对象
      • 女娲----场景
      • 八卦炉----工厂
      • 三种肤色不同的人----产品
    • 类图
    • 代码
      //Human
      public interface Human{
          //肤色
          public void getColor(); 
          //说话
          public void talk(): 
      }
      

       

      //BlackHuman
      class BlackHuman implements Human{
          public void getColor(){
              System.out.println("黑色人种的皮肤颜色是黑色的!");
          }
          public void talk(){
              System.out.println("黑人会说话,一般人听不懂!");
          }
      }
      

       

      //YelloHuman
      class YelloHuman implements Human{
          public void getColor(){
              System.out.println("黄色人种的皮肤颜色是黄色的!");
          }
          public void talk(){
              System.out.println("黄人会说话,一般是双字节的!");
          }
      }
      

       

      //WhiteHuman
      class WhiteHuman implements Human{
          public void getColor(){
              System.out.println("白色人种的皮肤颜色是白色的!");
          }
          public void talk(){
              System.out.println("白人会说话,一般是单字节的!");
          }
      }
      
    • 所有人种定义完毕,下一步就是定义八卦炉,女娲给八卦炉下达什么生产命令呢? 应该是“给我生产一个黑色人种”,而不是“给我生产一个会跑会走,会说,皮肤是黑色的人种。”,因为这样的命令增加了交流的成本,作为一个生产的管理者,只要知道生产什么就可以了,而不需要具体信息。这就解释createHuman方法的参数为什么是Class了。
      public abstract class AbstractHumanFactory{
          public abstract <T extends Human> T createHuman(Class<T> c);
      }
      //注意:这里的泛型表示为两层含义
      //1. 必须是Class类型
      //2. 必须是Human的实现类
      
    • 八卦炉
      public class HumanFactory extends AbstracterHumanFactory{
          public <T extends Human> T createHuman(Class<T> c){
              //定义一个生产的人种
              Human human = null;
              try{
                  //生产一个人种
                  huam=(T)Class.forName(c.getName()).newInstance();
              }catch(Exception e){
                  System.out.println("人种生产错误!");
              }
              return (T)human;
          }
      }
      
    • 人种有了,八卦炉也有了,剩下的工作就是女娲采集黄土,然后命令八卦炉进行生产了。下面是女娲类
      public class NvWa{
          public static void main(Stringa[] args){
              //声明阴阳八卦炉
              AbstracterHumanFactory YinYangLu=new HumanFactory();
              //造白人
              Human whiteHuman = YinYangLu.createHuman(WhiteHuman.class);
              whiteHuamn.getColor();
              whiteHuman.talk();
              //造黑人
              Human blackHuman = YinYangLu.createHuman(BlackHuman.class);
              blackHuman.getColor();
              blackHuman.talk();
              //造黄人
              Human yellowHuman = YinYangLu.createHuman(YellowHuman.class);
              yellowHuman.getColor();
              yellowHuman.talk();
          }
      }
      
  • 工厂方法的定义
    • Define an interface for creating an object,but let subclass decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类)
  • 工厂方法的通用类图

    • 在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事务最抽象的定义,Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的,工厂方法模式的变种较多。

    • 通用源码

      //抽象产品类
      public abstract class Product{
          //产品类的公共方法
          public void method1(){
              //业务逻辑处理
          }
          //抽象方法
          public abstract void method2();
      }
      

       

      //具体产品类
      public class ConcreteProduct1 extends Product{
          public void method2(){
              //业务逻辑处理
          }
      }
      
      public class ConcreteProduct2 extends Product{
          public void method2(){
              //业务逻辑处理
          }
      }
      
      ...
      

       

      //抽象工厂类
      public abstract class Creator{
          //创建一个产品对象
          public abstract <T extends Product> T createProduct(Class<T> c);
      }
      

       

      //具体工厂类
      public class ConcreteCreator extends Creator{
          public <T extends Product> T createProduct(Class<T> c){
              Product product=null;
              try{
                  product = (Product)Class.forName(c.getName).newInstance();
              }catch(Exception e){
                  //异常处理
              }
              return product;
          }   
      }
      

       

      //场景类
      public class Client {
          public class void main(String[] args){
              Creator creator=new ConcreteCreator();
              Product product=creator.createProduct(ConcreteProduct1.class);
              //继续业务处理
          }
      }
      
  • 工厂方法模式的应用
    • 优点
      1. 良好的封装性,代码结构清晰
      2. 扩展性优秀
      3. 屏蔽产品类
      4. 解耦框架
      
    • 使用场景
      1. 所有在需要生成对象的地方
      2. 灵活的,可扩展的框架(POP3,IMAP,HTTP协议)
      3. 异构项目中--WebService与一个非java的项目交互
      4. 测试驱动开发的框架--JMock和EasyMock--测试A类,就需要把相关联的B类也创建出来,我们可以使用工厂方法把B类虚拟出来,比避免A类与B类的耦合
      
  • 工厂方法的扩展

    • 1.缩小为简单工厂模式

      public class HumanFactory {
          public static <T extends Human> T createHuman(Class<T> c){
              //定义一个生产的人种
              Human human = null;
              try{
                  //生产一个人种
                  huam=(T)Class.forName(c.getName()).newInstance();
              }catch(Exception e){
                  System.out.println("人种生产错误!");
              }
              return (T)human;
          }
      }
      
    • 2.升级为多个工厂类

      • 问题:当项目复杂时,初始化对象是一件十分耗精力的事,而且所有的产品都放到一个工厂类中区创建会使代码结构不清晰。
      • 解决:每个产品对应一个创造者
    • 3.代替单例模式

       

      public class Singleton{
          private Singleton(){
      
          }
      
          public void doSomething(){
      
          }   
      }
      

       

      public class SingletonFactory{
          private static Singleton singleton;
          static{
              try{
                  Class cl=Class.forName(Singleton.class.getName());
                  //获得无参构造
                  Constructor constructor = cl.getDeclaredConstructor();
                  //设置无参构造是可访问的
                  constructor.setAccessible(true);
                  singleton=(Singleton)constructor.newInstance();
              }catch(Exception e){
                  //异常处理
              }
      
              public static Singleton getSingleton(){
                  return singleton;
              }
      
          }
      }
      
    • 4.延迟初始化

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

      • 延迟加载框架可以限制其实例的个数;可以通过延迟记载降低对象的产生和销毁带来的复杂性。

      • 代码

        public class ProductFactory {
            private static final Map<String,Product> peMap=new HashMap();
            public static synchronized Product createProduct (String type) throws Exception{
                Product product=null;
                //如果Map中已经有这个对象
                    if(prMap.containsKey(type)){
                        product=prMap.get(type);                
                    }else{
                        if(type.equals("Product1")){
                            product=new ConvreteProduct1();
                        }else{
                            product=new ConvreteProduct2();
                        }
                        //同时把这个对象放入Map
                        prMap.put(type,product);
                    }
                    return product;
            }
        }
        

最佳实践

  • 工厂方法模式可以与其他模式混合使用(模板方法模式,单例模式,原型模式等),变化出无穷的优秀设计,这也正是软件设计和开发的乐趣所在。

声明

  • 摘自秦小波《设计模式之禅》第2版;
  • 仅供学习,严禁商业用途;
  • 代码手写,没有经编译器编译,有个别错误,自行根据上下文改正。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乘风御浪云帆之上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值