创建类模式:抽象工厂模式VS建造者模式。

抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关系构件过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,他的主要目的是通过组装零配件而产生一个新产品,两者的区别还是比较明显的,但是还有读者对这两个模式产生混淆,我们通过一个例子说明两者的差别。

现代化的汽车工厂能够批量生产汽车(不考虑手工打造的豪华车)。不同的工厂生产不同的汽车,宝马工厂生产宝马牌子的车,奔驰工厂生产奔驰牌子的车。车不仅具有不同品牌,还有不同的用途分类,如商务车Van,运动型车SUV等,我们按照两种设计模式分别实现车辆的生产过程。

按抽象工厂模式生产车辆

按照抽象工厂模式,首先需要定义一个抽象的产品接口即汽车接口,然后宝马和奔驰分别实现该接口,由于他们只具有了一个品牌属性,还没有定义一个具体的型号,属于对象的抽象层次,每个具体车型由其子类实现,如R系列的奔驰车是商务车,X系列的宝马车属于SUV。

我们从两个维度看产品:品牌和车型,每个品牌下都有两个车型,如宝马SUV,宝马商务车等,同时我们又建造了两个工厂,一个专门生产宝马车的宝马工厂BMWFactory,一个是生产奔驰车的奔驰车生产工厂BenzFactory。当然,汽车工厂也有两个不同的维度,可以建立这样两个工厂:一个专门生产SUV车辆的生产工厂,生产宝马SUV和奔驰SUV,另外一个工厂专门生成商务车,分别实宝马商务车和奔驰商务车,这样设计在技术上是完全可行的,但是在业务上是不可行的,为什么?这是因为你看到过有一个工厂既能生产奔驰SUV也能生产宝马SUV吗?这是不可能的,因为业务受限,除非是国内的山寨工厂。我们先来看产品类,汽车接口如下所示。

public interface ICar {
	/**
	 * 汽车的生产商,也就是牌子
	 * 
	 * @return
	 */
	String getBand();

	/**
	 * 汽车的型号
	 * 
	 * @return
	 */
	String getModel();
}

在产品接口中我们定义了车辆有两个可以查询的属性:品牌和型号,奔驰车和宝马车是两个不同品牌的产品,但不够具体,只是知道他们的品牌而已,还不能够实例化,因此还是一个抽象类,如下所示。

public abstract class AbsBMW implements ICar {
	private final static String BMW_BAND = "宝马汽车";

	@Override
	public String getBand() {
		return BMW_BAND;
	}

	/**
	 * 型号由具体的实现类实现
	 */
	public abstract String getModel();
}

抽象产品类中实现了产品的类型定义,车辆的型号没有实现,两实现类分别实现商务车和运动型车,分别如下所示。

public class BMWVan extends AbsBMW {
	private final static String SEVENT_SEARIES = "7系列车型商务车";

	@Override
	public String getModel() {
		return SEVENT_SEARIES;
	}

}
public class BMWSuv extends AbsBMW {
	private final static String X_SEARIES = "X系列车型SUV";

	@Override
	public String getModel() {
		return X_SEARIES;
	}

}

奔驰车与宝马车类似,都已经有清晰品牌定义,但是型号还没有确认,也是一个抽象产品类,如下所示。

public abstract class AbsBenz implements ICar {
	private final static String BENZ_BAND = "奔驰汽车";

	@Override
	public String getBand() {
		return BENZ_BAND;
	}

	/**
	 * 具体型号由实现类完成
	 */
	public abstract String getModel();
}

由于分类的标准是相同的,因此奔驰车也应该有商务车和运动车两个类型,分别如下所示。

public class BenzVan extends AbsBenz {
	private final static String R_SERIES = "R系列商务车";

	@Override
	public String getModel() {
		return R_SERIES;
	}

}
public class BenzSuv extends AbsBenz {
	private final static String G_SERIES = "G系列SUV";

	@Override
	public String getModel() {
		return G_SERIES;
	}

}

