您的位置:首页 > 编程语言 > Java开发

工厂模式(工厂方法模式,抽象工厂模式) Java

2016-10-07 22:10 246 查看
工厂模式分为3类:

* 简单工厂模式 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 工厂模式
相关文章推荐