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

来源:互联网 发布:网络打印机怎么添加 编辑:程序博客网 时间:2024/06/10 04:33

工厂模式分为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 = ...//获得环境变量envServiceFactory 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内部保存的仅仅是一些参数,占用内存小得多,长时间驻留在内存中也没有太大的损害。

0 0