所有的产品类都已经实现了,剩下的工作就是要定义工厂类进行生产,由于产品类型多样,也导致了必须有多个工厂类来生产不同产品,首先就需要定义一个抽象工厂,声明每个工厂必须完成的职责,如下所示。

public interface CarFactory {
	/**
	 * 生产SUV
	 * 
	 * @return
	 */
	ICar createSuV();

	/**
	 * 生产商务车
	 * 
	 * @return
	 */
	ICar createVan();
}

抽象工厂定义了每个工厂必须生产两个类型车:SUV(运动车)和VAN(商务车),否则一个工厂就不能被实例化,我们来看宝马车工厂,如下所示。

public class BMWFactory implements CarFactory {

	@Override
	public ICar createSuV() {
		return new BMWSuv();
	}

	@Override
	public ICar createVan() {
		return new BMWVan();
	}

}

很简单,你要我生产宝马商务车,没问题,直接产生一个宝马商务车对象,返回给调用者,这对调用者来说根本不需要关心到底是怎么生产的,他只要找到一个宝马工厂,即可生产出自己需要的产品(汽车)。奔驰车工厂与此类似,如下所示。

public class BenzFactory implements CarFactory {

	@Override
	public ICar createSuV() {
		return new BenzSuv();
	}

	@Override
	public ICar createVan() {
		return new BenzVan();
	}

}

产品和工厂都具备了,剩下的工作就是建立一个场景类模拟调用者调用,如下所示。

public class Client {
	public static void main(String[] args) {
		// 要求生产一辆奔驰SUV
		System.out.println("===要求生产一辆奔驰SUV===");
		// 首先找到生产奔驰车的工厂
		System.out.println("A、找到奔驰车工厂");
		CarFactory carFactory = new BenzFactory();
		// 开始生产奔驰SUV
		System.out.println("B、开始生产奔驰SUV");
		ICar benzSuv = carFactory.createSuV();
		// 生产完毕,展示一下车辆信息
		System.out.println("C、生产出的汽车如下:");
		System.out.println("汽车品牌:" + benzSuv.getBand());
		System.out.println("汽车型号:" + benzSuv.getModel());
	}
}

对外界调用者来说,只要更换一个具备相同结构的对象,即可发生非常大的改变,如我们原本使用BenzFactory生产汽车,但是过了一段时间后,我们的系统需要生产宝马汽车,这对系统来说不需要很大的改动,只要把工厂类使用BMWFactory代替即可,立刻可以生产出宝马车,注意这里生产的是一辆完整的车,对于一个产品,只要给出产品代码(车类型)即可生产,抽象工厂模式把一辆车认为是一个完整的、不可拆分的对象。他注重完整性,一个产品一旦找到一个工厂生产,那就是固定的型号,不会出现一个宝马工厂生产奔驰车的情况。那现在的问题是我们就想要一辆混合的车型,如奔驰的引擎,宝马的车轮,那该如何处理呢?使用我们的建造者模式!

按建造者模式生产车辆

按照建造者模式设计一个生产车辆需要把车辆进行拆分,拆分成引擎和车辆两部分,然后由建造者进行建造,想要什么车,你只要有设计图纸就成,马上可以制造一辆车出来。他注重的是对零件的装配、组合、封装,他从一个细微构件装备角度看待一个对象。

注意蓝图类Blueprint,他负责对产品建造过程定义。既然要生产产品,那必然要对产品进行一个描述,在这里我们定义了一个接口来描述汽车,如下所示。

public interface ICar {
	/**
	 * 汽车车轮
	 * 
	 * @return
	 */
	String getWheel();

	/**
	 * 汽车引擎
	 * 
	 * @return
	 */
	String getEngine();
}

我们定义一辆车必须有车轮和引擎,具体的产品如下所示。

public class Car implements ICar {
	// 汽车引擎
	private String engine;
	// 汽车车轮
	private String wheel;

	/**
	 * 一次性传递汽车需要的信息
	 * 
	 * @param engine
	 * @param wheel
	 */
	public Car(String engine, String wheel) {
		this.engine = engine;
		this.wheel = wheel;
	}

