Code Copied

Unity系列教程(五)Unity和Decorator模式

1. 横切关注点

横切关注点涉及到应用程序中的很多地方。比如,在一个LOB引用程序中,你可能需要应用程序的不同地方将信息写入日志文件。
横切关注点指的是一些具有横跨多个模块的行为,使用传统的软件开发方法不能够达到有效的模块化的一类特殊关注点。

常见的LOB应用程序的横切关注点包括:

  • 日志:在日志中写入诊断信息,用于进行故障排除、跟踪或审计目的。
  • 验证:验证用户的输入或其他系统的输入是否符合一定的规则。
  • 异常处理:使用共通的方式进行异常处理。
  • 瞬态故障处理:使用共通的方式来识别瞬时性故障和重试操作。
  • 身份验证和授权:通过验证调用者的身份来判定调用者是否有权限执行操作。
  • 缓存:缓存经常使用的对象和资源来提高性能。
  • 性能监视:收集性能数据,以衡量是否遵守SLA。
  • 加密:使用共通的服务来加密和解密消息。
  • 映射:当数据在类或组件之间移动时,提供一个数据映射或翻译服务。
  • 压缩:在类或组件之间提供一个数据压缩服务。

在应用程序中,许多类和组件通常需要实现一些行为(而这些行为经常会是一些上面我们所提到的横切关注点)。但是,在一个LOB应用程序中实现这些横切关注点时,将会带来一些列的挑战,诸如:

  • 保持一致性:我们需要确保所有的类和组件在实现某个横切关注点时使用的是一致的方式。并且当我们更改了这个横切关注点时,需要确保这一变化能应用到所有的地方。
  • 创建可维护的代码:单一职责原则能够有助于我们的代码更容易维护。一个实现业务功能的类不应该负责实现一个横切关注点,例如日志记录。
  • 避免重复的代码:在应用程序的不同地方不应该出现相同的代码。

Unity的拦截可以帮助我们解决应用程序中的一些横切关注点。
虽然Untiy实现了拦截,但是我们也应该研究一些其他的途径来解决横切关注点。
例如Decorator模式或者AOP。

2. Decorator模式

一个共通的方法来实现一个横切关注点就是使用Decorator模式。Decorator模式的关键就是创建包装器(Wrapper),通过包装器动态地给对象添加一些职责来实现横切关注点。

image

假设在客户的消息输出问题中有以下需求:

  1. 对于一些确定的消息,必须先验证用户才能输出到文件。
  2. 对于一些消息涉及到隐私,通过加密的方式输出到文件,从而让其他人不能够从文件中读取到隐私消息。
  3. 对于一些消息,既不需要验证也不需要加密,直接输出到文件就可以了。

下面的代码中定义了一个IMessageWriter接口和实现它的MessageWriter类。

IMessageWriter接口:

public interface IMessageWriter
{
    string Message { get;set; }
    void WriterMessage(string filePath);
}

MessageWriter类:

public class MessageWriter : IMessageWriter
{
    public string Message { get; set; }

    public virtual void WriterMessage(string filePath)
    {
        File.WriteAllText(filePath, Message);
    }
}

EncryptedMessageWriter类:

public class EncryptedMessageWriter : IMessageWriter
{
    private string _message;
    // 被装饰的组件
    private readonly IMessageWriter _messageWriter;

    public EncryptedMessageWriter(IMessageWriter messageWriter)
    {
        this._messageWriter = messageWriter;
    }
    public string Message
    {
        set { _message = value; }
    }

    public void WriterMessage(string filePath)
    {
        // 添加的行为(加密)
        this._messageWriter.Message = EncodeBase64();
        this._messageWriter.WriterMessage(filePath);
    }

    private string EncodeBase64()
    {
        Encoding encode = Encoding.UTF8;
        byte[] bytes = encode.GetBytes(this._message);
        string result = Convert.ToBase64String(bytes);
        return result;
    }
}

SecureMessageWriter类:

public class SecureMessageWriter : IMessageWriter
{
    private string _message;
    // 被装饰的组件
    private readonly IMessageWriter _messageWriter;

    public SecureMessageWriter(IMessageWriter messageWriter)
    {
        this._messageWriter = messageWriter;
    }

