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

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值