抽象工厂的缺点和解决方法

先上干货,后面才是代码例子:
参考《设计模式之禅》的话“抽象工厂模式是工厂方法模式的升级版本”。其实就是用工厂方法生产具有多维度变化的产品类。什么是多维度的类?即一个类会有多个维度会影响其发生变化。比如下面例子的汽车类,一个维度是牌子,一个维度是型号。(23种设计模式里面还有结构型的桥接模式和行为型的访问者模式,也可以用来处理多维度类的问题)
我们知道,抽象工厂最大的缺点是产品族难以扩展。那么应该怎么解决这个问题呢?为什么下面例子中我要用型号这个维度作为抽象工厂的接口呢?
GoF里面提供了一种解决方法:一个更灵活但不太安全的设计是给创建对象的操作增加一个参数。该参数指定了将被创建的对象的种类。它可以是一个类标识符、一个整数、一个字符串,或其他任何可以标识这种产品的东西。
其实就是把判断逻辑集中到了工厂类,它自己也说了,这样做其实抽象工厂里面只需要一个方法make(typy)即可,make的实现根据传进来的type判断生产哪个产品。我认为这不是一个好的解决方法,它会使工厂类的判断逻辑越来越庞大,违反了单一性原则,再说这已经不是抽象工厂模式了。
让我来回答这个问题,要考虑两个点。第一是产品的扩展性,第二是需求的变更性。
前者指的是这个多维度的产品类一般会向哪个维度扩展,即哪个维度是相对稳定的,哪个维度是相对异变的。一般情况下选择稳定的维度作为抽象工厂的接口。举《设计模式之禅》里面的例子:女娲造人。这里的人有两个维度——性别和肤色。我们知道,性别这个维度是稳定的:男、女或不详(就算有第三种性别)。所以应该把性别这个维度作为抽象工厂的接口,这样就可以从容应对肤色的变更。(《设计模式之禅》里面把肤色作为抽象工厂的接口了,我认为是不妥的)。
后者考虑的是客户端的需求变更问题,就像代码里的例子。假设现在公司跟宝马公司决裂了,董事会决定全公司不用宝马车,跟奔驰公司签了新约换奔驰。好,这时只要在客户端代码的1处更换具体工厂即可:CarFactory factory = new BenzFactory(),后面的业务逻辑代码无需变动(这点与策略模式的功能有点像)。方便!
那么怎么衡量这两点呢?
我觉得应该优先考虑第二点,因为客户的需求才是最重要的。但如果在需求不明确,只是设计一般的API的情况下,就要考虑第一点了,如果是这样,例子中就要以牌子作为抽象工厂的接口了。因为一般情况下汽车牌子是稳定的,型号是不断更新的。举个生产手机的例子,假设富士康签了长期合同为两家公司(苹果、三星)生产手机,那必须要把牌子作为抽象工厂的接口,因为在很长一段时间内都是跟这两家公司合作,而型号却经常更新的。

接下来补上完整的抽象工厂模式&代码例子:
抽象工厂模式的定义是:为创建一组相关或相互依赖的对象提供一个接口,而无需指定他们的具体类。其结构图如下(来自GoF):
这里写图片描述
用生产汽车举个例子(java代码):现在有两种牌子的汽车宝马BMW和奔驰Benz,假设两个牌子的车都有两种型号v1,v2和v3(这个假设是抽象工厂应用场景的要求:一个对象族(或是一组没有任何关系的对象)都有相同的约束。——出自《设计模式之蝉》)。下面先定义汽车的接口(这里改为抽象类,用到了模板方法):

public abstract class Car {
    public String brand();
    public String version();
    public void run() {
        System.out.println(this.brand() + this.version());
    }
}

宝马汽车,因为只确定了牌子,型号还没定,所以属于抽象类,还不能生产(实例化):

public abstract class BMW extends Car {
    public String brand() {
        return “BMW”;
    }
}

奔驰:

public abstract class Benz extends Car {
    public String brand() {
        return “Benz”;
    }
}

下面确定型号:

public class BMWv1 extends BMW {
    public String version() {
        return “v1”;
    }
}
public class BMWv2 extends BMW {
    public String version() {
        return “v2”;
    }
}
public class BMWv3 extends BMW {
    public String version() {
        return “v3”;
    }
}

奔驰也有三种型号,这里省略。
下面定义抽象工厂,决定抽象工厂内的接口很重要,这里有两种选择,一个是牌子、一个是型号,这里选型号作为抽象工厂的接口,原因上面已分析:

public interface CarFactory {
    public Car createV1();
    public Car createV1();
    public Car createV1();
}

具体宝马的工厂:

public class BMWFactory implementsCarFactory{
    public Car createV1() {
        return newBMWv1();
    }
    public Car createV2() {
        return newBMWv2();
    }
    public Car createV3() {
        return newBMWv3();
    }
}

具体的奔驰工厂也类似,这里省略。
下面是客户端:

public class Client {
    public static void main(String[] args) {
        //一开始公司和宝马签了合同,不管是接送老板用的私家车v1,还是接送普通员工的面包车v2,都用宝马。
        CarFactory factory = new BMWFactory(); //1
        Car carForBoss = factory.createV1();
        Car carForStaff = factory.createV2();
        //开车去接送接送boss和普通的staff,业务代码
    }
}

以上就是抽象工厂模式的简单用法。

看官觉得有问题的话请多多指教。
支持原创,转载请注明出处。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值