设计模式(三)一文彻底搞明白工厂和抽象工厂

  • 一文带你搞懂Java动态代理
  • 几分钟带你搞懂策略模式
  • 几分钟带你搞懂观察者模式
  • 一文彻底搞明白工厂和抽象工厂
  • 一文搞明白装饰者模式
  • 最全单例模式
  • 几段代码搞明白命令模式
  • 几段代码搞明白适配器模式
  • 一看就懂的外观模式
  • 一看就懂的模版方法模式
  • 几段代码搞懂迭代器模式

在正式的介绍工厂模式和抽象工厂模式之前,我们来先简单的认识下简单工厂,简单工厂其实并不是一个设计模式,反而更像是一种约定俗称的编程习惯。下面就以一个糖葫芦的例子来分析一下吧:

设计模式(三)一文彻底搞明白工厂和抽象工厂_第1张图片

这是一个抽象的糖葫芦类。因为在糖葫芦的制作过程中,穿串、熬制甜料、浇糖过程都是不变的,所以我们创建了一个抽象类,不过这依然符合针对接口编程,不针对实现编程的OO原则,因为这里的接口的含义为广义的超类型。

public abstract class Product {
	
	/**
	 * 选取原料,包括山楂、橘子、糯米等
	 */
	abstract void prepare();
	/**
	 * 穿串
	 */
	public void chuanChuan() {
		System.out.println("将选取的原料穿串");
	}
	/**
	 * 熬制调料
	 */
	public void createSweetener(){
		System.out.println("熬制白糖调料");
	}
	/**
	 * 浇在串上冷却
	 */
	public void castSweetenerToChuan() {
		System.out.println("将调料汁浇在串上并冷却");
	}
}

这是一个具体的山楂糖葫芦。

public class ShanZhaProduct extends Product{
	
	@Override
	void prepare() {
		System.out.println("选取精品山楂");		
	}
}

这是一个具体的橘子糖葫芦。

public class OrangeProduct extends Product {

	@Override
	void prepare() {
		System.out.println("选取精品橘子");
	}
}

这是一个具体的糯米糖葫芦。

public class RiceProduct extends Product {

	@Override
	void prepare() {
		System.out.println("选取精品糯米,并揉成饭团");
	}
}

这是一个简单工厂类。

public class SimpleFactory {
	// 可根据客户的不同口味需求,提供不同的糖葫芦
	public Product createProduct(int type) {
		switch (type) {
		case SimpleFactoryTest.SHANZHA:
		    // 山楂材料
			return new ShanZhaProduct(); 
		case SimpleFactoryTest.ORANGE:
		    // 橘子材料
			return new OrangeProduct();
		case SimpleFactoryTest.RICE:
		    // 糯米材料
			return new RiceProduct()default:
			return null;
		}
	}
}

我们可以看出简单工厂将对象的创建细节封装起来(符合封装变化的OO原则),使对象的具体实例化过程从ProductStore代码中删除,让ProductStore从与具体的糖葫芦的依赖中解脱

这是客户购买糖葫芦的店铺。

public class ProductStore {
	
	//持有简单工厂对象
	SimpleFactory factory;
	
	public ProductStore(SimpleFactory factory) {
		this.factory = factory;
	}
	
	//购买糖葫芦
	public void buyProduct(int type) {
		Product product = factory.createProduct(type);
		product.prepare();
		product.chuanChuan();
		product.createSweetener();
		product.castSweetenerToChuan();
		System.out.println("制作完成,欢迎您的购买");
	}
}

看完了ProductStore的代码你可能会有一个疑问,问什么简单工厂只负责了对象的创建,为什么没有把具体的制作流程都包含进简单工厂中,直接为店铺生产出一种符合客户要求的糖葫芦呢?其实如果保证制作流程不变的情况下,也可以那么做,虽然违背了要让类尽量保持单一责任的原则吧,不过不推荐那样干,毕竟简单工厂模式目的就是为了封装对象的创建

我们来测下客户购买糖葫芦

public class SimpleFactoryTest {
	
