工厂模式

工厂模式也是非常经典的设计模式之一。工厂模式的主要目的是封装创建具体对象的逻辑,只向外提供创建对象的接口方法。此外,也有地方指出加入工厂模式还为了满足项目的扩展性

工厂模式网上已经有了很多例子,但是存在下列两个问题:

1. 将简单工厂模式(有的地方也成为“工厂方法”)也列入工厂模式;

2. 不合时宜地使用抽象工厂模式;

本文将以简单例子说明简单工厂模式、工厂模式、抽象工厂模式。


简单工厂模式

          简单工厂模式封装了对象实例化,客户端不再使用 new Object()形式,可以根据用户的选择条件来实例化相关的类。例如,戴尔鼠标、联想鼠标都是鼠标,我们抽象出两种鼠标共同的getBrand()函数至IMouse接口中。

public interface IMouse {

    String getBrand();
}

然后,戴尔鼠标类、联想鼠标类相当于实现了IMouse的getBrand()的抽象方法:

public class DellMouse implements IMouse {

    @Override
    public String getBrand() {
        return "Dell";
    }
}
public class LenovoMouse implements IMouse {

    @Override
    public String getBrand() {
        return "Lenovo";
    }
}

那么,可以定义个工厂类SimpleMouseFactory,利用Java的多态特性封装LenovoMouse、DellMouse的对象实例化过程:

public class SimpleMouseFactory {

    public IMouse createMouse(String b){
        switch (b){
            case "Dell":
                return new DellMouse();
            case "Lenovo":
                return new LenovoMouse();
            default:
                return null;
        }
    }
}

其实,通过String入参来枚举判断具体生成哪个对象实例是不漂亮的,因为用户往往不知道Brand是啥。而且就String来说还要判断是否大小写、用户输入错误,比较费力不讨好。参阅一些框架的源代码,其中的入参一般都是int枚举,或者enum枚举。

例如,int枚举:         

public class SimpleMouseFactory {

    public static final int DELL_MOUSE = 0;
    public static final int LENOVO_MOUSE = 1;

    public IMouse createMouse(int b){
        switch (b){
            case DELL_MOUSE:
                return new DellMouse();
            case LENOVO_MOUSE:
                return new LenovoMouse();
            default:
                return null;
        }
    }
    
    public static void main(String[] args){
        SimpleMouseFactory factory = new SimpleMouseFactory();
        IMouse dellMouse = factory.createMouse(DELL_MOUSE);    
        //avoid using factory.createMouse(0);
        IMouse lenovoMouse = factory.createMouse(LENOVO_MOUSE);
    }
}

再比如使用enum类型:

public class SimpleMouseFactory {

    enum Brand{Dell, Lenovo};

    public IMouse createMouse(Brand b){
        switch (b){
            case Dell:
                return new DellMouse();
            case Lenovo:
                return new LenovoMouse();
            default:
                return null;
        }
    }
}

简单工厂模式向外界封装了new Object()细节,工程量变大后方便维护管理。

但是,简单工厂模式缺点也很明显。最致命的一点,就是它违背了开-闭原则。开闭原则的最简单表达就是“只允许扩展,不允许修改”,即一个实体在不改变它的源代码的前提下变更它的行为。

试想,如果IMouse接口有新的产品——惠普鼠标。简单工厂模式(SimpleFactory)除了新建HPMouse类实现IMouse接口外,还要打开SimpleFactory,修改createMouse()函数,添加新的返回对象HPMouse。那么,随着项目扩展,需要增加/删除对象类型愈来愈多,SimpleFactory类打开修改的次数也会增多。这不仅使简单工厂愈来愈臃肿,耦合性大大增加,同时也违背了面向对象程序设计原则。

所以,简单工厂模式不是一种"真正"的工厂模式。


工厂模式

工厂模式就是为了解决简单工厂模式存在问题而生的。原来简单工厂耦合性很大,是因为一个工厂统管了所有产品类型的生产。如果创建很多工厂,每个工厂类专注于生产一个产品,就能降低不少耦合,此外也遵循了开-闭原则规范。

按这么做,首先我们抽象这些工厂类成一个IFactory接口:

public interface IMouseFactory {

    IMouse createMouse();
}

然后,就有实现IFactory接口的DellFactory专门生产DellMouse,LenovoFactory专门生产LenovoMouse:

public class DellFactory implements IMouseFactory {
    @Override
    public IMouse createMouse() {
        return new DellMouse();
    }
}
public class LenovoFactory implements IMouseFactory {
    @Override
    public IMouse createMouse() {
        return new LenovoMouse();
    }
}

可以看到,一个工厂类专门负责生产一个IMouse对象。这样加入新产品HPMouse,不用更改IFactory,只用构建HPMouse类和配套工厂类HPFactory即可。