	@Override
	public String getWheel() {
		return this.wheel;
	}

	@Override
	public String getEngine() {
		return this.engine;
	}

	@Override
	public String toString() {
		return "车的轮子是:" + wheel + "\n车的引擎是:" + engine;
	}
}

一个简单的JavaBean定义产品的属性,明确对产品的描述。我们继续来思考,因为我们的产品是比较抽象的,他没有指定引擎的型号,也没有指定车轮的牌子,那么这样的组合方式有很多,完全要靠建造者来建造,建造者说要生产一辆奔驰SUV那就得用奔驰的引擎和奔驰的车轮,该建造者对于一个具体的而产品来说是绝对的权威,我们来描述一下建造者,如下所示。

public abstract class CarBuilder {
	// 设计蓝图
	private Blueprint bp;

	public Car buildCar() {
		// 按照顺序生产一辆车
		return new Car(buildEngine(), buildWheel());
	}

	/**
	 * 接收一份设计蓝图
	 * 
	 * @param _bp
	 */
	public void receiveBlueprint(Blueprint _bp) {
		this.bp = _bp;
	}

	/**
	 * 查看蓝图,只有真正的建造者才可以查看蓝图
	 * 
	 * @return
	 */
	protected Blueprint getBlueprint() {
		return bp;
	}

	/**
	 * 建造轮子
	 * 
	 * @return
	 */
	protected abstract String buildWheel();

	/**
	 * 建造引擎
	 * 
	 * @return
	 */
	protected abstract String buildEngine();
}

看到Blueprint类了,他中文的意思是“蓝图”,你要建造一辆车必须有一个设计样稿或者蓝图吧,否则怎么生产?怎么装配?该类就是一个可参考的生产样本,如下所示。

public class Blueprint {
	// 车轮的要求
	private String wheel;
	// 引擎的要求
	private String engine;

	public String getWheel() {
		return wheel;
	}

	public void setWheel(String wheel) {
		this.wheel = wheel;
	}

	public String getEngine() {
		return engine;
	}

	public void setEngine(String engine) {
		this.engine = engine;
	}

}

这和一个具体的产品Car类是一样的?错,不一样!他是一个蓝图,是一个可以参考的模板,有一个蓝图可以设计出非常多的产品,如有一个R系统的奔驰商务车设计蓝图,我们就可以生产出一系列的奔驰车。他指导我们的产品生产,而不是一个具体的产品。我们来看宝马车建造车间,如下所示。

public class BMWBuilder extends CarBuilder {

	@Override
	protected String buildWheel() {
		return super.getBlueprint().getWheel();
	}

	@Override
	protected String buildEngine() {
		return super.getBlueprint().getEngine();
	}

}

这是非常简单的类。只要获得一个蓝图,然后按照蓝图制造引擎和车轮即可,剩下的事情就交给抽象的建造者进行装配。奔驰车间与此类似,如下所示。

public class BenzBuilder extends CarBuilder {

	@Override
	protected String buildWheel() {
		return super.getBlueprint().getWheel();
	}

	@Override
	protected String buildEngine() {
		return super.getBlueprint().getEngine();
	}

}

两个建造车间都已经完成,那现在的问题就变成了怎么让车间运作,谁来编写蓝图?谁来协调生产车间?谁来对外提供最终产品?于是导演类出场了,他不仅仅有每个车间需要的设计蓝图,还具有指导不同车间装配顺序的职责,如下所示。

public class Director {
	// 声明对建造者的引用
	private CarBuilder benzBuilder = new BenzBuilder();
	private CarBuilder bmwBuilder = new BMWBuilder();

	/**
	 * 生产奔驰车SUV
	 * 
	 * @return
	 */
	public ICar createBenzSuv() {
		return this.createCar(this.benzBuilder, "benz的引擎", "benz的轮胎");
	}

	/**
	 * 生产出一辆宝马商务车
	 * 
	 * @return
	 */
	public ICar createBMWVan() {
		return this.createCar(this.bmwBuilder, "BMW的引擎", "BMW的轮胎");
	}

