工厂模式(工厂方法模式,抽象工厂模式) Java
2016-10-07 22:10
246 查看
工厂模式分为3类:
* 简单工厂模式 Simple Factory
* 工厂方法模式 Factory Method
* 抽象工厂模式 Abstract Factory
thinking in Java 中工厂方法模式的例子:
知乎上的一个问题
为什么在最后Factories中的静态方法serviceConsumer中,不直接用Service呢?
省掉工厂方法也可以实现多态,直接用Service实例作为参数,向上转型成Service接口,去耦合。
如:
工厂模式真正的精髓并不在于多态去耦合,而是在于“工厂模式”,在Design Pattern中,工厂方法模式是和“虚拟工厂模式”,“生成器模式”,“单例模式”在一起,同属于创建型模式。
创建型模型最大的共同特征就是,把类型的定义过程和实例化过程分离开。即在类自身构造器之外,附加一个经常被误认为没什么用的工厂类,如Thinking in Java里的ServiceFactory:
实际中,工厂类作用巨大。
当一个类有很多不同的变种时,就需要工厂类来隔离实例化过程。
这确实有用,但实际生活中,这并不是逼迫我们决定分离出工厂类的最主要的原因。真正的原因是:相比于继承,我们更优先使用组合。
真正迫使我们决定分离出工厂类的原因,是实体类使用了组合,而且组件又非常的多,如果Service是由很多组件构成的庞大的家伙,比如一个迷宫:假设一个迷宫是由三种不同的小单元组成,为房间,墙,门。
问题是,要生成一个迷宫,要成百上千个门,墙,房组件。而且还要遵循一定的逻辑规则,因此构造器就会很复杂,迷宫有无数种,如果给每个迷宫都创建一个实体类,再给出具体构造流程,那就累死了。
这种情况下,合理的办法是写一个随机迷宫生成器,能根据具体要求不同生成无数种迷宫,这就是创建型模式的意义所在。无论是“虚拟工厂模式”,还是“生成器”模式,工厂模式,都是在具体生成的策略上做文章,但一个大前提不变:具体产出的产品Product都是由很多小的组件组合而成,而且组装的时候灵活度非常高,甚至是runtime用户定制的,或者是随机的。这就导致组合出来的产品Product单独作为一个类存在的意义很小,反而是构造器的作用被放大。索性把构造过程独立出来成为一个方法,把这个方法用到的参数作为成员字段一起封装起来,再来个构造器只负责初始化这些参数,这种累就是“工厂类”。
原来的那个迷宫类,本身的构造器不承担任何功能了。
所以再回到工厂方法,书里说的所有的东西,都是基于这个前提,也就是我们说好了啊,迷宫这东西的类文件里是没有具体构造方法的,都是要用工厂类MazeFactory来生成的。至于后来,我们加入了方迷宫,和方迷宫生成器。又有了圆迷宫和圆迷宫生成器。有了一堆生成器复杂了之后,又想到用多态来解耦,这都是后话,是在确定了使用工厂类的前提下,利用多态解耦的优化方案。所以才有了最初的这段代码:
再来,如果程序为:
Service从哪来呢?当然是从其他地方new了传进来:
Service为什么是一个接口?
如果有一天,领导要求将数据库从MYSQL换成ACCESS,因为只有一个ServiceA所以只能把所有用到ServiceA的地方都替换成ServiceB:
如果是个小项目,没关系,如果是一个庞大的团队项目,,,根据设计原则中的依赖倒置原则,设计一个接口,然后扩展一个类,这样就能灵活应对boss的需求了:
又过了几天,用到的数据库越来越多,if else也越来越多,我么你决定隔离这些变化,将Service实例化过程放在一个地方,简单工厂诞生:
这样,即时数据库不断变化,我也只需要扩展一个新的类,并且修改工厂这个类就行了。但是ServiceFactory是一个接口,这就是抽象工厂。
另一位回答者接下去回答:
如果只有一个工厂,但同一个Service在不同环境下会有不同的实现,如果环境很多,这个ServiceFactory会变成什么样呢:
那么if else越来越多,每次修改都需要重新编译整个Factory,而且,一般情况下,实现这些Service的往往是不同组的人,每个组都往这个类里加参数/状态,合并代码时会出现无数个冲突,还可能互相用错参数从而影响别人代码,抽象工厂大家的实现分开到不同的类里面,让大家各改各的文件,互不影响:
这些工厂不需要使用时才new,只要事先new好,存入一个Map,根据具体环境随时调用:
然后调用serviceConsumer的那个地方只需要:
以后如果有新的数据库,重新写一个ServiceFactory的实现类,编译一下放一块就行,在factoryMap里放入实例就好了,别的代码完全不需要动,甚至不需要重新编译。
可不可以不要Factory,直接用ServiceMap呢?
基本是不行的,首先Service的初始化一般需要很多参数,不可能在程序刚载入时就存在,另外Service的实例会new很多个,也不满足一一对应的关系。但Factory的构造函数一般不需要参数。相比于Service,Factory内部保存的仅仅是一些参数,占用内存小得多,长时间驻留在内存中也没有太大的损害。
* 简单工厂模式 Simple Factory
* 工厂方法模式 Factory Method
* 抽象工厂模式 Abstract Factory
thinking in Java 中工厂方法模式的例子:
interface Service{ void method1(); void method2(); } interface ServiceFactory{ Service getService(); } class Implementation1 implements Service{ Implementation1() {} public void method1(){System.out.println("implementation1 method1");}; public void method2(){System.out.println("implementation1 method2");}; } class Implementation1Factory implements ServiceFactory{ public Service getService(){ return new Implementation1(); } } class Implementation2 implements Service{ Implementation2(){}; public void method1(){System.out.println("implementation2 method1");}; public void method2(){System.out.println("implementation2 method2");}; } class Implememntation2Factory implements ServiceFactory{ public Service getService(){ return new Implementation2(); } } public class Factories { public static void serviceConsumer(ServiceFactory fact){ Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args){ serviceConsumer(new Implementation1Factory()); serviceConsumer(new Implememntation2Factory()); } }
知乎上的一个问题
为什么在最后Factories中的静态方法serviceConsumer中,不直接用Service呢?
省掉工厂方法也可以实现多态,直接用Service实例作为参数,向上转型成Service接口,去耦合。
如:
interface Service{ void method1(); void method2(); } public static void serviceConsumer(Service s){ s.method1(); s.method2(); }
工厂模式真正的精髓并不在于多态去耦合,而是在于“工厂模式”,在Design Pattern中,工厂方法模式是和“虚拟工厂模式”,“生成器模式”,“单例模式”在一起,同属于创建型模式。
创建型模型最大的共同特征就是,把类型的定义过程和实例化过程分离开。即在类自身构造器之外,附加一个经常被误认为没什么用的工厂类,如Thinking in Java里的ServiceFactory:
class ServiceA{ void method1(){}; void method2(){}; // 构造器 ServiceA(){}; } class ServiceFactory{ Service getService(){}; }
实际中,工厂类作用巨大。
当一个类有很多不同的变种时,就需要工厂类来隔离实例化过程。
class ServiceFactory{ Service getService(){ if(a){ ServiceA sA = new ServiceA(); }else(b){ ServiceB sB = new ServiceB(); }else(c){ ServiceC sC = new ServiceC(); } } }
这确实有用,但实际生活中,这并不是逼迫我们决定分离出工厂类的最主要的原因。真正的原因是:相比于继承,我们更优先使用组合。
真正迫使我们决定分离出工厂类的原因,是实体类使用了组合,而且组件又非常的多,如果Service是由很多组件构成的庞大的家伙,比如一个迷宫:假设一个迷宫是由三种不同的小单元组成,为房间,墙,门。
class Maze{ Room r1; Room r2; ... Wall w1; Wall w2; ... Door d1; Door d2; ... //构造器 Maze(){ //按顺序摆放成员字段组件 ... } }
问题是,要生成一个迷宫,要成百上千个门,墙,房组件。而且还要遵循一定的逻辑规则,因此构造器就会很复杂,迷宫有无数种,如果给每个迷宫都创建一个实体类,再给出具体构造流程,那就累死了。
这种情况下,合理的办法是写一个随机迷宫生成器,能根据具体要求不同生成无数种迷宫,这就是创建型模式的意义所在。无论是“虚拟工厂模式”,还是“生成器”模式,工厂模式,都是在具体生成的策略上做文章,但一个大前提不变:具体产出的产品Product都是由很多小的组件组合而成,而且组装的时候灵活度非常高,甚至是runtime用户定制的,或者是随机的。这就导致组合出来的产品Product单独作为一个类存在的意义很小,反而是构造器的作用被放大。索性把构造过程独立出来成为一个方法,把这个方法用到的参数作为成员字段一起封装起来,再来个构造器只负责初始化这些参数,这种累就是“工厂类”。
class MazeFactory{ //巨大迷宫生成算法 Maze mazeGenerator(int roomNum, int wallNum, int doorNum){ ... } //构造器 初始化生成迷宫的参数 MazeFactory(){ roomNum = 100; wallNum = 1000; doorNum = 200; } //字段:生成迷宫的参数 int roomNum; int wallNum; int doorNum; }
原来的那个迷宫类,本身的构造器不承担任何功能了。
class Maze{ void play(){...} Maze(int roomNum, int wallNum, int doorNum){ Room[] roomSet = new Room[roomNum]; Wall[] wallSet = new Wall[wallNum]; Door[] doorSet = new Door[doorSet]; } Room[] roomSet; Wall[] wallSet; Door[] doorSet; }
所以再回到工厂方法,书里说的所有的东西,都是基于这个前提,也就是我们说好了啊,迷宫这东西的类文件里是没有具体构造方法的,都是要用工厂类MazeFactory来生成的。至于后来,我们加入了方迷宫,和方迷宫生成器。又有了圆迷宫和圆迷宫生成器。有了一堆生成器复杂了之后,又想到用多态来解耦,这都是后话,是在确定了使用工厂类的前提下,利用多态解耦的优化方案。所以才有了最初的这段代码:
//两个接口 interface Maze{ void play(); } interface MazeFactory{ Maze mazeGenerator(); } //各自继承类 CircularMaze implements Maze{ ... } SquareMaze implements Maze{ ... } CircularFactory implements MazeFactory{ ... } SquareMazeFactory implements MazeFactory{ ... } //多态,面向接口 public static void mazeGame(MazeDactory fact){ Maze z = fact.mazeGenerator(); z.play(); }
再来,如果程序为:
public static void serviceConsumer(Service s){ s.method1(); s.method2(); }
Service从哪来呢?当然是从其他地方new了传进来:
Service s = new ServiceA(); serviceConsumer(s);
Service为什么是一个接口?
ServiceA s = new ServiceA(); s.method1(); s.method2();
如果有一天,领导要求将数据库从MYSQL换成ACCESS,因为只有一个ServiceA所以只能把所有用到ServiceA的地方都替换成ServiceB:
//!ServiceA s = new ServiceB(); ServiceB s = new ServiceB(); s.method1(); s.method2();
如果是个小项目,没关系,如果是一个庞大的团队项目,,,根据设计原则中的依赖倒置原则,设计一个接口,然后扩展一个类,这样就能灵活应对boss的需求了:
Service s; if (Config.DB == SQL) s = new ServiceA(); else if (Config.DB == ACCESS) s = new ServiceB(); s.method1(); s.method2();
又过了几天,用到的数据库越来越多,if else也越来越多,我么你决定隔离这些变化,将Service实例化过程放在一个地方,简单工厂诞生:
Service s = ServiceFactory.getService(Config.DB); s.method1(); s.method2(); public class ServiceFactory{ public static Service getService(DB db){ if(db == SQL) return new SerivceA(); else if(db == ACCESS) return new ServiceB(); else return null; } }
这样,即时数据库不断变化,我也只需要扩展一个新的类,并且修改工厂这个类就行了。但是ServiceFactory是一个接口,这就是抽象工厂。
另一位回答者接下去回答:
如果只有一个工厂,但同一个Service在不同环境下会有不同的实现,如果环境很多,这个ServiceFactory会变成什么样呢:
class ServiceFactory{ Environment env; public static Service getService(){ if(env == MYSQL){ return new ServiceA(arg1, arg2,...) }else if (env == ORACLE){ return new ServiceB(arg1, arg2,...); }else if (env == BIG_TABLE){ return new ServiceC(arg1, arg2,...); } } }
那么if else越来越多,每次修改都需要重新编译整个Factory,而且,一般情况下,实现这些Service的往往是不同组的人,每个组都往这个类里加参数/状态,合并代码时会出现无数个冲突,还可能互相用错参数从而影响别人代码,抽象工厂大家的实现分开到不同的类里面,让大家各改各的文件,互不影响:
//小组1的文件: class MySqlFactory implements ServiceFactory{ public static Service getService(){ //计算 arg1, arg2,... return new ServiceA(arg1, arg2); } } //小组2的文件 class OrcaleFactory implements ServiceFactory{ public static Service getService(){ //计算arg1, arg2, ... return new ServiceB(arg1, arg2); } } ...
这些工厂不需要使用时才new,只要事先new好,存入一个Map,根据具体环境随时调用:
Map<Environment, ServiceFactory> factoryMap = new HashMap<>(); factoryMap.put(MYSQL, new MySqlFactory()); factoryMap.put(ORACLE, new OracleFactory()); ...
然后调用serviceConsumer的那个地方只需要:
Environment env = ...//获得环境变量env ServiceFactory factory = factoryMap.get(env); serviceSonsumer(factory);
以后如果有新的数据库,重新写一个ServiceFactory的实现类,编译一下放一块就行,在factoryMap里放入实例就好了,别的代码完全不需要动,甚至不需要重新编译。
可不可以不要Factory,直接用ServiceMap呢?
Map<Environment, Service> serviceMap = new HashMap<>(); serviceMap.put(MYSQL, new ServiceA(..)); serviceMap.put(ORACLE, new ServiceB(..));
基本是不行的,首先Service的初始化一般需要很多参数,不可能在程序刚载入时就存在,另外Service的实例会new很多个,也不满足一一对应的关系。但Factory的构造函数一般不需要参数。相比于Service,Factory内部保存的仅仅是一些参数,占用内存小得多,长时间驻留在内存中也没有太大的损害。
相关文章推荐
- java 工厂模式,工厂方法模式,抽象工厂模式 详解
- 设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
- 轻松掌握Java工厂模式、抽象工厂模式
- Java-设计模式-工厂模式浅谈(二)-工厂方法模式
- 工厂模式,工厂方法模式,抽象工厂模式 详解
- C++设计模式之1-工厂模式(简单工厂模式-工厂方法模式-抽象工厂模式)
- 工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式
- java设计模式之工厂方法模式和抽象工厂模式
- 设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
- Java工厂模式---之简单工厂模式,工厂模式,抽象工厂模式
- 设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
- java va中工厂模式( 静态工厂模式、工厂方法模式、抽象工厂模式)的介绍以及简单例子
- 设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
- java学习之工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
- Java设计模式(2)创建型:工厂模式(工厂方法模式与抽象工厂模式)
- 设计模式系列——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
- 工厂模式——三个工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
- java23种设计模式-----简单工厂模式、工厂方法模式、抽象工厂模式的区别
- 工厂模式、工厂方法模式和抽象工厂模式
- Java设计模式-工厂方法模式和抽象工厂模式