我眼中的所谓工厂模式


  说起软件架构中的设计模式,大多数人肯定第一句就是,工厂模式。准确讲经典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

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

我的目录结构:

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