很多时候我们不希望一个类变得非常庞大,生来就包含很多职责。装饰器模式可以动态地给某个对象添加职责,而不会影响从这个类中派生的其他对象
为什么不用继承解决这个问题呢?如果用继承有可能会创造出数量庞大的子类,而如果把子类的属性变成装饰器,就可以灵活自由地组合了,就像天冷了要穿一件衣服等等
从结构上看,Attack 方法进入了一个包装链,先执行火焰技能,再执行了闪电技能
// 定义游戏角色接口
interface ICharacter
{
void Attack();
}
// 实现游戏角色类
class Character : ICharacter
{
public void Attack()
{
Console.WriteLine("角色进行普通攻击!");
}
}
// 装饰器基类
abstract class CharacterDecorator : ICharacter
{
protected ICharacter character;
public CharacterDecorator(ICharacter character)
{
this.character = character;
}
public virtual void Attack()
{
character.Attack();
}
}
// 具体装饰器类:添加火焰技能
class FireDecorator : CharacterDecorator
{
public FireDecorator(ICharacter character) : base(character)
{
}
public override void Attack()
{
base.Attack();
Console.WriteLine("释放火焰技能!");
}
}
// 具体装饰器类:添加闪电技能
class LightningDecorator : CharacterDecorator
{
public LightningDecorator(ICharacter character) : base(character)
{
}
public override void Attack()
{
base.Attack();
Console.WriteLine("释放闪电技能!");
}
}
class Program
{
static void Main(string[] args)
{
// 创建基本角色
ICharacter character = new Character();
// 添加火焰技能
character = new FireDecorator(character);
// 添加闪电技能
character = new LightningDecorator(character);
// 角色进行攻击
character.Attack();
// 输出结果:
// 角色进行普通攻击!
// 释放火焰技能!
// 释放闪电技能!
}
}
在这里我们使用了 C# 的 PostSharp 包,创建了方法切面监听
当 Attack 执行时,会引发 OnEntry 和 OnExit 生命周期
在分离业务代码和数据统计代码中, AOP 十分有效!
using PostSharp.Aspects;
using System;
// 日志记录切面
[Serializable]
public class LogAspect : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine($"[Log] Entering {args.Method.Name}");
}
public override void OnExit(MethodExecutionArgs args)
{
Console.WriteLine($"[Log] Exiting {args.Method.Name}");
}
}
// 游戏角色类
public class Character
{
[LogAspect] // 应用日志记录切面
public void Attack()
{
Console.WriteLine("Character attacking!");
}
}
// 使用 AOP 的游戏示例
class Program
{
static void Main(string[] args)
{
Character character = new Character();
character.Attack();
// 输出结果:
// [Log] Entering Attack
// Character attacking!
// [Log] Exiting Attack
}
}