• 我眼中的所谓工厂模式


      说起软件架构中的设计模式,大多数人肯定第一句就是,工厂模式。准确讲经典24个设计模式中没有工厂模式,而是简单工厂模式,工厂方法模式和抽象工厂模式这三种,三种互相有本质区别,各自为解决不同领域的问题,而形成的一套代码体系。我记得我毕业后第一份工作跳槽的时候,遇到的一次面试就有设计模式题目,不过当时太嫩,大败而归,也是那时知道有设计模式这个东东,因为这个学校是不教的。从此也就对设计模式就开始去研究啦,于是就有了这个系列的文章。

      面向对象编程的三大基本特性:封装、继承、多态。简简单单的概念,其实每一个特性都能拓展得很深很深。这里我们只看下多态,多态就是利用了继承的概念,抽象依赖实现,可以看一个大家在学习编程语言启蒙的时候都会遇到的一个实例,首先创建一个基类,用接口也可以,里面放一个方法。

      创建SampleBase类,代码如下:

    1 public class SampleBase {
    2 
    3     public String getName()
    4     {
    5         return this.getClass().toString();
    6     }
    7 }

      再创建一个集成该类的衍生类,代码如下:

    1 public class SampleImpl extends SampleBase {
    2     @Override
    3     public String getName()
    4     {
    5         return this.getClass().toString();
    6     }
    7 }

      在测试类中进行测试,测试代码如下:

    public static void main(String[] args) {
            
        SampleBase base = new SampleImpl();
        System.out.println(base.getName());
    }

      运行结果:

    class com.aspnetdream.test.SampleImpl

      

    其实对于这个情况,我脑海中一直记得一句话,基类指针指向子类应用,我的学习生涯接触这个最开始是在C++的语言中学习的,记住这句话,应对继承和多态我觉得是万能的法宝。基于编程语言这么一个优秀的特质,再想想在工作中遇到的很多应用场景,我曾经开发一个餐饮的ERP系统,在处理订单的时候,很多不同的单据,这些单据有相通之处,也会有各自不同点,比如配料订单和退货订单,这两种订单,相同的地方是对业务单据的物料拆分处理是一样的,但最后订单存储的表是不一样的,也就是说,两个单据我在拆分处理需要一样的代码,但在这一步完成之后,又各自走向不同的代码,好,套用简单工厂模式来解决。

    创建单据基类IOrderInfo,我在这里把他定义成了一个接口,实际应用中可以定义为抽象类,甚至就是一个类。
    创建IOrderInfo 接口,代码如下:

    1 //简单工厂模式接口定义层
    2 public interface IOrderInfo {
    3     public String getOrderInfo();
    4 }

    窗建完接口,我们分别创建配料单和退货单的衍生类。
    配料单类OrderMakeCustom,代码如下:

    1 //简单工程接口实现层   消耗订单操作类
    2 public class OrderMakeCustom implements IOrderInfo {
    3     
    4     public String getOrderInfo()
    5     {
    6         return this.getClass().toString();
    7     }
    8 
    9 }

    退货单类 OrderReturnCustom,代码如下:

    //简单工厂接口实现层  退货订单操作类
    public class OrderReturnCustom implements IOrderInfo {
        
        public String getOrderInfo()
        {
            return this.getClass().toString();
        }
    
    }

    两个衍生类都实现IOrderInfo接口,写到这里稍微有经验点的同学就可以看出来,在实际代码调用中就可以

    IOrderInfo order = new OrderMakeCustom();

    不错可以这么做,这是比较粗暴的做法,但现实在项目开发的时候,这样的代码却比比皆是。
    我们这里使用简单工厂来实现,把new的任务交给工厂来完成,将new的任务交给工厂而不是在具体的业务流程代码中去完成,我个人任务有一下几点好处:
    1、代码的整洁度,易于维护,如果整篇代码掺杂很多的new,我想读代码的人是相当痛苦的,这种流线型的代码方式,带来的恶果就是,业务一复杂,大家就开始走迷宫。
    2、将代码放入工厂中,可以为后续代码升级做准备,如果,我将对应的接口实现的所有子类对应的放入一个xml文件中,用反射来实例化一个具体的基类的,这是不是就是IOC的原型啦。

    继续代码,创建一个Factory,在这个类中就是根据不同业务流程,创建不同订单实例交给调用方,实现代码:

     1 public class Factory {
     2 
     3     //简单工厂模式   创建实现操作类工厂
     4     public static IOrderInfo createOrderInfo(EServiceType createType)
     5     {
     6         switch(createType)
     7         {
     8             case Make:
     9                 return new OrderMakeCustom();
    10             case Return:
    11                 return new OrderReturnCustom();
    12             default:
    13                 return null;
    14             
    15         }
    16     }
    17 }

    为了便于创建我这里放了一个枚举类型,简单工厂的工厂类根据传入的不同枚举类型,闯将不同的实例类。

    OK,到这里一个简单工厂模式就搭建完成了,我们来测试运行,测试代码如下:

     1 public static void main(String[] args) {
     2         
     3         SampleBase base = new SampleImpl();
     4         System.out.println(base.getName());
     5         
     6         //根据不同的类型实例化不同的订单操作类
     7         //------简单工厂模式(静态工厂模式)
     8         //退货订单操作类
     9         IOrderInfo order = Factory.createOrderInfo(EServiceType.Return);
    10         //获取订单后,处理订单
    11         getOrderInfo(order);
    12         
    13         order = Factory.createOrderInfo(EServiceType.Make);
    14         getOrderInfo(order);
    15 
    16     }
    17 
    18     //实际项目中对订单模型进行操作,而不用关注是那种类型订单,在业务实现层去关注返回什么样的订单
    19     private static void getOrderInfo(IOrderInfo order)
    20     {
    21         System.out.println(order.getOrderInfo());
    22     }

    运行结果:

    class com.aspnetdream.bussinessImpl.OrderReturnCustom
    class com.aspnetdream.bussinessImpl.OrderMakeCustom

    使用抽象层IOrderInfo对象作为句柄,具体实现看不同的业务要求去工厂请求相应的实现层。再回到我最开始描述的场景,对两种单据中相同的业务我们可以放入抽象层,甚至在抽象层我们可以定义不同的业务代码相同的调用方法,这样你的代码档次就会被拉高很多很多!简单工厂模式也叫静态工厂模式,它创建实现层的方式较为呆板,只是把new的代码都放入了工厂中,但你依然需要使用选择模式,也就是告诉工厂你是创建A,还是创建B,这就是我代码中加入枚举的原因了,这其实在代码本质上和new 一个具体实现没有太大的区别。所以,由此就产生了工厂方法模式。对于这个模式的应用在我的印象中是基于不同的产品操作而想到的,在开发一个POS后端应用时,都是产品信息,但是交给POS端和交给手机移动端的信息是不一样的,POS端也许信息比较全面,交给移动端信息比较少,因为在我这个产品中,移动端仅限于看报表。这里我们尝试着用工厂方法模式来解决这个问题。
    首先创建产品的基类,iproduct接口,代码如下:

    1 //工厂方法模式---抽象角色接口
    2 public interface iproduct {
    3 
    4     public String getPrdName();
    5 }

    再分别创建Pos和移动端不同的产品实现类,PosProduct和MobileProduct类,代码实现:

    //工厂方法模式 具体角色类
    public class MobileProduct implements iproduct {
     
        //实现抽象角色接口
        @Override
        public String getPrdName()
        {
            return this.getClass().toString();
        }
    }
    
    //工厂方法模式---具体角色类
    public class PosProduct implements iproduct {
    
        @Override
        public String getPrdName()
        {
            return this.getClass().toString();
        }
    }

    基于应用角色我们就创建完了,使用工厂模式,肯定需要创建工厂嘛,创建产品抽象工厂IProductFactory,代码如下:

    //工厂方法模式 ---- 抽象工厂
    public abstract class IProductFactory   {
    
        public abstract iproduct getProduct();
    }

    抽象工厂负责返回抽象角色,抽象对抽象。

    有抽象就有实现,我们需要操作POS和Mobile端不同的产品信息,如是,针对抽象我们有2个不同业务实现要求,那么实现抽象来创建这2个实现工厂PosFactory和MobileFactory,代码如下:

     1 //工厂方法模式 ----具体角色工厂 pos产品工厂
     2 public class PosFactory extends IProductFactory {
     3     
     4     public iproduct getProduct()
     5     {
     6         return new PosProduct();
     7     }
     8 
     9 }
    10 
    11 
    12 //工厂方法模式---工厂方法模式 具体产品共  移动类产品工厂类
    13 public class MobileFactory extends IProductFactory  {
    14 
    15     public iproduct getProduct()
    16     {
    17         return new MobileProduct();
    18     }
    19     
    20 }

    创建完这一步其实基本的工厂方法模式也就完成了,接下来我们测试,测试代码如下:

     1 public static void main(String[] args) {
     2     
     3         //------工厂方法模式
     4         IProductFactory ifactory = new MobileFactory();//移动端类产品工厂     返回移动端产品
     5         iproduct prd= ifactory.getProduct();
     6         System.out.println(prd.getPrdName());
     7         
     8         ifactory = new PosFactory();//返回pos类产品工厂  返回pos类产品
     9         prd = ifactory.getProduct();
    10         System.out.println(prd.getPrdName());
    11         
    12     }
    13 
    14     //实际项目中对订单模型进行操作,而不用关注是那种类型订单,在业务实现层去关注返回什么样的订单
    15     private static void getOrderInfo(IOrderInfo order)
    16     {
    17         System.out.println(order.getOrderInfo());
    18     }

    运行结果:

    class com.aspnetdream.driver.productImpl.MobileProduct
    class com.aspnetdream.driver.productImpl.PosProduct

    工厂方法模式,在我的记忆中就是抽象对抽象,实现对实现,实现多少依据具体的业务需求来完成,可以无限的实现下去,当然,你就需要无限的工厂来帮你实现。

      简单工厂模式和工厂方法模式比较分析:
      在我看来,简单工厂模式就是工厂模式中的屌丝,工厂方法模式就是高富帅,屌丝的最大特点就是,“躲进小楼成一统,管他春夏与秋冬”,外部怎么弄的我不管,你给让我创建什么类型,我创建好给你就行,同时你也必须将需要创建的类型写入工厂中,也就是new一个实例。
    然而,高富帅的工厂方法模式,有娘生也有娘养,首先在工厂的抽象层,你就必须要对应你这个工厂操作的对应的角色类型,也就是说你一出生属于哪个帮派的已经基本确定了,所以一般都会对角色进行抽象,在工厂抽象层操作的类型肯定也是抽象,“抽象对抽象”---又说了一遍。
    基于上面的表述,简单工厂模式路子很野,你想创建啥我就创建啥,随便来,这和直线的代码比起来其实差别不大。工厂方法模式一脉相承,代码比简单工厂模式要复杂,但是无形中确规范了不少,依据这样的模式,代码分层其实就特别顺溜。

      好了,对应于工厂模式我刚才说了,你想实现多少就实现多少,这是好处,也是坏处,代码总归是有限的,需求泛滥,也总过就人死灯灭的时候,正是由于实现终归是有限的,所以在实现的时候,如果能不要这么多实现工厂是不是代码就稍微简洁点呢。由此,我们就产生了抽象工厂模式,将原先在工厂方法模式中,实现工厂的代码移到一个统一的抽象层中,再来针对抽象进行实现,看看代码怎么实现。
    创建抽象工厂模式的抽象工厂类AbstractFactory,代码如下:

    1 //抽象工厂模式--抽象工厂
    2 public abstract class AbstractFactory {
    3 
    4     public abstract iproduct createPosProdct(); 
    5     
    6     public abstract iproduct createMobileProduct();
    7 }

    针对抽象类的实现层DefaultFactroy,代码如下:

     1 //抽象工厂模式的工厂实现层
     2 public class DefaultFactory extends AbstractFactory{
     3 
     4     public iproduct createPosProdct()
     5     {
     6         return new PosProduct();
     7     }
     8     
     9     public  iproduct createMobileProduct()
    10     {
    11         return new MobileProduct();
    12     }
    13 }

    好,抽象工厂就塑造好了,下面测试,测试代码如下:

     1     public static void main(String[] args) {
     2     
     3         ///---抽象工厂模式
     4         AbstractFactory dfactory = new DefaultFactory();
     5         prd = dfactory.createMobileProduct();
     6         System.out.println(prd.getPrdName());
     7         
     8         prd = dfactory.createPosProdct();
     9         System.out.println(prd.getPrdName());
    10     }

    运行结果:

    class com.aspnetdream.driver.productImpl.MobileProduct
    class com.aspnetdream.driver.productImpl.PosProduct

    抽象工厂和工厂方法模式我觉得没有谁好谁坏,不同的应用场景使用不同的模式,在我们现实的工作场景中,处理一个问题,往往是几种模式一起用,某个应用模式的内部代码或许有其他的模式来实现。二十四种设计模式,其实每一个割裂开来看,每个都很简单,上面的代码相信每一个人都能看的懂,但,如何去用就是基本功的问题,代码灵活地拓展和应用也同样是对自身代码能力的一个要求。
    好了,就此搁笔,算是抛砖引玉,欢迎拍砖指教!

    我的目录结构:

    最后贴上我的源代码:http://files.cnblogs.com/aspnetdream/factory.rar

       
  • 相关阅读:
    PPT_标题
    产品需求撰写-架构图
    测试
    SparkLauncher 1.6 版本bug
    Github fork同步
    Maven 配置远程仓库
    Sqoop2中传入配置文件中url之【坑】
    Spark性能优化-coalesce(n)
    面试算法题目
    Git 基本概念及常用命令
  • 原文地址:https://www.cnblogs.com/aspnetdream/p/3841597.html
Copyright © 2020-2023  润新知