上篇博文是对工厂模式的一点理解和示例演示。后续发现其中使用的例子可以使用装饰者模式进行优化。
装饰者模式:23种设计模式之一,英文叫DecoratorPattern,又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
所谓装饰者,可以在不改变原有代码的前提,拓展一个对象的功能。
先前的项目中有个pizza。
pizza:不同的pizza继承pizza超类,并拓展超类,加上不同的材料成为不同的pizza(番茄肉饼pizza,芝士pizza)
但是这样子就存在一个明显的不足,因为pizza的种类从某种意义上来说应该是可以无限拓展了,可以引入不同的材料,更新款式。
分析pizza类的组成,发现其很适合采用装饰者模式。
Pizza有最基本的Dough(面饼),加上诸如芝士、番茄、火腿等佐料组合而成,可以把面饼作为最基本的被装饰者,而各种佐料作为装饰者,通过采用不同佐料来修饰面饼来达到拓展面饼变成不同pizza的目的。
1、首先,使用一个超类,是的装饰者和被装饰者都继承此类,目的是保持后面对象被装饰者修饰了之后仍然属于同一个超类对象。
/*
* 原材料的超类
*/
public abstract class Ingredient {
protected String name = "Unknown ingredient"; //材料名称
protected String description = "Unknown ingredient"; //说明
protected Double price = 0.0; //材料价格
String getName() {
return name;
}
public abstract double cost();
public String getDescription(){
return description;
}
void setPrice(Double price) {
this.price = price;
};
}
2、创建被装饰者,其具有材料超类的属性。
/*
* 面团的超类
*/
public abstract class Dough extends Ingredient{
public Dough(String name) {
this.name = name;
this.description = "SimpleDough";
}
}
3、创建修饰者超类,修饰者超类定义了一个修饰者该有的属性,其需要维护一个被修饰者对象。
/*
* 修饰者 佐料
*/
public abstract class Jardiniere extends Ingredient {
Ingredient ingredient;
public Jardiniere(String name, Ingredient ingredient) {
super();
this.name = name;
this.ingredient = ingredient;
}
@Override
public double cost() {
// TODO Auto-generated method stub
return ingredient.cost() + this.price;
}
@Override
public String getDescription(){
return ingredient.getDescription() + ", " + this.name;
};
@Override
public void setPrice(Double price) {
this.price = price;
}
}
注意修饰者类重定义了cost方法和getDescription方法。因为当增加一个佐料的时候,这块pizza的价格是在原先的价格上进行累加,description的原理类似。
4、创建各种佐料,如cheese(芝士)
/*
* 奶酪
*/
public abstract class Cheese extends Jardiniere{
public Cheese(String name, Ingredient ingredient) {
super(name, ingredient);
}
}
5、材料都准备好了,使用装饰者模式来创建pizza(此处还使用了工厂模式)
/*
* the Ingredient factory of NewYork
* use ThinCrustDough, MarinaraSauce, ReggianoCheese, Garlic and so on
*/
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
// TODO Auto-generated method stub
System.out.println("拿点从NewYork的pizza原料工厂买来的薄皮面团");
return new ThinCrustDough();
}
@Override
public Sauce createSauce(Ingredient ingredient) {
// TODO Auto-generated method stub
System.out.println("拿点从NewYork的pizza原料工厂买来的海鲜酱");
return new MarinaraSauce(ingredient);
}
@Override
public Cheese createCheese(Ingredient ingredient) {
// TODO Auto-generated method stub
System.out.println("拿点从NewYork的pizza原料工厂买来的巴马干酪");
return new ReggianoCheese(ingredient);
}
@Override
public Veggies createVeggies(Ingredient ingredient) {
// TODO Auto-generated method stub
System.out.println("来点蔬菜,蒜头,洋葱,蘑菇,红辣椒");
Veggies veggies = new Garlic(ingredient);
veggies = new Onion(veggies);
veggies = new Mushroom(veggies);
veggies = new RedPepper(veggies);
return veggies;
}
}
6、我们看看如何使用修饰者来生产一个pizza
/*
* 芝士pizza
*/
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
super();
this.ingredientFactory = ingredientFactory;
}
@Override
public void prepare() {
// TODO Auto-generated method stub
System.out.println("准备"+name+"所需原材料 ");
Ingredient ingredient = ingredientFactory.createDough();
ingredient = ingredientFactory.createSauce(ingredient);
ingredient = ingredientFactory.createCheese(ingredient);
this.ingredient = ingredient;
}
}
这里用到了工厂模式,但是不管这个先,我们看看如何使用修饰者来修饰一个对象。
制作cheesePizza时,我们需要创建一个面饼对象(createDough),然后使用海鲜酱(createSauce)和芝士(createCheese)作为佐料。
注意1:使用createSauce时,实际上是调用NYPizzaIngredientFactory对象新建了一个Sauce类(这就是装饰者模式),并将被装饰者(面饼对象)作为参数传入。注意2:因为所有装饰者、被装饰者都有一个共同的超类,可以保证对象被装饰后不会发生类型变化。
完整代码见:https://github.com/markey92/HeadFirstPizza/tree/PizzaUseDecoratorPattern