装饰模式(Decorator)——给类增加额外功能
一、先看个例子
给一个人搭配不同的服饰,代码可以这样写:
@AllArgsConstructor
public class Person01 {
private String name;
public String wearTShirt(){
return "大T恤";
}
public String wearTrousers(){
return "垮裤";
}
public String wearShoes(){
return "洞洞鞋";
}
public String wearLeatherShoes(){
return "皮鞋";
}
public String wearSuit(){
return "西装";
}
public String wearTie(){
return "红领带";
}
public void show(StringBuilder msg){
Console.log(msg);
}
public static void main(String[] args) {
Person01 person = new Person01("王富贵");
StringBuilder msg_01 = new StringBuilder("第一套装扮:");
StringBuilder msg_02 = new StringBuilder("第二套装扮:");
msg_01.append(person.wearTShirt())
.append(person.wearShoes())
.append(person.wearTie());
msg_02.append(person.wearTrousers())
.append(person.wearLeatherShoes())
.append(person.wearTie());
person.show(msg_01);
person.show(msg_02);
}
}
这么写直观的感觉就是不符合单一原则,这个类里面的行为太多了。
另外若现在要有第三套装扮,那就要修改这个类了。也就是违背了开闭原则。
二、装饰模式结构
装饰模式,动态的给一个对象添加一些额外的功能,就增加功能来说,装饰模式比生成子类更灵活。
Component 接口(或抽象类)就是来定义动态添加的职责。
ConcreteComponent 是具体对象,具体实现要添加的职责。
Decorator 装饰抽象类,继承了Component,且持有Component对象。
三、装饰模式代码实现
abstract class Component {
public abstract void operation();
}
public class ConcreteComponent extends Component {
@Override
public void operation() {
Console.log("具体对象的操作!");
}
}
@Data
abstract class Decorator extends Component {
protected Component component;
@Override
public void operation() {
if (component != null) {
component.operation();
}
}
}
@Data
public class ConcreteDecoratorA extends Decorator {
// 本类独有,来区别ConcreteDecoratorB类
private String addedState;
@Override
public void operation() {
component.operation();
addedState = "New State";
Console.log("具体装饰对象A的操作");
}
}
@Data
public class ConcreteDecoratorB extends Decorator {
// 本类独有,来区别ConcreteDecoratorA类
private void addedBehavior(){
}
@Override
public void operation() {
component.operation();
addedBehavior();
Console.log("具体装饰对象B的操作");
}
}
public class Client01 {
public static void main(String[] args) {
ConcreteComponent c = new ConcreteComponent();
ConcreteDecoratorA a = new ConcreteDecoratorA();
ConcreteDecoratorB b = new ConcreteDecoratorB();
a.setComponent(c);
b.setComponent(a);
b.operation();
}
}
具体对象的操作!
具体装饰对象A的操作
具体装饰对象B的操作
首先是用ConcreteComponent实例对象c,然后ConcreteDecoratorA的实例化对象a来包装c,再用ConcreteDecoratorB的实例化对象b来装饰a,即c-->a-->b。最终执行的是b的operation()方法。
四、改写第一节代码
若只有一个ConcreteComponent类没有抽象类Component,那么Decorator类可以是ConcreteComponent的一个子类。
若只有一个ConcreteDecorator类,那么就没必要建立单独的Decorator类,可以把ConcreteDecorator和Decorator的责任合并成一个类。
这里就没必要有Component类了,直接让装饰类Decorator继承Person类这个ConcreteComponent即可。
@Data
public class Person {
private String name;
public void show() {
Console.log("身体body");
}
}
@Data
public class Finery extends Person{
protected Person component;
public void show() {
if (component != null) {
component.show();
}
}
}
@Data
public class TShirt extends Finery {
public void show() {
Console.log("大T恤");
component.show();
}
}
@Data
public class Trouser extends Finery {
public void show() {
Console.log("裤子");
component.show();
}
}
@Data
public class Suit extends Finery {
public void show() {
Console.log("西服");
component.show();
}
}
@Data
public class Tie extends Finery {
public void show() {
Console.log("领带");
component.show();
}
}
@Data
public class LeatherShoes extends Finery {
public void show() {
Console.log("皮鞋");
component.show();
}
}
@Data
public class Shoes extends Finery {
public void show() {
Console.log("洞洞鞋");
component.show();
}
}
public class Client02 {
public static void main(String[] args) {
Person component = new Person();
Console.log("第一种装扮:");
Suit suit = new Suit();
Trouser trouser = new Trouser();
Tie tie = new Tie();
suit.setComponent(component);
trouser.setComponent(suit);
tie.setComponent(trouser);
tie.show();
Console.log("第二种装扮:");
TShirt tShirt = new TShirt();
Shoes shoes = new Shoes();
LeatherShoes leatherShoes = new LeatherShoes();
tShirt.setComponent(component);
shoes.setComponent(tShirt);
leatherShoes.setComponent(shoes);
leatherShoes.show();
}
}
第一种装扮:
领带
裤子
西服
身体body
第二种装扮:
皮鞋
洞洞鞋
大T恤
身体body
五、总结
装饰模式是为已有功能动态添加更多功能的一种方式。
什么时候使用装饰者模式呢?
当系统需要新功能,在旧的类中添加新的代码会增加类的复杂度,破坏单一职责原则。
装饰模式把每个要添加的功能放在单独的类中,然后让这个装饰类来包装需要添加这个功能的类。
所以装饰模式的好处是不增加原有类复杂度的前提下为其增加新的功能,将装饰职责和核心职责分开。
但是在使用时要注意装饰顺序。