	/**
	 * 生产出一个混合车型
	 * 
	 * @return
	 */
	public ICar createComplexCar() {
		return this.createCar(this.bmwBuilder, "BMW的引擎", "benz的轮胎");
	}

	/**
	 * 生产车辆
	 * 
	 * @param _carBuilder
	 * @param engine
	 * @param wheel
	 * @return
	 */
	private ICar createCar(CarBuilder _carBuilder, String engine, String wheel) {
		// 导演怀揣蓝图
		Blueprint bp = new Blueprint();
		bp.setEngine(engine);
		bp.setWheel(wheel);
		System.out.println("获得生产蓝图");
		_carBuilder.receiveBlueprint(bp);
		return _carBuilder.buildCar();
	}
}

这里有一个私有方法createCar,其作用是减少导演类中的方法对蓝图的依赖,全部由该方法来完成。我们编写一个场景类,如下所示。

public class Client {
	public static void main(String[] args) {
		// 定义出导演类
		Director director = new Director();
		// 给我一辆奔驰车SUV
		System.out.println("===制造一辆奔驰SUV===");
		ICar benzSuv = director.createBenzSuv();
		System.out.println(benzSuv);
		// 给我一辆宝马商务车
		System.out.println("\n===制造一辆宝马商务车===");
		ICar bmwVan = director.createBMWVan();
		System.out.println(bmwVan);
		// 给我一辆混合车型
		System.out.println("\n===制造一辆混合车===");
		ICar complexCar = director.createComplexCar();
		System.out.println(complexCar);
	}
}

场景类只要找到导演类(也就是车间主任了)说给我制造一辆这样的宝马车,车间主任马上通晓你的意图,设计了一个蓝图,然后命令建造车间拼命加班加点建造,最终返回给你一件最新出品的产品。

只要有设计蓝图,我们可以立刻生产出一辆混合车型。反观我们的抽象工厂模式,他是不可能实现该功能的,因为他更关注的是整体,而不关注到底用的是奔驰引擎还是宝马引擎,而我们的建造者模式却可以很容易实现该设计,市场信息变更了,我们就可以立刻跟进,生产出客户需要的产品。

最佳实践

注意看上面的描述,我们在抽象工厂模式中使用“工厂”来描述构建者,而在建造者模式中使用“车间”来描述构件者,其实我们已经在说他们两者的区别了,抽象工厂模式就好比是一个一个的工厂,宝马车工厂生产宝马SUV和宝马VAN,奔驰车工厂生产奔驰车SUV和奔驰车VAN,他是从一个更高层次去看对象的构建,具体到工厂内部还有很多的车间,如制造引擎的车间、装配引擎的车间等,但这些都是隐藏在工厂内部的细节,对外不公布。也就是对领导者来收,他只要关心一个工厂到底是生产什么产品的,不用关心具体怎么生产。而建造者模式就不同了,他是由车间组成,不同的车间完成不同的创建和装配任务,一个完整的汽车生产过程需要引擎制造车间、引擎装配车间的配合才能完成,他们配合的基础就是设计蓝图,而这个蓝图是掌握在车间主任(导演类)手中,他给建造车间什么蓝图就能生产什么产品,建造者模式更关心建造过程。虽然从外界看来一个车间还是生产车辆,但是这个车间的转型是非常快的,只要重新设计一个蓝图,即可产生不同的产品,这有赖于建造者模式的功劳。

相对来说,抽象工厂模式比建造者模式的尺度要大,他关注产品整体,而建造者模式关注构建过程,因此建造者模式可以很容易的很容易的构建出一个崭新的产品,只要导演类能够提供具体的工艺流程。也正因此如此,两者的应用场景截然不同,如果希望屏蔽对象的创建过程,只提供一个封装良好的对象,则可以选择抽象工厂方法模式。而建造者模式可以用在构件的装配方面,如通过装配不同的组件或者相同组件的不同顺序,可以产生出一个新的对象,他可以产生一个非常灵活的架构,方便的扩展和维护系统。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值