装饰器模式
它允许你在不改变对象结构的情况下,动态地将新功能附加到对象上。
结构:
- 抽象组件(Component):定义了原始对象和装饰器对象的公共接口或抽象类,可以是具体组件类的父类或接口。
- 具体组件(Concrete Component):是被装饰的原始对象,它定义了需要添加新功能的对象。
- 抽象装饰器(Decorator):继承自抽象组件,它包含了一个抽象组件对象,并定义了与抽象组件相同的接口,同时可以通过组合方式持有其他装饰器对象。
- 具体装饰器(Concrete Decorator):实现了抽象装饰器的接口,负责向抽象组件添加新的功能。具体装饰器通常会在调用原始对象的方法之前或之后执行自己的操作。
图例:
平常假如要加一个配料,都需要修改餐品的源代码,但是随着配料的增多, 类会变得越来越庞大,等下类爆炸了。
public class Main {
public static void main(String[] args) {
FriedNoodles friedNoodles = new FriedNoodles();
friedNoodles.addBacon();
friedNoodles.addEgg();
friedNoodles.addFish();
System.out.println("Cost: $" + friedNoodles.getCost());
}
}
案例:
假设有一个简单的咖啡店系统,其中有一个 Coffee
接口表示咖啡,它有一个方法 getCost()
来获取咖啡的价格。现在我们要给咖啡添加一些额外的配料,比如牛奶、摩卡和奶泡。
// 咖啡接口
interface Coffee {
double getCost();
}
// 具体咖啡类
class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 1.0;
}
}
// 装饰者抽象类
abstract class CoffeeDecorator implements Coffee {
protected final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
public double getCost() {
return decoratedCoffee.getCost();
}
}
// 具体装饰者类
class Milk extends CoffeeDecorator {
public Milk(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 0.5;
}
}
class Mocha extends CoffeeDecorator {
public Mocha(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 1.0;
}
}
class Foam extends CoffeeDecorator {
public Foam(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
@Override
public double getCost() {
return super.getCost() + 0.3;
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
coffee = new Milk(coffee);
coffee = new Mocha(coffee);
coffee = new Foam(coffee);
System.out.println("Cost: $" + coffee.getCost());
}
}
通过组合不同的装饰者,可以在不改变原有咖啡对象的情况下,动态地添加额外的功能和费用。
使用场景:
- 动态地给对象添加功能:当需要给对象动态地添加一些额外的功能,而且这些功能可以独立于该对象的创建。
-
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
不能采用继承的情况主要有两类:
- 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
- 第二类是因为类定义不能继承(如final类)