鉴于当前这个号无法留言,所以我申请了新号,有兴趣的朋友可以去看看
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
引自【菜鸟教程】
优点:
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能
缺点:
多层装饰比较复杂
Component(抽象组件):具体构件与抽象装饰类的共同父类,声明具体构件中实现的业务方法,它的出现能够让客户端一致的透明的对待装饰前和装饰后的类
ConcreteComponent(具体组件):抽象构件的子类,实现具体的业务方法
Decorator(抽象装饰类):抽象构件的子类,内部维持一个抽象构件的引用,通过该引用调用具体构件的业务方法
ConcreteDecorator(具体装饰类):抽象装饰类的实现类,声明并实现各种装饰方法实现对具体构件的装饰
其实单纯的阅读上面的定义是很容易一头雾水。那么下面我尝试以游戏中的例子解释一下
今天以瘟疫之源为例,老鼠的输出主要靠平 A,在没有装备和 Buff 的加持下相当于刮痧。这些加成对于原平 A 也只是叠加上去的加成,一层套一层。就像平 A 加上了减速效果、中毒效果,而这些效果又不影响
类图就不画了,直接上代码
入口函数单元
program Decorate;
{$APPTYPE CONSOLE}
{$R *.res}
{ 装饰设计模式 }
uses
System.SysUtils,
DecorateUnit in 'DecorateUnit.pas';
begin
try
// 装饰各种特效
var RetardDecorator := TRetardDecorator.Create(TNormalAttack.Create(120));
var PoisonDecorator := TPoisonDecorator.Create(RetardDecorator);
// 制造攻击
PoisonDecorator.MakeADAttack;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
代码实现单元
unit DecorateUnit;
interface
type
// 攻击接口
INormalAttack = interface
['{D8FE89F6-8492-48E4-85BF-72C2940C2259}']
procedure MakeADAttack();
end;
// 普通攻击
TNormalAttack = class(TInterfacedObject, INormalAttack)
private
FAdAttack: Integer;
public
property AdAttack: Integer read FAdAttack write FAdAttack;
procedure MakeADAttack(); overload;
// 构造方法,用于初始化攻击值
constructor Create(AdAttack: Integer); overload;
end;
// 基础装饰器
TNormalAttackDecorator = class abstract(TInterfacedObject, INormalAttack)
private
FNormalAttack: INormalAttack;
private
FSpecialEfficacy: string;
public
property NormalAttack: INormalAttack read FNormalAttack write FNormalAttack;
// 虚函数,不做函数实现
procedure MakeADAttack(); virtual; abstract;
constructor Create(NormalAttack: INormalAttack); overload;
end;
// 减速
TRetardDecorator = class(TNormalAttackDecorator)
public
procedure MakeADAttack(); override;
constructor Create(NormalAttack: INormalAttack); overload;
end;
// 中毒
TPoisonDecorator = class(TNormalAttackDecorator)
public
procedure MakeADAttack(); override;
constructor Create(NormalAttack: INormalAttack); overload;
end;
implementation
{ TNormalAttack }
uses
System.SysUtils;
constructor TNormalAttack.Create(AdAttack: Integer);
begin
Self.AdAttack := AdAttack;
end;
procedure TNormalAttack.MakeADAttack;
begin
Writeln('初始攻击值为:' + Self.AdAttack.ToString);
end;
{ TNormalAttackDecorator }
constructor TNormalAttackDecorator.Create(NormalAttack: INormalAttack);
begin
Self.NormalAttack := NormalAttack;
end;
{ TRetardDecorator }
constructor TRetardDecorator.Create(NormalAttack: INormalAttack);
begin
inherited Create(NormalAttack);
self.FSpecialEfficacy := '减速';
end;
procedure TRetardDecorator.MakeADAttack;
begin
Self.NormalAttack.MakeADAttack();
Writeln('增加了' + self.FSpecialEfficacy + 'Buff');
end;
{ TPoisonDecorator }
constructor TPoisonDecorator.Create(NormalAttack: INormalAttack);
begin
inherited Create(NormalAttack);
self.FSpecialEfficacy := '中毒';
end;
procedure TPoisonDecorator.MakeADAttack;
begin
Self.NormalAttack.MakeADAttack();
Writeln('增加了' + self.FSpecialEfficacy + 'Buff');
end;
end.
执行结果
本来设计模式的学习我都是以 Java 先行实现一边,然后翻译成对应的 Delphi 代码。毕竟代码不熟悉,这里面有个小插曲,在这里特别说一下
abstract 关键字
我是在读系统代码的时候发现类的声明部分是可以带有这个关键字的,基本格式
type
TDemo=class abstract
end;
但是我今天遇到的问题是当 TNormalAttackDecorator 类时按照上面的格式直接报错,经群又指点修正之后问题解决了
type
TNormalAttackDecorator = class abstract(TInterfacedObject, INormalAttack)
end;
override 和 overload
代码中我全部都用 overload 然后死活编译不过,还是群里的朋友 简单 指点的。特别感谢,顺便将两者的区别整理如下
override 关键字来说明函数覆盖的。被覆盖的函数必须是虚 (virtual) 的,或者是动态 (dynamic) 的
overload 表示是重载方法;用于一个类中有许多同名的方法带着不同的参数表的情形