掘金 后端 ( ) • 2024-03-28 10:25

装饰模式概述

装饰模式,作用是完成程序的各种功能的动态的添加,将功能像穿衣服一样,一层一层的嵌套起来。装饰模式分为四个部分:功能接口、初始基本功能类、功能装饰类、具体功能装饰类。

  • 功能接口:所有的功能(包括初始基本功能类,功能装饰类,具体功能装饰类)都直接或间接实现了这个接口,以方便对功能的规范和多态的实现。
  • 初始基本功能类:最基本的功能,即未装饰的功能,之后的所有装饰都是基于此类的对象,这个类要实现功能接口
  • 功能装饰类:其中定义了“装饰”这个动作的代码,这个类要实现功能接口
  • 具体功能装饰类:具体实现了想要装饰给目标对象的功能,这个类要继承于功能装饰类,以继承“装饰”的能力,由于继承了功能装饰类,所以间接实现了功能接口,无需再次实现

代码实现

若要实现给游戏玩家添加装备,玩家开局有一把小铁剑。

功能接口

public interface ObtainEquipmentInterface {
    void showEquipment();
}

若showEquipment是玩家获得指定装备并展示

初始基本功能类

public class Player implements ObtainEquipmentInterface {
    private String name;
    public Player(String name){
        this.name = name;
    }
    @Override
    public void showEquipment() {
        System.out.println("玩家" + name + "装备了开局小铁剑");
    }
}

初始基本功能类也是吧装饰模式中重要的,比如要穿衣服,总得要先有一个还没穿好衣服的人来穿吧。玩家的初始基本功能可以是有一把小铁剑,也可以是装备了赤手空拳(讲了句废话)。反正,理解就是,包几层饺子皮都可以,但总得有个馅,就算是空气馅也要写出来(+1)。因为在这个设计模式中,“装饰”这个功能的代码是要带入一个包裹的对象的,就算是没有功能的对象,也要包裹一个。如果要的基本功能类不想设定功能,可以假设一个“赤手空拳”、“空气馅”的功能(也就是假功能)。才能保证后面的装饰代码有“馅”可包。总之,看完功能装饰类的装饰的能力是如何实现的就懂了。

功能装饰类

public class ObtainEquipment implements ObtainEquipmentInterface {
    protected ObtainEquipmentInterface obtainEquipment;

    public void addEquipment(ObtainEquipmentInterface obtainEquipment){
        this.obtainEquipment = obtainEquipment;
    }

    @Override
    public void showEquipment() {
        if (this.obtainEquipment != null) {
            this.obtainEquipment.showEquipment();
        }
    }
}

addEquipment就是装饰能力的实现,参数里面的obtainEquipment赋值给this.obtainEquipment就是装饰的动作,showEquipment中的代码就是获得装饰后的功能。但是不是用这个类的对象进行装饰,从此类还看不出是如何装饰的,这个类主要用来做继承的父类。接下来就要体现面向对象的继承性的魔力了。

具体功能装饰类

public class ObtainEquipmentGoldArmor extends ObtainEquipment {
    @Override
    public void showEquipment() {
        super.showEquipment();
        System.out.println("玩家装备了金铠甲");
    }
}

public class ObtainEquipmentGoldSword extends ObtainEquipment {
    @Override
    public void showEquipment() {
        super.showEquipment();
        System.out.println("玩家装备了金剑");
    }
}

public class ObtainEquipmentPegasus extends ObtainEquipment {
    @Override
    public void showEquipment() {
        super.showEquipment();
        System.out.println("玩家装备了飞马");
    }
}

具体功能装饰类是实现了功能装饰类,其实就是隐形地将功能装饰类中“装饰”的功能继承到了这些子类中。在这些子类重写了父类的showEquipment的情况下,想象一下,父类中的以下代码也继承过来了

protected ObtainEquipmentInterface obtainEquipment;

public void addEquipment(ObtainEquipmentInterface obtainEquipment){
        this.obtainEquipment = obtainEquipment;
    }

这些就是用来装饰的代码,obtainEquipment就是装饰的对象。而,装饰的对象的类型为ObtainEquipmentInterface类型,说明不管是初始基本功能类的对象还是具体功能装饰类的对象,都可以成为被装饰的对象。所有的具体装饰的对象都继承了装饰的能力,所有的具体功能装饰类的对象,都可以装饰其他的ObtainEquipmentInterface类型的对象。

功能装饰,就是在原来的功能不变的基础上,加上新的功能。而让原来的功能不变,就是重写的showEquipment中的super.showEquipment的功劳

@Override
    public void showEquipment() {
        super.showEquipment();
        System.out.println("玩家装备了飞马");
    }

super.showEquipment()调用了被装饰的对象的showEquipment()功能,然后再将要添加的功能加在重写的superEquipment中。

public class Main {
    public static void main(String[] args) {
        Player Bob = new Player("Bob");

        ObtainEquipmentGoldArmor goldArmor = new ObtainEquipmentGoldArmor();
        goldArmor.addEquipment(Bob);

        ObtainEquipmentGoldSword goldSword = new ObtainEquipmentGoldSword();
        goldSword.addEquipment(goldArmor);

        ObtainEquipmentPegasus pegasus = new ObtainEquipmentPegasus();
        pegasus.addEquipment(goldSword);

        pegasus.showEquipment();
    }
}

代码运行效果:

玩家Bob装备了开局小铁剑
玩家装备了金铠甲
玩家装备了金剑
玩家装备了飞马

总结

装饰模式的原理就是利用继承性,给每一个具体功能装饰类的对象赋予装饰的能力,并能利用super调用被装饰对象的功能,再加上新的功能。

用衣服来说明装饰模式就是。定义一件衣服,它能直接套在人身上,也能继续套在其他衣服上。假如一号衣服能保暖,二号衣服能防弹,三号衣服能防水。那么一号衣服装饰人,二号衣服装饰一号衣服,三号衣服装饰二号衣服。那么,这个人就能既保了暖,又能防弹,又防水。

装饰模式巧妙的利用了继承性,使得功能能根据需求动态添加。且只要再定义一个具体功能装饰类,并继承功能装饰类,最后装饰目标对象,就能加上要添加的功能。

真的才知道继承性还能这么玩,完了,要长脑子了。