	public static final int SHANZHA = 0; // 山楂原料
	public static final int ORANGE = 1; // 橘子原料
	public static final int RICE = 2; // 糯米原料

	public static void main(String[] args) {
		ProductStore productStore = new ProductStore(new SimpleFactory());
		productStore.buyProduct(SHANZHA);
	}
}

到这里我们已经介绍完了简单工厂,相信你已经对于它有了一定的认识,虽然简单工厂的使用简单方便,但却不具备弹性,比如,如果SimpleFactory这个工厂并不能满足我的所有需求,我需要再添加一个工厂SimpleFactory二号,是不是还需要更改ProductStore的代码啊,这就违背了开放-关闭原则,我们应该针对接口编程,而不是针对这个SimpleFactory具体实现进行编程,解决办法可以是定义一个工厂接口,使ProductStore持有抽象引用。不要急,接着往下看吧,其实这种情况工厂方法为我们提供了更好的解决办法。

工厂方法模式

关于工厂方法模式的定义,我就直接引用HeadFirst了:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

还是以上面的糖葫芦为例,由于糖葫芦卖的非常火爆,所有有好多的店铺想要加盟,但是由于地理位置原因,客户的口味和运输成本的问题,一家工厂已经满足不了我们的全部需求啦,我们现在需要在加盟店本地开建工厂。

这里的需求决定要开建多家工厂,并不是说明工厂方法就得需要多个具体工厂类,只开建一家工厂是完全可以的,看完下面之后,你会发现只有一个具体创建者依然很有用。

这是一个糖葫芦店铺的抽象类。

public abstract class ProductStore {
	// 购买糖葫芦
	public void buyProduct(String type) {
		Product product = createProduct(type);
		if(product == null){
			System.out.println("当前暂不提供该种类糖葫芦");
			return;
		}
		product.prepare();
		product.chuanChuan();
		product.createSweetener();
		product.castSweetenerToChuan();
		System.out.println("制作完成,欢迎您的购买");
	}
	// 将具体的小店的实例化过程推迟到子类
	abstract Product createProduct(String type);
}

抽象的目的,就是为了让子类决定要实例化的类是哪一个,ProductStore类只依赖Product,它并不知道要操作的具体糖葫芦种类是什么,这就让产品的实现从使用中解了耦。createProduct(String type)方法,就是我们上面定义中所说的创建对象的接口。

这是一个抽象的糖葫芦类。

public abstract class Product {
	// 选取原料,包括山楂、橘子、糯米、哈密瓜、西瓜等
	abstract void prepare();
	// 穿串
	public void chuanChuan() {
		System.out.println("将选取的原料穿串");
	}
	// 熬制调料
	public void createSweetener(){
		System.out.println("熬制白糖调料");
	}
	// 浇在串上冷却
	public void castSweetenerToChuan() {
		System.out.println("将调料汁浇在串上并冷却");
	}
}

这是一个记录具体糖葫芦种类的一个枚举。

public enum ProductType {
	
	ShanZhaType("精品山楂"), OrangeType("精品橘子"), Rice("糯米")
	, Hamimelon("哈密瓜"), Watermelon("西瓜");
	
	String type;
	
	private ProductType(String type) {
		this.type = type;
	}
}

这是北方的一家加盟糖葫芦店。

public class NorthProductStore extends ProductStore {
	@Override
	Product createProduct(String type) {
		if(ProductType.ShanZhaType.type.equals(type)){
			return new ShanZhaProduct();
		}
		else if(ProductType.OrangeType.type.equals(type)){
			return new OrangeProduct();
		}
		else if(ProductType.Rice.equals(type)){
			return new RiceProduct();
		}
		return null;
	}
}

是不是看着代码非常眼熟,这不就是我们上面的简单工厂嘛,如果仔细比较你会发现还是很有区别的:简单工厂中使用的工厂是独立的类,或者是一个工厂接口的具体实现,跟ProductStore类是组合的关系,而这个NorthProductStore类是直接从ProductStore类扩展而来的

这是具体的山楂糖葫芦。

public class ShanZhaProduct extends Product{
	@Override
	void prepare() {
		System.out.println("选取精品山楂");		
	}
}

