现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,都是装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时为对象动态地添加额外的行为或功能,而无需修改原始类的代码。该模式通过创建一个包装器,即装饰器,来包裹原始对象,并以透明的方式扩展其功能。装饰器和原始对象都实现了相同的接口,这使得它们可以互相替代使用。
装饰器模式适用于以下场景:
装饰器模式适用于需要在运行时动态地扩展对象功能,以及对核心对象进行修改但又不希望直接修改其代码的情况。它提供了一种灵活且无侵入性的方式来添加新功能,并允许根据需要组合多个装饰器以实现不同的行为变化
- 当你需要在不修改现有对象代码的情况下,动态地给一个对象添加新的行为或功能时,可以使用装饰器模式。它允许你通过包装对象来扩展其功能。
- 当你希望能够在运行时选择要添加的特定行为时,装饰器模式非常有用。你可以根据需要动态地组合多个装饰器,以实现不同的行为组合效果。
- 当你有一个核心对象,并且希望能够在不影响其他对象的情况下对其进行修改时,装饰器模式是一种很好的选择。由于装饰器模式使用了继承关系,因此你可以针对不同的需求创建多个具体装饰器类,并通过组合它们以满足所需的行为变化。这样就避免了直接修改核心对象的代码。
- 当你想要以层次结构的方式动态地组合对象时,可以使用装饰器模式。你可以通过创建多个具体装饰器类并将它们嵌套在一起,形成一个层次结构,从而实现更复杂的行为组合。
- 当你想要在不同的时间点为一个对象添加或删除特定行为时,装饰器模式是一个很好的选择。你可以根据需要随时添加或删除特定的装饰器,而不需要修改对象的代码。
装饰器模式中的核心角色包括:
- 抽象构件(Component):定义了原始对象和装饰器的公共接口。
- 具体构件(Concrete Component):实现了抽象构件的接口,表示原始对象。
- 装饰器(Decorator):持有一个抽象构件的引用,并实现了抽象构件接口,用于添加额外的行为或功能。
- 具体装饰器(Concrete Decorator):扩展了装饰器的功能,可以在调用原始对象的方法之前或之后执行额外的操作。
装饰器模式代码实践
用装饰模式来完成Coffee的制作具体实现
要求:Coffee有两种款式,浓Coffee和淡Coffee
利用装饰模式加料:牛奶,巧克力
涉及的所有类:
抽象构件:Coffee
java
/**
* coffee制作接口:抽象构件接口,表示咖啡
*/
ublic interface Coffee {
String getDescription();//获取对应的描述
int getPrice();//获取价格
}
具体构件:LightCoffee、StrongCoffee
java
public class LightCoffee implements Coffee{
@Override
public String getDescription() {
return "you will get a cap of LightCoffee";
}
@Override
public int getPrice() {
return 18;
}
}
/**
* 浓咖啡
*/
public class StrongCoffee implements Coffee {
@Override
public String getDescription() {
return "you will get a cap of StrongCoffee";
}
@Override
public int getPrice() {
return 20;//浓咖啡价格为十元
}
}
抽象装饰器:CoffeeDecorator
java
/**
* 实现一个抽象的Coffee装饰器
*/
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
public String getDescription() {
return coffee.getDescription();
}
public int getPrice() {
return coffee.getPrice();
}
}
具体装饰器:Chocolate、Milk、Mocha
java
public class Chocolate extends CoffeeDecorator {
public Chocolate(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " with Chocolate";
}
@Override
public int getPrice() {
return coffee.getPrice() + 12;
}
}
public class Milk extends CoffeeDecorator {
public Milk(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription()+" with Milk";
}
@Override
public int getPrice() {
return coffee.getPrice() + 8;
}
}
public class Mocha extends CoffeeDecorator{
public Mocha(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription()+" with Mocha";
}
@Override
public int getPrice() {
return coffee.getPrice() +16;
}
}
运行结果:
java
public static void main(String[] args) {
//创建一杯浓coffee
Coffee strongCoffee = new StrongCoffee();
System.out.println(strongCoffee.getDescription() + "~it cost :" + strongCoffee.getPrice());
//创建一杯带有牛奶的浓coffee
Coffee strongCoffeeWithMilk = new Milk(strongCoffee);
System.out.println(strongCoffeeWithMilk.getDescription() + "~it cost :" + strongCoffeeWithMilk.getPrice());
//创建一杯牛奶和巧克力的淡Coffee
Coffee lightCoffee = new LightCoffee();
Coffee lightCoffeeWithMilk = new Milk(lightCoffee);
Coffee lightCoffeeWithMilkAndChocolate = new Chocolate(lightCoffeeWithMilk);
System.out.println(lightCoffeeWithMilkAndChocolate.getDescription() + "~it cost :" + lightCoffeeWithMilkAndChocolate.getPrice());
}
you will get a cap of StrongCoffee~it cost :20
you will get a cap of StrongCoffee with Milk~it cost :28
you will get a cap of LightCoffee with Milk with Chocolate~it cost :38
以上示例演示了如何使用装饰器模式来制作咖啡。通过创建具体的咖啡类和装饰器类,我们可以在运行时动态地添加额外的配料或功能,而不需要修改原始咖啡类的代码。
装饰器模式的优缺点
装饰器模式的优点包括:
- 动态扩展功能:装饰器模式允许在运行时动态地为对象添加新的功能,而无需修改现有代码。这使得系统更具灵活性和可扩展性。
- 组合多个装饰器:通过使用多个装饰器,你可以在对象上组合不同的功能,以实现各种组合效果。这样可以避免类的继承关系变得过于复杂,提高了代码的可读性和可维护性。
- 分离关注点:装饰器模式将功能的添加与核心对象的实现分离开来,使得各个功能模块可以独立开发和测试。这使得代码更加清晰、易于理解和维护。
- 遵循开闭原则:装饰器模式符合开闭原则,即对扩展开放,对修改关闭。通过使用装饰器模式,你可以在不修改现有代码的情况下,通过添加新的装饰器来扩展功能。
装饰器模式的缺点包括:
- 可能产生过多的小对象:装饰器模式涉及创建多个对象,并将它们嵌套在一起,可能会导致系统中存在大量的小对象。这可能会增加内存使用和运行时开销。
- 可能增加复杂度:当装饰器的层次结构变得复杂时,理解和维护代码可能会变得困难。过多的装饰器可能会使代码变得混乱并且难以debug。
- 需要注意继承问题:装饰器模式是通过继承来实现的,因此需要特别注意父类与子类之间的关系。如果不正确地设计和使用装饰器模式,可能会导致继承关系变得复杂和脆弱。