Java常用设计模式-装饰者模式

426 阅读4分钟

装饰者模式,又叫做包装模式,指的是不改变原有对象的基础上,把功能附加在对象上。提供了比继承更加有弹性的扩展方案,属于结构型设计模式。

1,从一个例子开始

相信很多人都玩过《和平精英》这个游戏。在游戏中,玩家可以自由地捡一些枪械和部件,并把部件安装到枪械上,自由地组合一把枪以提升枪的功能,这其实就是装饰者模式的一个体现。

今天我们就来模拟一下这个例子。

首先创建一个枪械抽象类:

package com.gitee.swsk33.decorator.model;

import lombok.Getter;
import lombok.Setter;

/**
 * 枪械
 */
@Getter
@Setter
public abstract class Gun {

   /**
    * 名字
    */
   private String name;

   /**
    * 当前武器状态
    */
   private String status;

   /**
    * 武器性能
    */
   private int capability;

}

然后创建具体枪械类-98K步枪:

package com.gitee.swsk33.decorator.model.impl;

import com.gitee.swsk33.decorator.model.Gun;

/**
 * 98K步枪
 */
public class Kar98K extends Gun {

   /**
    * 构造函数:初始化属性
    */
   public Kar98K() {
      setName("Kar98K");
      setStatus("98K步枪");
      setCapability(10);
   }

}

好了,假设要给这个步枪加上八倍镜,那有的人可能会在这个类基础上又创建一个类Kar98KWithEightfoldScope并修改其参数,再加上一个子弹袋,就又创建一个类。。。

很显然这是不行的,在游戏中枪械和部件种类非常的多,而每一把枪和很多部件可以玩出多种组合,如果把每个组合都列举一遍,显然是非常不现实的。

这时,就要用到装饰者模式了。

2,创建装饰者抽象类和具体类

在这里,枪械部件都称之为装饰者,因为它们可以动态扩展枪械功能,枪械就是被装饰者

创建装饰者的抽象:

package com.gitee.swsk33.decorator.wrapper;

import com.gitee.swsk33.decorator.model.Gun;

/**
 * 枪的部件
 */
public abstract class GunWrapper extends Gun {

   /**
    * 传入要安装部件的枪械
    */
   private Gun gun;

   /**
    * 构造器依赖注入
    *
    * @param gun 要安装部件的枪械
    */
   public GunWrapper(Gun gun) {
      this.gun = gun;
   }

}

然后实现具体的装饰者。

八倍镜:

package com.gitee.swsk33.decorator.wrapper.impl;

import com.gitee.swsk33.decorator.model.Gun;
import com.gitee.swsk33.decorator.wrapper.GunWrapper;

/**
 * 八倍镜部件
 */
public class EightfoldScope extends GunWrapper {

   /**
    * 构造器依赖注入
    *
    * @param gun 要安装部件的枪械
    */
   public EightfoldScope(Gun gun) {
      super(gun);
      this.setStatus(gun.getStatus() + " + 八倍镜");
      this.setCapability(gun.getCapability() + 5);
   }

}

子弹袋:

package com.gitee.swsk33.decorator.wrapper.impl;

import com.gitee.swsk33.decorator.model.Gun;
import com.gitee.swsk33.decorator.wrapper.GunWrapper;

/**
 * 子弹袋部件
 */
public class BulletBag extends GunWrapper {

   /**
    * 构造器依赖注入
    *
    * @param gun 要安装部件的枪械
    */
   public BulletBag(Gun gun) {
      super(gun);
      this.setStatus(gun.getStatus() + " + 子弹袋");
      this.setCapability(gun.getCapability() + 3);
   }

}

这里可见:装饰者抽象类中有一个属性就是被装饰者,因为我们要先把被装饰者传入给装饰者实例,装饰者才能对被装饰者进行操作和附加。然后具体的装饰者,就会对传入的被装饰者进行一个功能的附加。

就像你要把具体的一个物品放进礼物盒子一样,礼物盒子就是装饰者。

我们来运行一下:

// 模拟玩家正在吃鸡游戏
// 捡到一把98K步枪
Gun kar98k = new Kar98K();
System.out.println("当前武器情况:" + kar98k.getStatus() + ",性能:" + kar98k.getCapability());
// 捡到一个八倍镜并安装至98K上面
kar98k = new EightfoldScope(kar98k);
System.out.println("当前武器情况:" + kar98k.getStatus() + ",性能:" + kar98k.getCapability());
// 捡到一个子弹袋并安装至98K上面
kar98k = new BulletBag(kar98k);
System.out.println("当前武器情况:" + kar98k.getStatus() + ",性能:" + kar98k.getCapability());

结果:

image.png

3,总结

可见装饰者模式,可以很灵活地对原有的对象进行扩展,我们需要什么装饰,就实例化什么装饰并附加给被装饰物,是非常的方便灵活的。

可见装饰者模式中,有以下类:

  • 抽象被装饰者:上面的枪械抽象类
  • 具体被装饰者:上面的98K类
  • 抽象装饰者:上面的部件抽象类
  • 具体装饰者:上面的八倍镜、子弹袋类

通常使用装饰者接收被装饰者实例,在装饰者中对被装饰者实例进行功能附加,就完成了装饰的步骤。上述例子比较简单,就在构造函数中对被装饰者进行了附加。很多时候在装饰者中会重写被装饰者的某个成员方法实现功能附加

并且,具体被装饰者抽象装饰者都有着共同的超类(父类):抽象被装饰者。这样使得装饰之后的实例,仍然属于最初的类型。

装饰者模式的类图如下:

image.png

示例仓库地址