设计模式笔记04-工厂模式(一)

2014-11-23 23:41:32 · 作者: · 浏览: 0

设计模式笔记04-工厂模式


1 引言


除了使用new操作符之外,还有更多制造对象的方法。你将了解到实例化这个活动不应该总是公开地进行,也会认识到初始化经常造成耦合问题。你不希望这样,对吧?读下去,你将了解工厂模式如何从复杂的依赖中帮你脱困。

2 正文


2.1 new有什么不对劲


当有一群相关的具体类时,通常会写出这样的代码:
Duck duck;

if (picnic)
{
    duck = new MallardDuck();
}
else if (hunting)
{
    duck = new DecoyDuck();
}
else if (inBathTub)
{
    duck = new RubberDuck();
}

这里有一些要实例化的具体类,究竟实例化哪个类,要在运行时由一些条件决定。 当看到这样的代码,一旦有变化或扩展,就必须重新打开这段代码进行检查和修改。通常这样修改过的代码将造成部分系统更难维护和更新,而且更容易犯错。
但是,总是要创建对象吧!而Java只提供一个new关键词创建对象,不是吗?还能有些什么? 在技术上,new没有错,毕竟这是Java的基础部分。真正的犯人是我们的老朋友“change",以及它是如何影响new的使用的。 针对接口 编程,可以隔离掉以后系统可能发生的一大堆改变。为什么呢?如果代码是针对接口而写,那么通过多态,它可以与任何新类实现该接口。但是,当代码使用大量的具体类时,等于是自找麻烦,因为一旦加入新的具体类,就必须改变代码。也就是说,你的代码并非”对修改关闭“。想用新的具体类型来扩展代码,必须重新打开它。 所以,该怎么办?当遇到这样的问题时,就应该回到OO设计原则去寻找线索。别忘了,我们的第一个原则用来处理改变,并帮助我们”找出会变化的方面,把它们从不变的部分分离出来“。

2.2 识别变化的部分


假设你又一个披萨店,身为对乡村内最先进的披萨主任,你的代码可能这么写
	public Pizza orderPizza(String type)
	{
		Pizza pizza;
		
		//根据顾客的口味匹配不同种类的披萨
		//问题出现了,随着时间的变化,披萨的菜单会改变,这里就必须已改再改
		if (type.equals("cheese"))
		{
			pizza = new CheesePizza();
		}
		else if
		{
			
		}
		
		//这个是我们不想改变的地方。因为披萨的准备、烘烤、切片、包装,多年来都保持不变,
		//所以这部分代码不会改变,只有发生这些动作的披萨会改变
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}

很明显,如果实例化”某些“具体类,将使orderPizza()出问题,而且也无法让orderPizza()对修改关闭;但是,现在我们已经知道哪些会改变,哪些不会改变,该是试用封装的时候了。

2.3 建立一个简单披萨工厂


首先,把创建对象的代码从orderPizza()中抽离,然后把这部分的代码搬到另一个对象中,这个新对象只管如何创建披萨。如果任何对象想要创建披萨,找它就对了。
public class SimplePizzaFactory {
	//SimplePizzaFactory只做一件事情,帮它的客户创建Pizza
	//首先,在这个工厂内定义一个createPizza()方法。所有客户用这个方法来实例化新对象
	public Pizza createPizza(String type) {
		Pizza pizza = null;
		//这个代码没有什么变动,和原本orderPizza()方法中的代码一样,依然是以pizza的类型为参数
		if (type.equals("cheese")) {
			pizza = new CheesePizza();
		} else if (type.equals("pepperoni")) {
			pizza = new PepperoniPizza();
		} else if (type.equals("clam")) {
			pizza = new ClamPizza();
		} else if (type.equals("veggie")) {
			pizza = new VeggiePizza();
		}
		return pizza;
	}
}

除此之外,利用静态方法定义一个简单的工厂,这是很常见的技巧,常被称为静态工厂。为何使用静态方法?因为不需要使用创建对象的方法来实例化对象。但请记住,这也有缺点,不能通过继承来改变创建方法的行为。
重做PizzaStore类
public class PizzaStore {
	//为PizzaStore加上一个对SimplePizzaFactory的引用
	SimplePizzaFactory factory;
 
	//PizzaStore的构造器,需要一个工厂作为参数
	public PizzaStore(SimplePizzaFactory factory) { 
		this.factory = factory;
	}
 
	public Pizza orderPizza(String type) {
		Pizza pizza;
		
		//而orderPizza()方法通过简单传入订单类型来使用工厂创建pizza
		//请注意,我们把new操作符替换成工厂对象的创建方法,这里不再使用具体实例化!
		pizza = factory.createPizza(type);
 
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();

		return pizza;
	}

}

2.4 定义简单工厂


简单工厂其实不是一个设计模式,反而比较像是一种编程习惯。有些开发人员的确是把这个编程习惯误认为是”工厂模式“(Factory Patter)。 不要因为简单工厂不是一个”真正的“设计模式,就忽略了他的用法。谢谢简单工厂来为我们暖身。接下来登场的是两个重量级的模式,他们都是工厂。

2.5 工厂方法模式


对象村Pizza店经营又称,现在大家都希望对象村Pizza点能够在自家附近有加盟店。 其中一件加盟店希望工厂能制造纽约风味的Pizza:薄饼、美味的酱料和少量的芝士;另一家加盟店希望能制造芝加哥风味的Pizza:厚饼、重味的酱料和大量的芝士。 如果利用SimplePizzaFactory,写出三种不用的工厂,那么各地的加盟店都有自己适合工厂可以使用,这是一种做法。 在 推广SimpleFactory时,你发现加盟店的确是你采用你的工厂创建Pizza,但是其他部分,却开始采用他们自创的流程:烘烤的做法有些差异、不要切片、使用其他厂商的盒子。 再想想这个问题,你真的希望能够建立一个框架,把加盟店和创建pizza绑在一起的同时又保持一定的弹性。

工厂方法类的设计
public abstract class PizzaStore {
 
	/*
	 * 工厂方法用来处理对象的创建,并将这样的行为封装在子类中。
	 * 这样,客户程序中关于超类的代码就和子类对象创建代码