这个是具体的橘子糖葫芦.

public class OrangeProduct extends Product {
	@Override
	void prepare() {
		System.out.println("选取精品橘子");
	}
}

这是具体的糯米糖葫芦。

public class RiceProduct extends Product {
	@Override
	void prepare() {
		System.out.println("选取精品糯米,并揉成饭团");
	}
}

即使只有上面这一家加盟店是不是工厂方法模式依然很有用,它让商店和工厂中间解耦,当需要添加工厂时,又不必更改商店代码,符合对扩展开放,对修改关闭

这是西部地区的一家加盟糖葫芦店。

public class WestProductStore extends ProductStore{
	@Override
	Product createProduct(String type) {
		if(ProductType.Hamimelon.type.equals(type)){
			return new HamimelonProduct();
		}
		else if(ProductType.Watermelon.type.endsWith(type)){
			return new WatermelonProduct();
		}
		else if(ProductType.ShanZhaType.type.equals(type)){
			return new ShanZhaProduct();
		}
		return null;
	}
}

这是一个具体的哈密瓜糖葫芦。

public class HamimelonProduct extends Product {
	@Override
	void prepare() {
		System.out.println("选取精品哈密瓜");
	}
}

这是一个具体的西瓜糖葫芦。

public class WatermelonProduct extends Product {
	@Override
	void prepare() {
		System.out.println("选取精品西瓜");
	}
}

我们来测下客户购买糖葫芦。

public class FactoryMethodPatternTest {
	public static void main(String[] args) {
		// 这是北方的加盟糖葫芦店
		ProductStore pStoreNorth = new NorthProductStore();
		// 购买山楂糖葫芦
		pStoreNorth.buyProduct(ProductType.ShanZhaType.type);
		
		// 这是西方的加盟糖葫芦店
		ProductStore pStoreWest = new WestProductStore();
		// 购买哈密瓜糖葫芦
		pStoreWest.buyProduct(ProductType.Hamimelon.type);
	}
}

抽象工厂模式

关于抽象工厂模式的定义,我就直接引用Head First了:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

还是以上面的糖葫芦为例,我们知道在糖葫芦的制作过程中,包括制作糖料这个步骤,现由于加盟店的增多,在制作糖料的原料使用上出现了很多问题,为了加强管理,挽回损失,所以总部决定在加盟店本地建设原料工厂,为加盟店提供专用水,白糖和芝麻。

这是一个糖葫芦店铺的抽象类。

public abstract class ProductStore {
	//购买糖葫芦
	public void buyProduct(String type) {
		Product product = createProduct(type);
		if(product == null){
			System.out.println("当前暂不提供该种类糖葫芦");
			return;
		}
		product.prepare();
		product.chuanChuan();
		product.createSweetener();
		product.castSweetenerToChuan();
		System.out.println("制作完成,欢迎您的购买");
	}
	// 将具体的小店的实例化过程推迟到子类
	abstract Product createProduct(String type);
}

这是一个抽象的糖葫芦类。

public abstract class Product {
	// 选取原料,包括山楂、橘子、糯米、哈密瓜、西瓜等
	abstract void prepare();
	// 穿串
	public void chuanChuan() {
		System.out.println("将选取的原料穿串");
	}
	/**
	 * 熬制调料。
	 * 
	 * 

我们将创建一个抽象工厂, * 负责创建熬制糖料所需要的全部原料对象。 */ abstract void createSweetener(); // 浇在串上冷却 public void castSweetenerToChuan() { System.out.println("将调料汁浇在串上并冷却"); } }

和上面不同的是,当前类熬制调料方法是抽象方法,因为不同的加盟店可能提供的原料不同,所以熬成的调料也就会有所差异

这是一个记录糖葫芦种类的枚举。

public enum ProductType {
	
	ShanZhaType("精品山楂"), OrangeType("精品橘子"), Rice("糯米")
	, Hamimelon("哈密瓜"), Watermelon("西瓜");
	
	String type;
	