    public string Message
    {
        set { _message = value; }
    }

    public void WriterMessage(string filePath)
    {
        // 添加的行为(验证)
        if (this.ValidateUser())
        {
            this._messageWriter.Message = this._message;
            this._messageWriter.WriterMessage(filePath);
        }
        else
        {
            Console.WriteLine("User Validation failed...");
        }
    }

    private bool ValidateUser()
    {
        return true;
    }
}

通过Decorator模式我们能够灵活地组合消息的输出方式:

  1. 无验证、无加密消息输出
  2. 有验证、无加密消息输出
  3. 无验证、有加密消息输出
  4. 有验证、有加密消息输出
static void Main(string[] args)
{

    var filePath = @"D:\message.txt";

    // 1.无验证、无加密消息输出
    IMessageWriter msgWriter1 = new MessageWriter();
    msgWriter1.Message = "123456";
    msgWriter1.WriterMessage(filePath);

    // 2.有验证、无加密消息输出
    IMessageWriter secureMsgWriter = new SecureMessageWriter(msgWriter1);
    secureMsgWriter.Message = "123456";
    secureMsgWriter.WriterMessage(filePath);

    // 3.无验证、有加密消息输出
    IMessageWriter encryptedMsgWriter = new EncryptedMessageWriter(msgWriter1);
    encryptedMsgWriter.Message = "123456";
    encryptedMsgWriter.WriterMessage(filePath);

    // 4.有验证、有加密消息输出
    IMessageWriter secureEncryptMsgWriter = new SecureMessageWriter(encryptedMsgWriter);
    secureEncryptMsgWriter.Message = "123456";
    secureEncryptMsgWriter.WriterMessage(filePath);

    Console.ReadKey();
}

3. 使用Unity连接装饰链

Register代码:

public class UnityConfig
{
    public static void RegisterTypes(IUnityContainer container)
    {
        container.RegisterType<IMessageWriter, MessageWriter>("BasicMessageWriter");

        container.RegisterType<IMessageWriter, EncryptedMessageWriter>("EncryptedMessageWriter",
            new InjectionConstructor(new ResolvedParameter<IMessageWriter>("BasicMessageWriter")));

        container.RegisterType<IMessageWriter, SecureMessageWriter>("SecureMessageWriter",
            new InjectionConstructor(new ResolvedParameter<IMessageWriter>("BasicMessageWriter")));

        container.RegisterType<IMessageWriter, SecureMessageWriter>("EncryptSecureMessageWriter",
            new InjectionConstructor(new ResolvedParameter<IMessageWriter>("EncryptedMessageWriter")));
    }
}

Reslove代码:

using (IUnityContainer container = new UnityContainer())
{
    UnityConfig.RegisterTypes(container);

    // 1.无验证、无加密消息输出
    var messageWriter1= container.Resolve<IMessageWriter>("BasicMessageWriter");
    messageWriter1.Message = "123456";
    messageWriter1.WriterMessage(filePath);

    // 2.有验证、无加密消息输出
    var messageWriter2 = container.Resolve<IMessageWriter>("EncryptedMessageWriter");
    messageWriter2.Message = "123456";
    messageWriter2.WriterMessage(filePath);

    // 3.无验证、有加密消息输出
    var messageWriter3 = container.Resolve<IMessageWriter>("SecureMessageWriter");
    messageWriter3.Message = "123456";
    messageWriter3.WriterMessage(filePath);

    // 4.有验证、有加密消息输出
    var messageWriter4 = container.Resolve<IMessageWriter>("EncryptSecureMessageWriter");
    messageWriter4.Message = "123456";
    messageWriter4.WriterMessage(filePath);
}

 

4. 参考和引用

http://www.codeproject.com/Articles/185798/Decorator-Design-Pattern

http://www.codeproject.com/Articles/479635/UnderstandingplusandplusImplementingplusDecoratorp

http://www.codeproject.com/Articles/73393/Decorator-Pattern-A-Layman-to-Laymen

http://www.codeproject.com/Articles/511874/Understanding-the-Decorator-Pattern

http://msdn.microsoft.com/en-us/library/dn178467(v=pandp.30).aspx