65.9K
CodeProject 正在变化。 阅读更多。
Home

动态装饰器模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (12投票s)

Aug 26, 2010

CPOL

3分钟阅读

viewsIcon

69384

downloadIcon

926

在不修改类或在设计时编写装饰代码的情况下扩展对象的函数。

引言

本文介绍了一种动态装饰器模式,可用于动态地向对象添加额外功能。动态装饰器利用了 .NET 远程处理和反射技术。它消除了传统装饰器模式(GoF 设计模式之一)中所需的设计时工作。它非常灵活,在需要向对象方法添加新功能时具有很大的优势。

背景

在使用 GoF 装饰器模式时,我们需要在编译时设计装饰代码。我发现在需求发展和添加新功能时,维护代码很困难。我认为拥有一种更简单、免维护的方式来扩展对象的功能会很好。这促使我产生了动态装饰器的想法。

Using the Code

装饰器模式的核心是可以在不修改实例化对象的类的情况下,向对象方法添加新功能。假设,您的软件有一个经过良好测试的 Employee 类,该类实现了 IEmployee 接口,如下所示。

public interface IEmployee
{
    System.Int32? EmployeeID { get; set; }
    System.String FirstName { get; set; }
    System.String LastName { get; set; }
    System.DateTime DateOfBirth { get; set; }
    System.String FullName();
    System.Single Salary();
}
public class Employee : IEmployee
{
    #region Properties

    public System.Int32? EmployeeID { get; set; }
    public System.String FirstName { get; set; }
    public System.String LastName { get; set; }
    public System.DateTime DateOfBirth { get; set; }

    #endregion

    public Employee(
        System.Int32? employeeid
        , System.String firstname
        , System.String lastname
        , System.DateTime bDay
    )
    {
        this.EmployeeID = employeeid;
        this.FirstName = firstname;
        this.LastName = lastname;
        this.DateOfBirth = bDay;
    }

    public Employee() { }

    public System.String FullName()
    {
        System.String s = FirstName + " " + LastName;
        Console.WriteLine("Full Name: " + s);
        return s;
    }

    public System.Single Salary()
    {
        System.Single i = 10000.12f;
        Console.WriteLine("Salary: " + i);
        return i;
    }
}

在您的软件发布后,一些客户希望在调用 Salary 方法之前进行安全权限检查。并且您认为在方法调用前后放置一些日志信息,以便进行调试和支持,会很有用。

您可以通过几种方式实现这些新功能和需求。您可以直接修改您的 Employee 类来添加这些新功能。或者您可以使用 GoF 的装饰器模式为 Employee 的对象创建一些装饰类。或者您可以使用动态装饰器。动态装饰器实现的源代码可以在 zip 文件中找到。

在这里,我将演示如何使用动态装饰器在运行时实现这一点,而无需修改 Employee 类或创建装饰类。以下代码是您需要为 Employee 对象的这些方法添加安全检查和日志记录所需要做的全部操作。

static void Main(string[] args)
{
    IEmployee em = new Employee(1, "John", "Smith", new DateTime(1990, 4, 1));
    IEmployee tpCheckRight = (IEmployee)ObjectProxyFactory.CreateProxy(
        em,
        new String[] { "Salary" },
        new Decoration(new DecorationDelegate(UserRightCheck), null), 
        null);

    IEmployee tpLogCheckRight = (IEmployee)ObjectProxyFactory.CreateProxy(
        tpCheckRight,
        new String[] { "Salary", "FullName" },
        null, 
        new Decoration(new DecorationDelegate(ExitLog), null));

    try
    {
        tpLogCheckRight.FullName();
        Console.WriteLine("");
        tpLogCheckRight.Salary();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

private static void UserRightCheck(object target, object[] parameters)
{
    Console.WriteLine("Do security check here");
}

private static void ExitLog(object target, object[] parameters)
{
    Console.WriteLine("Do exit log here");
}

在上面的代码中,创建了 Employee 的一个实例 em。然后,使用 em 对象作为第一个参数调用 ObjectProxyFactory.CreateProxy。第二个参数是一个 string 数组,其中包含您希望添加附加功能的对象方法的名称,对于本例,是 { "Salary" }。第三个参数是 Decoration 实例。在其委托中实现的逻辑将在第二个参数中指定的方法调用之前执行。对于本例,UserRightCheck 方法被传递给委托,没有参数,它将在 Salary 方法调用之前执行。第四个参数是另一个 Decoration 实例。在其委托中实现的逻辑将在第二个参数中指定的方法调用之后执行。对于本例,在 Salary 方法调用之后,不需要执行任何逻辑。

调用 ObjectProxyFactory.CreateProxy 返回 em 对象的代理 tpCheckRight。从现在开始,您可以像使用 em 对象一样使用 tpCheckRight。例如,如果您使用 tpCheckRight 调用 Salary 方法,则将执行 UserRightCheck,然后执行 emSalary 方法。

在上面的代码中,我们传递 tpCheckRight 到第二个 ObjectProxyFactory.CreateProxy 调用中,以便在执行 em 对象的 SalaryFullName 方法之后添加 ExitLog 逻辑。

当执行上面的代码时,您将看到以下输出

Full Name: John Smith
Do exit log here

Do security check here
Salary: 10000.12
Do exit log here

正如您所看到的,ExitLog 逻辑是在对象的实际方法调用之后被调用的,而 UserRightCheck 是在对象的实际方法调用之前被调用的。

关注点

在不更改原始类的情况下,将新功能添加到对象方法中。

与 GoF 的装饰器模式相比,不需要在设计时进行任何准备工作。

通过不将各种功能混入一个强大的类中(关注点分离),可以实现更好的设计。

下一步

在文章 使用动态装饰器向对象添加方面 中,我深入研究了动态装饰器的重要功能,并讨论了如何在运行时向对象添加方面以及如何使用动态装饰器增强它们。

© . All rights reserved.