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

2014-11-23 23:41:32 · 作者: · 浏览: 3
事实上有一个OO设计原则就正式阐明了这一点;这个原则甚至还有一个又响亮又正式的名称:依赖倒置原则(Dependency Inversion Principle)。 依赖倒置原则:要依赖抽象,不要依赖具体类。 首先,这个原则听起来很像是“针对接口编程,不针对实现编程”,不是吗?的确很相似,然而这里更强调抽象。这个原则说明了:不能让高层组件依赖低层组件,而且,不管高层或者低层组件,”两者“都应该依赖于抽象。 所谓”高层“组件,是由其他低层组件定义其行为的类。例如,PizzaStore是个高层组件,因为它的行为是由Pizza定义的:PizzaStore创建所有不同的Pizza对象,准备、烘烤、切片、装盒;而Pizza本身属于低层组件。 这到底是什么意思? 这个嘛,让我们再看一次上文创建这类和产品类的关系,PizzaStore是”高层组件”,而Pizza实现是“低层组件”,很清楚地,PizzaStore依赖这些具体Pizza类。
现在,这个原则告诉我们,应该重写代码以便于我们依赖抽象类,而不依赖具体类。对于高层及低层模块都应该如此。
依赖倒置原则,究竟倒置在哪里? 在依赖倒置原则中的倒置指的是和一般OO设计的思考方法完全相反。低层组件居然依赖高层的抽象,同样地,高层组件现在也依赖相同的抽象。前面的依赖是从上而下的,现在却倒置了,而且高层与低层模块现在都依赖这个抽象。
几个指导方针帮助你遵循此原则: 1、变量不可以持有具体类的引用。如果使用new,就会持有具体类的引用。你可以改用工厂来避开这样的做法。 2、不要让类派生自具体类。如果派生自具体类,你就会依赖具体类。请派生自一个抽象(接口或者抽象类)。 3、不要覆盖基类中已实现的方法。如果覆盖基类中已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享。
应该尽量达到这个原则,而不是随时都要遵循这个原则。我们都很清楚,任何java程序都有违反这些指导方针的地方。 但是,如果你深入体验这些方针,将这些方针内化成你思考的一部分,那么在设计时,你将指导何时有足够的理由违反这样的原则。比如说,如果有一个不像是会改变的类,那么在代码中直接实例化具体类也就没什么大碍。比如实例化字符串对象。 另一方面,如果有个类可能改变,你可以采用一些好技巧(例如工厂方法)来封装变化。
再回到Pizza店 现在,对象村Pizza店的成功关键在于新鲜、高质量的原料,而且通过导入新的框架,加盟店将遵循你的流程,但是有一些加盟店,使用低价原料来增加利润。你必须采取一些手段,以免长此以往毁了对象村的品牌。 你打算建造一家生产原料的工厂,并将原料运送到各家加盟店。加盟店坐落在不同的区域,每个地区的原料是不一样的,所以对于不同的地区(纽约、芝加哥),你准备了两组不同的原料。
建造原料工厂
public interface PizzaIngredientFactory {
	//在接口中,每个原料都有一个对应的方法创建该原料
	//如果每个工厂实例内都有某一种通用的“机制”需要实现,就可以把这个例子改写成抽象类
	public Dough createDough();
	public Sauce createSauce();
	public Cheese createCheese();
	public Veggies[] createVeggies();
	public Pepperoni createPepperoni();
	public Clams createClam();
 
}

纽约原料工厂
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
	//对于原料家族内的每一种原料,我们都提供了纽约的版本
	public Dough createDough() {
		return new ThinCrustDough();
	}
 
	public Sauce createSauce() {
		return new MarinaraSauce();
	}
 
	public Cheese createCheese() {
		return new ReggianoCheese();
	}
 
	public Veggies[] createVeggies() {
		Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
		return veggies;
	}
 
	public Pepperoni createPepperoni() {
		return new SlicedPepperoni();
	}

	public Clams createClam() {
		return new FreshClams();
	}
}

重做Pizza类
public abstract class Pizza {
	String name;

	//每个Pizza都有一组在准备时会用到的原料
	Dough dough;
	Sauce sauce;
	Veggies veggies[];
	Cheese cheese;
	Pepperoni pepperoni;
	Clams clam;

	//现在把prepare()方法生命诚抽象。在这个方法中,我们需要收集披萨所需的原料,而这些原料当然是来自原料工厂了。
	abstract void prepare();

	void bake() {
		System.out.println("Bake for 25 minutes at 350");
	}

	void cut() {
		System.out.println("Cutting the pizza into diagonal slices");
	}

	void box() {
		System.out.println("Place pizza in official PizzaStore box");
	}

	void setName(String name) {
		this.name = name;
	}

	String getName() {
		return name;
	}

	public String toString() {
		StringBuffer result = new StringBuffer();
		result.append("---- " + name + " ----\n");
		if (dough != null) {
			result.append(dough);
			result.append("\n");
		}
		if (sauce != null) {
			result.append(sauce);
			result.append("\n");
		}
		if (cheese != null) {
			result.append(cheese);
			result.append("\n");
		}
		if (veggies != null) {
			for (int i = 0; i < veggies.length; i++) {
				result.append(veggies[i]);
				if (i < veggies.length-1) {
					result.append(", ");
				}
			}
			result.append("\n");
		}
		if (clam != null) {
			result.append(clam);
			result.append("\n");
		}
		if (pepperoni != null) {
			result.append(pepperoni);
			result.append("\n");
		}
		return result.toString();
	}
}

现在我们不需要设计两个不同的类来处理不同风味的Pizza了,让原料工厂处理这种区域差异就可以了。下面是CheesePizza
public class Cheese