介绍
装饰器设计模式是一种结构型设计模式,它允许动态地将行为添加到对象中,而无需在对象的类中使用子类化。它允许您通过将对象封装在一个具有新行为的对象中来动态地修改对象的行为。
这种模式是基于组合的思想,而不是继承。
可动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
在这篇文章中,我们将深入探讨Java中的装饰器设计模式,包括模式的定义、使用场景和一个实际的案例。
定义
装饰器模式可以理解为在不改变原有对象结构的前提下,通过动态地为对象添加新的行为来扩展对象的功能。这种模式通过将对象包装在装饰器对象中来实现。
使用场景
装饰器设计模式在以下情况下非常有用:
- 需要动态地向对象添加行为,而不是静态地添加它们。
- 需要通过将许多小型对象组合成更大的对象来创建复杂的对象。
- 需要增强现有对象的功能,而不是创建新的子类来实现它。
装饰器模式的结构
1、Component,抽象构件
Component是一个接口或者抽象类,是定义我们最核心的对象,也可以说是最原始的对象,比如街边小吃;
2、ConcreteComponent,具体构件,或者基础构件
ConcreteComponent是最核心、最原始、最基本的接口或抽象类Component的实现,可以单独用,也可将其进行装饰;
3、Decorator,装饰角色
一般是一个抽象类,继承自或实现Component,在它的属性里面有一个变量指向Component抽象构件,这是装饰器最关键的地方。
4、ConcreteDecorator,具体装饰角色
牛奶咖啡和加糖牛奶咖啡是两个具体的装饰类,它们可以把基础构件装饰成新的东西,比如把一个普通的咖啡装饰成加牛奶、加糖的咖啡。
优缺点:
优点:
可以在不改变现有代码的情况下向对象添加功能,从而使代码更加灵活和可扩展。
可以通过组合不同的装饰器来创建具有不同功能集的对象,这样可以避免类爆炸问题。
装饰器可以嵌套使用,使得功能的组合更加灵活,可以根据需要组合不同的功能。
缺点:
使用装饰器会增加代码的复杂性,尤其是在需要嵌套多个装饰器时。
可能会导致运行时的性能损失,因为每个装饰器都需要在运行时动态地修改对象的行为。
如果使用不当,装饰器可能会导致代码可读性和维护性下降,特别是在使用多个装饰器时。
咖啡案例
首先,我们定义一个抽象的咖啡类,它有一个名字和一个价格:
public abstract class Coffee { protected String name; protected double price; public String getName() { return name; } public double getPrice() { return price; } }
然后,我们定义一个具体的咖啡类,它继承自抽象咖啡类:
public class Espresso extends Coffee { public Espresso() { name = "浓咖啡"; price = 2.0; } }
现在,我们可以使用装饰者模式来为咖啡添加配料。我们首先定义一个抽象的配料装饰器类
public abstract class CoffeeDecorator extends Coffee { protected Coffee coffee; public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; } public String getName() { return coffee.getName() + " + " + name; } public double getPrice() { return coffee.getPrice() + price; } }
这个类包含了一个抽象的咖啡对象,它将被添加配料。然后,我们可以定义具体的配料装饰器类,它们
继承自配料装饰器类:
public class Milk extends CoffeeDecorator { public Milk(Coffee coffee) { super(coffee); name = "牛奶"; price = 0.5; } } public class Sugar extends CoffeeDecorator { public Sugar(Coffee coffee) { super(coffee); name = "加糖"; price = 0.3; } } public class Caramel extends CoffeeDecorator { public Caramel(Coffee coffee) { super(coffee); name = "焦糖"; price = 0.7; } }
现在,我们可以创建具体的咖啡对象,并添加各种配料:
Coffee espresso = new Espresso(); espresso = new Milk(espresso); espresso = new Sugar(espresso); espresso = new Caramel(espresso); System.out.println(espresso.getName() + ": " + espresso.getPrice());
输出结果将是:
装饰器模式在Java I/O系统中的实现
Java I/O类库就采用到了装饰器设计模式,以InputStream为例简单说明一下
InputStream作为抽象构件,其下面大约有如下几种具体基础构件,从不同的数据源产生输入:
- ByteArrayInputStream,从字节数组产生输入;
- FileInputStream,从文件产生输入;
- StringBufferInputStream,从String对象产生输入
- PipedInputStream,从管道产生输入;
- SequenceInputStream,可将其他流收集合并到一个流内;
FilterInputStream作为装饰器在JDK中是一个普通类,其下面有多个具体装饰器
比如BufferedInputStream、DataInputStream等。我们以BufferedInputStream为例,使用它就是避免每次读取时都进行实际的写操作,起着缓冲作用。