装饰模式
本篇装饰模式,借鉴参考《大话设计模式》中的例子,使用了 Java 来进行简单的实现。
一个可以给人搭配不同的衣服的系统,比如类型 QQ、网络游戏或论坛都有的 Avatar 系统。
普通实现
根据简单工厂思想,可以将题目抽象成人类、服饰、每个部位的具体穿着对象。
先创建一个 Person 类
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public void show() {
System.out.printf("装扮的%s", name);
}
}
再创建一个 Finery 抽象类
public abstract class Finery {
public abstract void show();
}
此 Finery 类中定义了一个抽象方法 show() ,子类通过重写 show() 方法,实现各自的功能。
创建具体的服装子类
// 垮裤
public class BigTrouser extends Finery {
@Override
public void show() {
System.out.print("垮裤 ");
}
}
// 皮鞋
public class LeatherShoes extends Finery {
@Override
public void show() {
System.out.print("皮鞋 ");
}
}
// 破球鞋
public class Sneakers extends Finery {
@Override
public void show() {
System.out.print("破球鞋 ");
}
}
// 西装
public class Suit extends Finery {
@Override
public void show() {
System.out.print("西装 ");
}
}
// 领带
public class Tie extends Finery {
@Override
public void show() {
System.out.print("领带 ");
}
}
// T恤
public class TShirt extends Finery {
@Override
public void show() {
System.out.print("T恤 ");
}
}
模拟客户端
public static void main(String[]args){
Person person=new Person("小明");
System.out.println("\n第一种装扮");
Finery tShirt=new TShirt();
Finery bigTrouser=new BigTrouser();
Finery sneakers=new Sneakers();
tShirt.show();
bigTrouser.show();
sneakers.show();
person.show();
System.out.println("\n\n第二种装扮");
Finery suit=new Suit();
Finery tie=new Tie();
Finery leatherShoes=new LeatherShoes();
suit.show();
tie.show();
leatherShoes.show();
person.show();
System.out.println();
}
输出
第一种装扮
T恤 垮裤 破球鞋 装扮的小明
第二种装扮
西装 领带 皮鞋 装扮的小明
以上便是文首例子的简单实现了,可以观察一下有什么问题。
衣服的种类多不胜数,后期维护时需要创建很多的服饰子类,难以维护。另外,可以观察一下客户端的代码。
tShirt.show();
bigTrouser.show();
sneakers.show();
person.show();
以上代码是将服饰与人一个词一个词的显式出来,就好比是这个人光着身子,当着大家的面进行穿衣服的行为,这里可能想到建造者模式,但再仔细考虑一下,建造者模式要求建造的过程必须是稳定的,可这个例子中,人穿衣服是不稳定的,有无数种组合方案。
那应该如何把所需的功能按照正确的顺序串联起来进行控制呢?可以参考下装饰模式实现。
改造装饰模式
装饰模式是一种结构型设计模式,它允许动态的将行为添加到对象中,而无需通过继承扩展整个类。这种模式通过创建一个包装对象,也就是装饰器,来包裹真实的对象,从而增强其行为。
装饰模式类图
---
title: 装饰模式类图
---
classDiagram
direction BT
ConcreteComponent --|> Component
Decorator --|> Component
ConcreteDecoratorA --|> Decorator
ConcreteDecoratorB --|> Decorator
Decorator o--> Component: component
note for Component "Component 是定义一个对象接口,\n可以给这些对象动态地添加职责"
class Component {
<<interface>>
+ operation()
}
note for ConcreteComponent "ConcreteComponent 是定义了\n一个具体的对象,也可以给这个对象\n添加一些职责"
class ConcreteComponent {
+ operation()
}
note for Decorator "Decorator,装饰抽象类\n继承了 Component,从外类来扩展 Component 类的功能,\n但对于 Component 来说,是无需知道 Decorator 的存在的。"
class Decorator {
<<abstract>>
+ operation()
}
note for ConcreteDecoratorA "ConcreteDecorator 就是具体的装饰对象,\n起到给 Component 添加职责的功能"
class ConcreteDecoratorA {
- addedState: string
+ operation()
}
class ConcreteDecoratorB {
+ operation()
- addedBehavior()
}
可以参考上图理解装饰模式,Component 是定义一个对象接口,可以给这些对象动态的添加职责。ConcreteComponent 是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator 是装饰抽象类,继承了 Component ,从外类来扩展 Component 类的功能,但是对于 Component 来说,是无需知道 Decorator 的存在的。至于 ConcreteDecorator 就是具体的装饰对象,起到给 Component 添加职责的功能。
对于上面的例子,可以看出来,Person 只有一个 ConcreteComponent 类而没有抽象的 Component 类,那么服饰类 Finery 可以是 Person 的一个子类。同样的道理,如果只有一个 ConcreteDecorator 类,那么就没有必要建立一个单独的 Decorator 类,而可以把 Decorator 和 ConcreteDecorator 的责任合并成一个类。
示例的装饰模式实现
Person 类 (ConcreteComponent)
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public void show() {
System.out.printf("打扮的%s", name);
}
}
服装类 Finery(Decorator)
public class Finery extends Person {
protected Person component;
public void dressUp(Person component) {
this.component = component;
}
@Override
public void show() {
if (!Objects.isNull(component)) {
component.show();
}
}
}
具体服装类 ConcreteDecorator
// 垮裤
public class BigTrouser extends Finery {
@Override
public void show() {
System.out.print("垮裤 ");
super.show();
}
}
// 皮鞋
public class LeatherShoes extends Finery {
@Override
public void show() {
System.out.print("皮鞋 ");
super.show();
}
}
// 其余省略 ......
模拟客户端
public class Wear {
public static void main(String[] args) {
Person person = new Person("小明");
System.out.println("第一种装扮\n");
TShirt tShirt = new TShirt();
BigTrouser bigTrouser = new BigTrouser();
Sneakers sneakers = new Sneakers();
tShirt.dressUp(person);
bigTrouser.dressUp(tShirt);
sneakers.dressUp(bigTrouser);
sneakers.show();
System.out.println();
System.out.println("第二种装扮\n");
LeatherShoes leatherShoes = new LeatherShoes();
Tie tie = new Tie();
Suit suit = new Suit();
leatherShoes.dressUp(person);
tie.dressUp(leatherShoes);
suit.dressUp(tie);
suit.show();
System.out.println();
}
}
通过实例改造可以看出来,装饰模式其实就是对现有功能动态地添加更多功能的一种方式,将每个要装饰的功能放在单独的类中,并让这个类包装它索要装饰的对象,当客户端需要执行特殊行为时,就可以根据需要有选择性的、按顺序的使用装饰功能包装对象了。
优缺点
优点
-
装饰模式遵循开放封闭原则,可以在不改变现有代码的情况下,动态的添加或删除对象的行为。
-
通过使用不同的装饰器,可以在运行时为对象添加不同的行为,使得代码更加灵活和提高可扩展性。
-
装饰器可以递归嵌套,从而为对象添加多个装饰器,实现更复杂的功能。
缺点
-
使用装饰模式会增加代码的复杂性,因为需要创建更多的类和对象来实现装饰器的嵌套。
-
如果使用不当,可能会导致代码逻辑混乱,难以维护。
适用的场景
-
在不影响其他对象的情况下,需要动态的增加或删除对象的行为。
-
需要为对象添加一些额外的功能,但又不希望直接修改该对象的代码。
-
需要在运行时递归的为对象添加多个装饰器。
-
需要对一个对象的不同属性或方法进行不同的装饰。例如,一个类的某个方法需要添加不同的日志记录、缓存、验证等功能。