设计模式3--Decorator

本文最后更新于:3 years ago

修饰器模式

  装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于 结构型模式 ,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

如果想了解修饰器模式的具体的介绍,菜鸟教程介绍得比较详细↓
菜鸟教程-修饰器模式

结构图

参考博客

优缺点

优点: 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点: 多层装饰比较复杂。

使用场景

1、扩展一个类的功能。
2、动态增加功能,动态撤销。

实现代码

这里是拿别人的例子来说的,我们都知道坦克大战这个游戏,在游戏设计完了,玩了一段时间后,我觉得游戏里的坦克不够炫,没有别人设计的那种感觉。于是这时,我想给他加一个外壳,加一条尾巴,再加个血条。这酷!
这不难实现吧。但我们要求是,再给坦克发出的子弹也加上一条尾巴和外壳。

方法一(×)

即使不会设计模式,大部分人也能够想到用继承来实现。是的,这是一种方法,
当我们需要加血条是,继承Tank类,实现BloodTank,加尾巴,实现TailTank类,加外壳,实现RectTank类
类似的BloodTailTank、BloodRectTank、TailBullet…
但是缺点在哪里呢?当我们在修饰时,需要继承出来,产生新的类。这时当父类变化时,子类也需要跟着变化。

是不是一下看出来缺点了:

不灵活:装饰和别装饰者之间耦合度太高。

此时就有了进一步的方法

方法二(×)

首先有了tank类,接着将tank和所需添加的装饰聚合到一个TankDecorator类中,接着建立TankDecorator类。

1
2
3
TankDecorator = 
tank();
paint() -> tank.paint() + 装饰(Blood+Rect+Tail+...)

这么一看是不是比抽象要好很多了。
But,依然不够完美,比如说,我们要将装饰(尾巴,血条)装饰到子弹上,用TankDecorator就不行了,因为里面聚合的是坦克,此时就还需要写一个BulletDecorator类。

方法三(√)

分析

我们采用的就是要说的修饰器模式。

将聚合的tank -> GameObject


此时变为

1
2
Decorator = 
GameObject();

此时Decorator不是专属于子弹或坦克,它是给GameObject做装饰的,所以任何的GameObject都可以和某种具体的Decorator聚合到一起
这时,子弹也可以和某种具体的Decorator聚合到一起。例如,在子弹上添加RectDecorator和TailDecorator,需要做的是,new 一个 RectDecorator,将子弹传入,接着new 一个TailDecorator,将子弹传入进来。

代码

首先创建一个GameObject接口

1
2
3
4
GameObject.java
public interface GameObject {
void draw();
}

创建子弹实体类(Bullet)和坦克实体类(Tank)实现 GameObject 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Bullet.java
public class Bullet implements GameObject{
@Override
public void draw() {
System.out.println("GameObject:Bullet");
}
}

Tank.java
public class Tank implements GameObject{
@Override
public void draw() {
System.out.println("GameObject:Tank");
}
}

接着,创建实现了 GameObject 接口的抽象装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
GODecorator.java
public abstract class GODecorator implements GameObject{
protected GameObject decoratorGameObject;

public GODecorator(GameObject decoratorGameObject){
this.decoratorGameObject = decoratorGameObject;
}

@Override
public void draw() {
System.out.println("GameObject:GODecorator");
}
}

创建扩展了 GODecorator 类的实体装饰类。

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
28
29
30
31
32
33
34
RectDecorator.java
public class RectDecorator extends GODecorator{

public RectDecorator(GameObject decoratorGameObject) {
super(decoratorGameObject);
}

@Override
public void draw() {
super.draw();
setRectDecorator(decoratorGameObject);
}

private void setRectDecorator(GameObject decoratorGameObject) {
System.out.println(" + Rect");
}
}

TailDecorator.java
public class TailDecorator extends GODecorator{
public TailDecorator(GameObject decoratorGameObject) {
super(decoratorGameObject);
}

@Override
public void draw() {
super.draw();
setTailDecorator(decoratorGameObject);
}

private void setTailDecorator(GameObject decoratorGameObject) {
System.out.println(" + Tail");
}
}

使用 RectDecorator 和 TailDecorator 来修饰 GameObject 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {

public static void main(String[] args) {
Tank tank = new Tank();
//给坦克加上外壳
GODecorator rectTank = new RectDecorator(tank);
GODecorator tailTank = new TailDecorator(tank);
tank.draw();
rectTank.draw();
tailTank.draw();

Bullet bullet = new Bullet();
GODecorator rectTank1 = new RectDecorator(bullet);
GODecorator rectTailTank = new TailDecorator(rectTank1);
rectTailTank.draw();
}

}

执行步骤,输出结果

1
2
3
4
5
6
7
8
GameObject:Tank
GameObject:GODecorator
+ Rect
GameObject:GODecorator
+ Tail
---------------
GameObject:GODecorator
+ Tail

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!