这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战
前言
一般情况下如果我们想要对一个对象进行扩展,会使用继承或组合的方式来进行,这个扩展的动作发生在编译时,比如在继承扩展时,子类的所有对象在编译时就已经确定有什么特性。
而如果需要在运行时按照不同的情况进行不同的功能扩展,并且不会对现有对象的功能进行添加和删除,则需要用装饰器模式来实现。
装饰器模式定义
装饰器模式(Decorator Design Pattern)用于在运行时修改对象的功能。同时,同一类的其他实例将不受此影响,因此单个对象将获得修改后的行为。装饰器设计模式是结构设计模式(如适配器模式、桥接模式、组合模式)的一种,它使用抽象类或具有组合的接口来实现。
假设我们想要实现不同类型的汽车,我们可以创建接口Car来定义汽车有哪些功能(接口中的方法),然后我们可以有一个基本款汽车实现类,进一步我们可以将基本款扩展到跑车和豪华车。实现层次结构如下图所示。
但是,如果我们想在运行时得到一辆同时具有跑车和豪华车特性的汽车,那么实现就会变得复杂,如果我们想进一步指定应该首先添加哪个特性,它就会变得更加复杂。现在想象一下,如果我们有10种不同的汽车,那么使用继承和组合的实现逻辑将会变得特别难以管理。为了解决这种编程问题,我们在java中应用了装饰器模式。
实现装饰器模式一般需要以下类型的组件。
组件接口
定义要实现的方法的接口或抽象类。在我们的例子中,Car将是组件接口。
public interface Car {
public void assemble();
}
组件实现
组件接口的基本实现。我们可以将BasicCar类作为组件实现。
public class BasicCar implements Car {
@Override
public void assemble() {
System.out.print("基本款汽车.");
}
}
装饰器
Decorator类实现了组件接口,并且它与组件接口具有has-a
关系。组件变量应该可以被子装饰器类访问,因此我们将这个变量设置为protected
。
public class CarDecorator implements Car {
protected Car car;
public CarDecorator(Car c){
this.car=c;
}
@Override
public void assemble() {
this.car.assemble();
}
}
扩展基本装饰器功能并相应地修改组件行为。我们可以有具体的装饰器类,如LuxuryCar
和SportsCar
。
跑车装饰器实现:
public class SportsCar extends CarDecorator {
public SportsCar(Car c) {
super(c);
}
@Override
public void assemble(){
super.assemble();
System.out.print("添加跑车特性");
}
}
豪华车装饰器实现:
public class LuxuryCar extends CarDecorator {
public LuxuryCar(Car c) {
super(c);
}
@Override
public void assemble(){
super.assemble();
System.out.print("添加豪华车特性");
}
}
测试类:
public class DecoratorPatternTest {
public static void main(String[] args) {
Car sportsCar = new SportsCar(new BasicCar());
sportsCar.assemble();
// 可以灵活地对目标对象BasicCar进行扩展(装饰)
Car sportsLuxuryCar = new SportsCar(new LuxuryCar(new BasicCar()));
sportsLuxuryCar.assemble();
}
}
在测试代码中,我们可以对目标对象BasicCar灵活扩展,并且SportCar和LuxuryCar的顺序可以改变。
装饰器模式类图
小结
装饰器设计模式有助于提供运行时修改功能,因此更加灵活。当选择数量较多时,易于维护和扩展。
装饰器模式在Java IO类中使用较多,如FileReader、BufferedReader等。
如果对你有帮助,点个赞是对我最大的鼓励。