WHCSRL 技术网

当我们聊装饰的时候,我们在聊什么?装饰模式

当我们聊装饰的时候,我们在聊什么?

装饰模式又称为包装模式,如何解释该模式呢?

从需求出发,我们希望存在这样一种设计,在不改变原有载体基础属性的前提下更换载体的一些外在属性,从而改变其对外的展示形态。或者动态增加其功能。

开头为什么要提到 又称为 ”包装模式“ 呢?那就是在我们程序的设计上,会突出一个 ”包“ 字,给你原有的对象包一层。赋予新的特性和功能。

形象一点,我们以王者荣耀的皮肤和拖尾为例子来构造一个需求,并尝试对其进行设计。

需求如下:对于一个英雄,我们希望可以对其功能和特效进行拓展,比如添加皮肤,添加拖尾,添加回城特效等。

在这里插入图片描述

在这里插入图片描述

需求很简单,但设计并不容易,我们需要考虑通用性和可拓展性,那么需求存在我们开始设计。

一个设计

ok ,从类设计的角度来看,我们考虑一个最简单满足需求的设计,并进行分析改进。首先考虑的就是接口和继承,基本上玩设计都离不开这两个东西,尤其是玩通用性。

首先给人物一个统一的父类 GameObject

public abstract class GameObject {
    
    // 展示皮肤
    public abstract void showSkin();

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

创建一个悟空类继承 GameObject

public class WuKong extends GameObject{

    @Override
    public void showSkin() {
        System.out.println("原皮肤的悟空");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

那换肤和拖尾呢,我多实现几个不同的悟空类不就行了?

public class WuKong86AndTrailing extends GameObject{

    @Override
    public void showSkin() {
        System.out.println("86版皮肤的悟空和原始拖尾");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
public class WuKongHellfireAndAdventureTrailing extends GameObject{

    @Override
    public void showSkin() {
        System.out.println("地狱火皮肤的悟空和冒险拖尾");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用:

public class Main {

    public static void main(String[] args) {

        GameObject wk=new WuKong();
        wk.showSkin();
        GameObject wk2=new WuKong86AndTrailing();
        wk2.showSkin();
        GameObject wk3=new WuKongHellfireAndAdventureTrailing();
        wk3.showSkin();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出:

原皮肤的悟空
86版皮肤的悟空和原始拖尾
地狱火皮肤的悟空和冒险拖尾
  • 1
  • 2
  • 3

到这里,完工!这样设计有问题吗?有问题!问题大了。

首先这个设计根本没有完成我们的需求,虽然说玩了下继承,但跟没玩一样,一个皮肤一个建立一个新的英雄对象,那如果换个英雄呢?完全没法复用了,子类对象爆炸。

我们在聊什么?

上个设计有问题,好!改!首先我们先分析一下如何改。

  1. 我们要复用对象,在原始对象的基础下进行附加。
  2. 游戏对象和装饰对象需要抽离

首先我们把接口抽象出来,展示皮肤提到接口里

public interface Component {

    void show();

}
  • 1
  • 2
  • 3
  • 4
  • 5

统一的游戏基类,给个名字

public abstract class GameObject implements Component{

    String name;

    public GameObject(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    @Override
    public void show() {
        System.out.println("英雄登场:"+this.name);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

抽象的装饰器类,我们所有的附加装饰都要实现该类,该类实现 Component 接口

public abstract class GameObjectDecorator implements Component {

    private Component component;

    public GameObjectDecorator(Component component) {
        this.component = component;
    }

    @Override
    public void show() {
        component.show();
        addDecorator();
    }

    public abstract void addDecorator();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

创建角色,这里创建两个,来展示其可复用性

public class WuKong extends GameObject {


    public WuKong() {
        super("悟空");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
public class Human extends GameObject {

    public Human() {
        super("普通人类");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

皮肤装饰类:

public class Clothes86Decorator extends GameObjectDecorator {

    public Clothes86Decorator(Component component) {
        super(component);
    }

    @Override
    public void addDecorator() {
        System.out.println("装备 86 皮肤");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class ClothesHellfireDecorator extends GameObjectDecorator {

    public ClothesHellfireDecorator(Component component) {
        super(component);
    }

    @Override
    public void addDecorator() {
        System.out.println("装备地狱火皮肤");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

拖尾装饰类:

public class AdventureTrailDecorator extends GameObjectDecorator {

    public AdventureTrailDecorator(Component component) {
        super(component);
    }

    @Override
    public void addDecorator() {
        System.out.println("装备 冒险拖尾");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class TrailDecorator extends GameObjectDecorator {

    public TrailDecorator(Component component) {
        super(component);
    }

    @Override
    public void addDecorator() {
        System.out.println("装备 原始拖尾");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

使用:

public class Main {

    public static void main(String[] args) {

        GameObject wuKong=new WuKong();
        wuKong.show();
        System.out.println();

        Clothes86Decorator clothes86Decorator=new Clothes86Decorator(wuKong);
        TrailDecorator trailDecorator=new TrailDecorator(clothes86Decorator);
        trailDecorator.show();
        System.out.println();

        ClothesHellfireDecorator clothesHellfireDecorator=new ClothesHellfireDecorator(wuKong);
        AdventureTrailDecorator adventureTrailDecorator=new AdventureTrailDecorator(clothesHellfireDecorator);
        adventureTrailDecorator.show();
        System.out.println();

        Human human=new Human();
        ClothesHellfireDecorator clothesHellfireDecorator2=new ClothesHellfireDecorator(human);
        AdventureTrailDecorator adventureTrailDecorator2=new AdventureTrailDecorator(clothesHellfireDecorator2);
        adventureTrailDecorator2.show();
        System.out.println();

        wuKong.show();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

输出:

英雄登场:悟空

英雄登场:悟空
装备 86 皮肤
装备 原始拖尾

英雄登场:悟空
装备地狱火皮肤
装备 冒险拖尾

英雄登场:普通人类
装备地狱火皮肤
装备 冒险拖尾

英雄登场:悟空
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

你会发现我们所有的装饰类都可以一个套一个,进行无限的附加,这归功于我们在抽象装饰器类中,组合了原始的 Component 接口,而我们所有的类都实现的该接口,使得我们在使用时可以进行任意的添加,并形成了这种 “包装的感觉”。

使用场景

当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,或者对象的功能要求可以动态地添加,也可以再动态地撤销时。考虑使用装饰模式或许是一个不错的选择

推荐阅读