4 回答
TA贡献1836条经验 获得超3个赞
装饰器是扩展另一个类的功能的类。装饰器通常实现相同的接口,以便可以使用修饰的对象而不是基本对象。一个很好的例子是应用于文件或更一般地说,应用于数据流实现的压缩器和/或加密器,如@nits.kk的答案所示。
在披萨的例子中,我们应该定义我们需要的行为:
public interface Pizza {
public String getIngredients(); // comma separated
public double getTotalPrice();
}
比萨饼由两种主要类型的成分组成:强制性的单一基料和可选的多种配料。每种成分都有自己的价格。
public class PizzaIngredient {
private double getPrice() {
return 0.0;
}
}
披萨基地本身就是最简单的披萨,因此它必须实现接口。它有一个大小作为其属性(当然还有价格)。我们可以将一个大小实现为一个单独的类,但我不认为它是合理的 - 它不够通用,无法在披萨世界之外有用,也不够复杂,不值得拥有自己的接口。Pizza
public 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,如果对象是在某个上下文中创建的,则工厂可以是与上下文关联的对象(例如,参数化),并在创建过程中使用该上下文。
我们可以根据用户输入,使用工厂随意制作比萨饼配料。大多数配料都是浇头,必须涂在已经存在的比萨饼之上,所以让我们把比萨饼送到工厂,这样就可以收到装饰好的比萨饼。特殊情况是创建比萨饼底,该比萨饼底不应用于另一种比萨饼;在这种情况下,参数被忽略,因此我们可以传递 。pizzanull
public 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()
TA贡献1866条经验 获得超5个赞
让我们看看下面的几点开始
装饰者图案以其最纯粹的形式打算.
enhance existing behavior of an object at run time without destroying the existing interface of the object
装饰意味着增强物体的现有行为。
装饰对象具有与被修饰的基本对象相同的(基本)接口。
问题 : 对象从其类(编译时)中分离出来。现在,您将如何继续增强行为?
答:通过使用装饰器模式也称为包装器。
例如:你有一个可以加密的文件,假设加密的方法目前是5个,结果将是加密的文件。可以再次加密加密加密的文件。此外,让我们假设有5种方法可以压缩文件,以后也可以增加。一个文件可以通过方法EA加密,然后可以通过方法ZA压缩,然后再次可以通过方法EB加密,类似的序列可以产生不同的结果文件。
其中一个好方法是如下。
public class TextFile{
public void create(){/*somecode*/};
public void format(){//code for default plain text};
}
public class AEncryptedFile extends TextFile{
private TextFile wrapped;
public AEncryptedFile(TextFile file){
this.wrapped = file;
}
public void format(){
super.format();
//add enhacements for encryption type A
}
}
public class BEncryptedFile extends TextFile{
private TextFile wrapped;
public BEncryptedFile(TextFile file){
this.wrapped = file;
}
public void format(){
super.format();
//add enhacements for encryption type B
}
}
public class AZippedFile extends TextFile{
private TextFile wrapped;
public BEncryptedFile(TextFile file){
this.wrapped = file;
}
public void format(){
super.format();
//add enhacements for zip type A
}
}
public class BZippedFile extends TextFile{
private TextFile wrapped;
public BEncryptedFile(TextFile file){
this.wrapped = file;
}
public void format(){
super.format();
//add enhacements for zip type B
}
}
public void UserClass{
public static void main(String[] args){
TextFile file = new BZippedFile(new AEncryptedFile(new TextFile()));
file.format();
}
在上面的示例代码中,可以说
TextFile的一个对象已被AEncryptedFile对象修饰(通过包装),该对象由BZippedFile进一步修饰,在每个这些装饰中,对现有对象进行了额外的增强
这样,TextFile的现有对象可以在运行时传递给各种方法,并且可以通过将其包装在TextFile子类型的另一个对象中来修饰该对象。
注意:装饰器模式实现具有LinkedList的结构。
TA贡献1824条经验 获得超6个赞
您对装饰器图案目的的期望/理解可能略有不同。装饰器模式旨在包装一组现有功能,以便除了已经存在的功能之外,还提供一些新功能。
一个更好的披萨示例是参加披萨课程,它可以执行以下操作:
提供比萨饼
提供软饮料
然后尝试添加可以供应沙拉的功能。因此,此披萨类的简化版本可能如下所示:
public class Pizzeria {
public String orderPizza() {
System.out.println("you ordered a pizza");
}
public String orderSoftDrink() {
System.out.println("you ordered a soft drink");
}
}
为了在这里实现装饰器模式,我们包装现有的类,然后添加一些新函数公共字符串订单Pizza() { System.out.println(“you order a pizza”); } public String orderSoftDrink() { System.out.println(“you order a soft drink”); } ality:Pizzeria
public class NewPizzeria {
private Pizzeria pizzeria;
public NewPizzeria() {
pizzeria = new Pizzeria();
}
public String orderPizza() {
pizzeria.orderPizza();
}
public String orderSoftDrink() {
pizzeria.orderSoftDrink();
}
public String orderSalad() {
System.out.println("you ordered a salad");
}
}
这里的关键点是类“拥有”自己的对象。在大多数情况下,它只是重新定位已经具有的相同功能。但是,它还增加了一些自己的新功能。NewPizzeriaPizzeriaPizzeria
装饰器设计模式在已经存在一个类的情况下很有用,该类主要适合您的需求,但是您需要其他东西并且您也无法重写该类(例如,因为它是某些库的一部分)。在这种情况下,包装该类并使用装饰器模式是一个不错的选择。
TA贡献1854条经验 获得超8个赞
概念方面,在装饰器模式中,一个处理的输出作为另一个处理的输入。
因此,在您的情况下,它应该像这样:
getToppingFoo(getToppingBar(...(getXBaseSizePizzaCost())
它解析为:
FooToppingCost + (BarToppingCost + ... ( Cost of pizza with base of X size )
此外,您可以定义一个工厂类来获取各种大小的对象的 Pizza,例如标准、中型、大型。无论你选择哪种语言,逻辑都是一样的。
添加回答
举报