然而,这样的工厂模式也有弊端:

1. 每增加一个产品类,就要对应增加一个对应的工厂类,增加额外代码量;

2. 只适用于单产品族的生产管理;


抽象工厂模式

我们把之前IMouse称为”产品族“,对应IMouseFactory抽象工厂来生产。试想,假设加入新产品族IKeyBoard:

public interface IKeyBoard {

    int getKeys();
}


public class DellKeyboard implements IKeyBoard {
    @Override
    public int getKeys() {
        return 87;
    }
}


public class LenovoKeyboard implements IKeyBoard {

    @Override
    public int getKeys() {
        return 104;
    }
}

那就势必除了IMouseFactory外还要有新的IKeyBoardFactory抽象工厂诞生:

public interface IKeyBoardFactory {
    
    IKeyBoard createKeyBoard();
}

但是我们看到,DellKeyBoard、LenovoKeyBoard仍然可以一一对应DellFactory、LenovoFactory生产。因此,可以聚合IKeyBoardFactory和IMouseFactory为一个超级工厂IComputerFactory生产:

public interface IComputerFactory {
    
    IMouse createMouse();
    IKeyBoard createKeyBoard();
}

那么自然,DellFactory、LenovoFactory的职责发生了扩展:

public class DellFactory implements IComputerFactory {
    @Override
    public IMouse createMouse() {
        return new DellMouse();
    }

    @Override
    public IKeyBoard createKeyBoard() {
        return new DellKeyboard();
    }
}


public class LenovoFactory implements IComputerFactory {
    
    @Override
    public IMouse createMouse() {
        return new LenovoMouse();
    }

    @Override
    public IKeyBoard createKeyBoard() {
        return new LenovoKeyboard();
    }
}

也就是说,抽象工厂模式整合了多个抽象工厂为一个“超级工厂”,是为了解决多个产品族加入造成抽象工厂过多的问题(它不能解决同一产品族中多个产品类的多个工厂弊病)。

抽象工厂模式是在工厂模式基础上,加入了简单工厂的优点。但是,这样带来同样一个问题,产品族的扩展性。如果产品族增加,例如多了Screen类,IComputerFactory类扩展需要多加一个createScreen(),同样要打开IComputerFactory这个工厂,也同样容易违背开闭原则。


思辩

抽象工厂模式还有一个值得注意的地方,就是抽象聚合必须是多个产品族之间有相互关联。否则,DellFactory可以生产LenovoKeyBoard,也可以生产LenovoMouse,就会造成2*2=4种排列组合:

1. DellFactory生产DellMouse,DellKeyBoard;LenovoFactory生产LenovoMouse,LenovoKeyBoard。

2. DellFactory生产DellMouse,LenovoKeyBoard;LenovoFactory生产LenovoMouse,DellKeyBoard。

3. DellFactory生产LenovoMouse,DellKeyBoard;LenovoFactory生产DellMouse,LenovoKeyBoard。

4. DellFactory生产LenovoMouse,LenovoKeyBoard;LenovoFactory生产DellMouse,DellKeyBoard。

试想,这样就需要构建四个工厂类来实现IComputerFactory,这是个噩梦!于是,对于多个产品族之间完全独立的例子,网上流行了这种写法:

public interface IComputerFactory {

    enum Brand{Dell, Lenovo};

    IMouse createMouse(Brand b);
    IKeyBoard createKeyBoard(Brand b);
}



public class MouseFactory implements IComputerFactory{
    @Override
    public IMouse createMouse(Brand b) {
        switch (b){
            case Dell:
                return new DellMouse();
            case Lenovo:
                return new LenovoMouse();
            default:
                return null;
        }
    }

    @Override
    public IKeyBoard createKeyBoard(Brand b) {
        return null;
    }
}



public class KeyBoardFactory implements IComputerFactory{
    @Override
    public IMouse createMouse(Brand b) {
        return null;
    }

    @Override
    public IKeyBoard createKeyBoard(Brand b) {
        switch (b){
            case Dell:
                return new DellKeyboard();
            case Lenovo:
                return new LenovoKeyboard();
            default:
                return null;
        }
    }
}

这种写法有两大弊病:

1. IComputerFactory超级工厂的createMouse(Brand b)和createKeyBoard(Brand b),并不是两个子Factory(MouseF actory和KeyBoardFactory)的共同特性,所以这是一个不成功的抽象;

2. IComputerFactory超级工厂本身完成了子工厂的整合,这里因为抽象不成功间接又造成了拆分;

所以抽象工厂模式并非都适用多产品族的情况。这时候多个抽象工厂IMouseFactory、IKeyBoardFactory...反而层次更加清晰。

这也是为何抽象工厂模式多常用于对数据库中的表项进行修改(关联性强)。

抽象共性一定要注意一点:强扭的瓜不甜。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值