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