从吃鸡中论装饰器模式

在讲装饰器模式之前,我先讲讲代码实例,在讲具体的原理和结构。

情景:游戏中经常使用枪(英文:Gun),有手枪(英文:Pistol),狙击枪(英文:SniperRifle)等等。
然后枪有个基本功能,肯定是fire,也许shot更恰当,如果用代码实现,大致如戏:

  • 抽象类:枪(Gun)
public abstract class Gun {
    //开火
    public abstract Fire fire();
}
  • 具体类:手枪(Pistol),继承与 Gun
public class Pistol extends Gun{

    @Override
    public Fire fire() {
        Fire fire = new Fire();
        fire.setRange(50); //射程50
        fire.setVoice(false); //开火有声音
        return fire;
    }
}
  • 具体类:狙击枪(SniperRifle),继承与 Gun
public class SniperRifle extends Gun{

    @Override
    public Fire fire() {
        Fire fire = new Fire();
        fire.setRange(400);//射程400
        fire.setVoice(false);//开火有声音
        return fire;
    }
}

枪可能有很多种,这里,就是举两个。

  • 开火属性类,表示开火的属性。
public class Fire {
    //射程
    private int range; 
    //开枪是否有声音
    private boolean voice; 

    @Override
    public String toString() {
        return "[射程:" + range + ","+((voice) ? "消音":"没有消音")+"]";
    }

    /***************************getter & setter*******************************/
    public int getRange() {return range;}
    public void setRange(int range) {this.range = range;}

    public boolean isVoice() {return voice;}
    public void setVoice(boolean voice) {this.voice = voice;}
    /***************************getter & setter*******************************/
}

在没有给枪装任何配件的情况下,这就是裸枪了。开火始终有声音,射程总是不变。
现在,我们用装饰器模式给枪装配件,我们设定如下:

  1. 如果装上消音器(英文:Silencer),可以消音
  2. 如果装上瞄准镜(英文:Collimation Mirror),可以增加射程。

我们代码实现如 :

  • 我们定义一个配件的父类,这个类可以不是抽象类,但是很明显,定义为abstract 更符合语义
/**
 * 配件父类
 */
public abstract class DecorateParts extends Gun{

    protected Gun gun;

    /**核心方法,这个方法很关键,是装饰器模式的核心 */
    @Override
    public Fire fire() {
        return (gun != null) ? gun.fire() : null;
    }

    /**安装配件*/
    public void install(Gun gun) {
        this.gun = gun;
    }

}
  • 具体配件类:消音器
/**
 * 消音器
 */
public class Silencer extends DecorateParts{

    @Override
    public Fire fire() {
        Fire fireResult = super.fire();
        fireResult.setVoice(true);//装上消音
        return fireResult;
    }

}
  • 具体配件类:瞄准镜
/**
 * 瞄准镜
 */
public class CollimationMirror extends DecorateParts{
    //增加的射程,这里设定增加100射程
    private int addRange = 100;

    @Override
    public Fire fire() {
        Fire fireResult = super.fire();
        //增加射程
        fireResult.setRange(fireResult.getRange() + addRange);
        return fireResult;
    }

}

至此,配件相关的类也写完了,接下来,测试一下:

public class DecorateMain {

    public static void main(String[] args) {
        //创建一把手枪
        Pistol gun = new Pistol();
        System.out.println("手枪-[裸枪]-" + gun.fire());

        //创建一个瞄准镜,裸枪上,安装瞄准镜
        CollimationMirror mirrorGun = new CollimationMirror();
        mirrorGun.install(gun);
        System.out.println("手枪-[瞄准镜]-" + mirrorGun.fire());

        //创建一个消音器,在安装了瞄准镜的基础上,再安装消音器
        Silencer silencerMirrorGun = new Silencer();
        silencerMirrorGun.install(mirrorGun);
        System.out.println("手枪-[瞄准镜,消音器]-" + silencerMirrorGun.fire());

        //创建一个消音器,裸枪上,安装消音器
        Silencer silencerGun = new Silencer();
        silencerGun.install(gun);
        System.out.println("手枪-[消音器]-" + silencerGun.fire());
    }
}

具体的运行结果如下:
这里写图片描述

在不影响枪的情况下,对枪进行装饰,主要还是看配件的3个类。
DecorateParts 本质上他不是配件,他继承了Gun,然后还有一个Gun的引用(protected Gun gun),
可以说这是装饰器模式关键。
我们从代码中可以知道,手枪,狙击枪是在Gun的基础上进行扩展,而消音器,瞄准器是在DecorateParts 上扩展。

/**
 * 消音器
 */
public class Silencer extends DecorateParts{

    @Override
    public Fire fire() {
        Fire fireResult = super.fire();
        fireResult.setVoice(true);//装上消音
        return fireResult;
    }

}

我们主要分析下消音器,使用装饰器模式,基本上第一个的方法的就是super.fire(),也就是调用父类的同名方法,
这也代理模式有些相似,但是并不相同。看如下代码,也是就是配件的装配过程:
Pistol gun = new Pistol();
mirrorGun.install(gun);
silencerMirrorGun.install(mirrorGun);

对于silencerMirrorGun;super.fire()其实就是mirrorGun.fire();
对于mirrorGun,super.fire()其实是gun.fire()
由此可见:silencerMirrorGun.fire()中第一步调用了mirrorGun.fire(),mirrorGun.fire()第一步调用了gun.fire()。
所以mirrorGun.fire()是在gun.fire()上装饰,而silencerMirrorGun.fire()在mirrorGun.fire()的基础上装饰。
这就是装饰模式的真正意义。而代理模式也是类似,不过代理一般不会改变方法的返回结果,装饰器模式一遍都是多层的,也就是说装配了很多东西,而代理模式一般也就只有一层而已。

最后来看看装饰器模式的类图结构,图片来自大话设计模式,如下:
这里写图片描述

与我上面的例子其实很容易对应,如下:
(Component – Gun)
(ConcreteComponent – Pistol)
(Decorator – DecorateParts)
(ConreteDecoratorA – Silencer)
(ConreteDecoratorB – CollimationMirror)

结语:当系统需要新的功能的时候,而且是像旧的类中添加新的代码。这些新的逻辑通常修饰了原有类的核心设计和职责。这时候可以考虑用装饰器模式

猜你喜欢

转载自blog.csdn.net/u014801432/article/details/82012446
今日推荐