米脂
装饰器是扩展另一个类的功能的类。装饰器通常实现相同的接口,以便可以使用修饰的对象而不是基本对象。一个很好的例子是应用于文件或更一般地说,应用于数据流实现的压缩器和/或加密器,如@nits.kk的答案所示。在披萨的例子中,我们应该定义我们需要的行为:public interface Pizza { public String getIngredients(); // comma separated public double getTotalPrice();}比萨饼由两种主要类型的成分组成:强制性的单一基料和可选的多种配料。每种成分都有自己的价格。public class PizzaIngredient { private double getPrice() { return 0.0; }}披萨基地本身就是最简单的披萨,因此它必须实现接口。它有一个大小作为其属性(当然还有价格)。我们可以将一个大小实现为一个单独的类,但我不认为它是合理的 - 它不够通用,无法在披萨世界之外有用,也不够复杂,不值得拥有自己的接口。Pizzapublic class PizzaBase extends PizzaIngredient implements Pizza { public PizzaBase(String size) { this.size = size; } public String getIngredients() { return size + " base"; // the only ingredient is this base } public double getTotalPrice() { return getPrice(); // the base-only pizza costs the base cost } private double getPrice() { if(size == "small") return 2.0; if(size == "medium") return 2.5; return 3.0; // large and undefined } private final String size;}现在我们需要浇头。它们将被添加到比萨饼的顶部作为装饰器:比萨饼加浇头也是比萨饼,所以最上面的顶部将代表整个构图。这种比萨饼的配料表是基础比萨饼的成分清单加上其最上面的浇头的名称。同样,总价也是如此。public class PizzaTopping extends PizzaIngredient implements Pizza { public PizzaTopping(String name, Pizza pizza) { this.name = name; this.pizza = pizza; } public String getIngredients() { return pizza.getIngredients() + ", " + getName(); } public double getTotalPrice() { return pizza.getTotalPrice() + getPrice(); } public String getName() { return name; } private final String name; private final Pizza pizza;}让我们定义一些混凝土浇头:public class MozzarellaTopping extends PizzaTopping { public MozzarellaTopping(Pizza pizza) { super("mozzarella", pizza); } private double getPrice() { return 0.5; }}public class MushroomTopping extends PizzaTopping { public MushroomTopping(Pizza pizza) { super("mushroom", pizza); } private double getPrice() { return 2.0; }}public class PepperoniTopping extends PizzaTopping { public PepperoniTopping(Pizza pizza) { super("pepperoni", pizza); } private double getPrice() { return 1.5; }}public class GreenOliveTopping extends PizzaTopping { public GreenOliveTopping(Pizza pizza) { super("green olive", pizza); } private double getPrice() { return 1.2; }}好吧,这是很多课程;但是我们什么时候需要它们中的哪一个?在这里,一个工厂加入了团队。工厂是用于创建某些类的对象的类。它用于在幕后隐藏创建细节,特别是当创建的对象很复杂或具有不同的具体类时。当生成的对象创建为独立实体时,工厂类可以只是一个包含静态方法的命名空间。OTOH,如果对象是在某个上下文中创建的,则工厂可以是与上下文关联的对象(例如,参数化),并在创建过程中使用该上下文。我们可以根据用户输入,使用工厂随意制作比萨饼配料。大多数配料都是浇头,必须涂在已经存在的比萨饼之上,所以让我们把比萨饼送到工厂,这样就可以收到装饰好的比萨饼。特殊情况是创建比萨饼底,该比萨饼底不应用于另一种比萨饼;在这种情况下,参数被忽略,因此我们可以传递 。pizzanullpublic class PizzaFactory { public static Pizza getPizza(Pizza pizza, String name) { if ( name.equals("small") || name.equals("medium") || name.equals("large") ) return new PizzaBase(name); else if ( name.equals("mozzarella") ) return new MozzarellaTopping(pizza); // add topping to the pizza else if ( name.equals("mushroom") ) return new MushroomTopping(pizza); else if ( name.equals("pepperoni") ) return new PepperoniTopping(pizza); else if ( name.equals("green olive") ) return new GreenOliveTopping(pizza); return null; }}现在我们准备制作我们的比萨饼。class PizzaTest { public static void main(String[] args) { DecimalFormat priceFormat = new DecimalFormat("#.##"); Pizza pizza; pizza = PizzaFactory.getPizza(null, "small"); System.out.println("The small pizza is: " + pizza.getIngredients()); System.out.println("It costs " + priceFormat.format(pizza.getTotalCost())); pizza = PizzaFactory.getPizza(null, "medium"); pizza = PizzaFactory.getPizza(pizza, "mozzarella"); pizza = PizzaFactory.getPizza(pizza, "green olive"); System.out.println("The medium pizza is: " + pizza.getIngredients()); System.out.println("It costs " + priceFormat.format(pizza.getTotalCost())); String largePizzaOrder[] = { "large", "mozzarella", "pepperoni", "mushroom", "mozzarella", "green olive" }; pizza = null; for (String cmd : largePizzaOrder) pizza = PizzaFactory.getPizza(pizza, cmd); System.out.println("The large pizza is: " + pizza.getIngredients()); System.out.println("It costs " + priceFormat.format(pizza.getTotalCost())); }}警告:上面的代码中有一些陷阱和快捷方式。最重要的是缺乏对输入的验证:当意外的命令到达时,工厂将返回,这将导致将来使用或 时崩溃。nullgetIngredients()getTotalCost()另一个是将价格硬编码为具体的类。实际的解决方案必须使用一些价目表,并在成分创建时(并将提取的价格存储在成分对象中)或使用时获取价格,即在方法中(这需要从比萨饼的成分中访问价格表)。getCost()