	private ProductType(String type) {
		this.type = type;
	}
}

这是一个抽象工厂。

public interface IAbstractFactory {
	// 拿到温水
	WaterRawMaterial createWater();
	// 获取白糖
	SugarRawMaterial createSugar();
	// 获取芝麻
	SesameRawMaterial createSesame();
}

为工厂定义一个接口,这个接口负责创建熬制糖料所需要的全部原料对象。当需要创建产品家族和想让制造的相关产品集合起来时,可以优先选择抽象工厂

这是一个专门给北方糖葫芦加盟店提供原料的具体工厂

public class NorthFactory implements IAbstractFactory {

	@Override
	public WaterRawMaterial createWater() {
		return new WaterRawMaterial();
	}

	@Override
	public SugarRawMaterial createSugar() {
		return new SugarRawMaterial();
	}

	@Override
	public SesameRawMaterial createSesame() {
		return new SesameRawMaterial();
	}
}

这是原料清水类。

public class WaterRawMaterial {
	public WaterRawMaterial() {
		System.out.print("加清水");
	}
}

这是原料白糖类。

public class SugarRawMaterial {
	public SugarRawMaterial(){
		System.out.print(" 加白糖  ");
	}
}

这是原料芝麻类。

public class SesameRawMaterial {
	public SesameRawMaterial() {
		System.out.println("加芝麻");
	}
}

这是北方的一家具体的加盟店。

public class NorthProductStore extends ProductStore {
	// 持有一个抽象工厂的引用
	IAbstractFactory factory;
	
	@Override
	Product createProduct(String type) {
		factory = new NorthFactory();
		if(ProductType.ShanZhaType.type.equals(type)){
			return new ShanZhaProduct(factory);
		}
		else if(ProductType.OrangeType.type.equals(type)){
			return new OrangeProduct(factory);
		}
		else if(ProductType.Rice.equals(type)){
			return new RiceProduct(factory);
		}
		return null;
	}
}

这是山楂糖葫芦。

public class ShanZhaProduct extends Product{
	IAbstractFactory factory;
	
	public ShanZhaProduct(IAbstractFactory factory) {
		this.factory = factory;
	}
	
	@Override
	void prepare() {
		System.out.println("选取精品山楂");		
	}
	
	@Override
	void createSweetener() {
		factory.createWater();
		factory.createSugar();
		factory.createSesame();
	}
}

让糖葫芦类与具体的原料类解耦。工厂方法模式通过继承,扩展自一个类,让子类决定要实例化的类是哪一个,从而达到解耦目的,而抽象工厂模式,通过对象的组合,即将定义了相关或依赖的对象家族接口,实例化之后,传入针对抽象类型所写的代码中,从而使客户从所使用的实际具体产品中解耦

这是橘子糖葫芦。

public class OrangeProduct extends Product {
	IAbstractFactory factory;
	
	public OrangeProduct(IAbstractFactory factory) {
		this.factory = factory;
	}

	@Override
	void prepare() {
		System.out.println("选取精品橘子");
	}
	
	@Override
	void createSweetener() {
		factory.createWater();
		factory.createSugar();
		factory.createSesame();
	}
}

这是糯米糖葫芦。

public class RiceProduct extends Product {
	IAbstractFactory factory;
	
	public RiceProduct(IAbstractFactory factory) {
		this.factory = factory;
	}

	@Override
	void prepare() {
		System.out.println("选取精品糯米,并揉成饭团");
	}
	
	@Override
	void createSweetener() {
		factory.createWater();
		factory.createSugar();
		factory.createSesame();
	}
}

这个例子只提供了北方糖葫芦加盟店和北方原料工厂,你可以自己轻易扩展出西方糖葫芦加盟店和西方原料工厂类,因为他们都是松耦合的,可以轻易扩展.

我们来测下客户购买糖葫芦

public class AbstractFactoryPatternTest {
	public static void main(String[] args) {
		// 创建一个北方加盟糖葫芦店
		ProductStore pStore = new NorthProductStore();
		// 购买一个橘子糖葫芦
		pStore.buyProduct(ProductType.OrangeType.type);
	}
}

你可能感兴趣的:(模式架构)