装饰模式

93 阅读3分钟

使用场景

在不修改类源码的情况下,丰富类的各个行为

场景假设

小无和他的同事小头下班后一起回家,发现路边有个歌手在唱歌

小头:小无,你看这个歌手唱歌的的场景是不是可以抽象成代码

小无:(啥子哦,下班还在这想代码,多少带点)是的,可以抽象成代码,首先是将歌手抽象成一个Singer类

public class Singer {
    String sing() {
        return "唱歌";
    }
}

然后创建实例,调用就行

public class Main {
    public static void main(String[] args) {

        Singer singer = new Singer();

        System.out.println("歌手在" + singer.sing());
    }
}

小头:那如果我想这个歌手在唱歌的时候同时跳舞呢

小无:那么修改sing方法,让他支持跳舞

public class Singer {
    String sing(boolean isDance) {
        String action = "唱歌";
        if (isDance) {
            action = action + "、跳舞";
        }
        return action;
    }
}

小头:这样写违背了设计六大原则的开闭原则

开闭原则是指一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。也就是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化

小无:(你平时不都是这样写的嘛,这个时候给我说六大原则?)那我们可以继承Singer新写一个子类,重写sing方法,在唱歌时同时跳舞

public class SingerWithDance extends Singer {
    @Override
    String sing() {
        return super.sing() + "、跳舞";
    }
}

然后调用时初始化这个类的实例就行

public class Main {
    public static void main(String[] args) {

        Singer singer = new SingerWithDance();

        System.out.println("歌手在" + singer.sing());
    }
}

小头:那我如果还想在唱歌的时候同时rap呢

小无:(这人今天是不是来找茬的呀)那。。。再继承写个子类?

小头:如果我还想唱歌的同时打篮球呢,排列组合下来,这得写多少个子类呀

image.png

小无:那应该咋写呢

小头:这种情况就可以使用装饰模式了

装饰模式

小头:装饰模式主要就是被封装者和装饰器,放在代码中分四个部分

  • 被封装者的抽象接口

    • 主要是定义被封装的行为是哪些
  • 被封装者的具体类

    • 被封装的行为的基础实现
  • 装饰器的基础类

    • 里面定义了装饰器的基本原理,就是会持有被封装者对象,封装完的最后都会由被封装者去执行
  • 装饰器的具体实现类

    • 实现各种装饰逻辑

UML图

放在我们这个场景,以UML图表示就是这样

image.png

各部分具体代码实现

ISinger
public interface ISinger {
    String sing();
}
Singer
public class Singer implements ISinger {
    @Override
    public String sing() {
        return "唱歌";
    }
}
SingerBaseDecorator
public class SingerBaseDecorator implements ISinger {
    ISinger singerWapper;

    public SingerBaseDecorator(ISinger singer) {
        singerWapper = singer;
    }

    @Override
    public String sing() {
        return singerWapper.sing();
    }
}
SingerDanceDecorator
public class SingerDanceDecorator extends SingerBaseDecorator{
    public SingerDanceDecorator(ISinger singer) {
        super(singer);
    }

    @Override
    public String sing() {
        return super.sing()+"、跳舞";
    }
}
SingerRapDecorator
public class SingerRapDecorator extends SingerBaseDecorator{
    public SingerRapDecorator(ISinger singer) {
        super(singer);
    }

    @Override
    public String sing() {
        return super.sing() + "、rap";
    }
}
SingerBasketballDecorator
public class SingerBasketballDecorator extends SingerBaseDecorator {

    public SingerBasketballDecorator(ISinger singer) {
        super(singer);
    }

    @Override
    public String sing() {
        return super.sing()+"、篮球";
    }
}

运行代码

小头:现在我们就可以随意的封装我们的Singer了

public class Main {
    public static void main(String[] args) {

        ISinger singer = new Singer();
        singer = new SingerDanceDecorator(singer);
        singer = new SingerRapDecorator(singer);
        singer = new SingerBasketballDecorator(singer);

        System.out.println("歌手在" + singer.sing());
    }
}

小结

装饰模式跟我们平时穿衣服很像,在身体上,穿上各种衣物,身体是被封装者,而各种衣物就是装饰器。身体是必要的,衣服是非必要。