现在,这个原则告诉我们,应该重写代码以便于我们依赖抽象类,而不依赖具体类。对于高层及低层模块都应该如此。
依赖倒置原则,究竟倒置在哪里? 在依赖倒置原则中的倒置指的是和一般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