有效地使用Decorator模式

时间:2011-12-11 06:48:27

标签: c# design-patterns architecture decorator

我正在设计一个库,可以访问我们公司使用的Bug Tracker应用程序。

目前,我们只需要访问简单的功能:

  1. 打开缺陷
  2. 查询缺陷(按给定标准搜索)
  3. 连接/断开与Bug Tracker的连接
  4. 我设计了库来提供这些操作的接口,这样我们就可以在新版本发布时透明地替换实现。

    为了支持将来的更多运营,我们可以:

    1. 展开界面;让所有实现类实现添加的成员。
    2. 使用Decorator模式在运行时添加操作/功能。
    3. 问题是 - Decorator似乎与底层基类/接口过于紧密。 我的意思是,它依赖于它所装饰的对象提供了足够的访问权限,以便让它轻松添加操作。

      在这个例子中,如果我没有在我的界面中公开为bug追踪器提供API的底层第三方对象,装饰者将无法添加更多操作。

      如何通过更好地设计来克服这个问题?

1 个答案:

答案 0 :(得分:3)

您不需要装饰器模式

装饰器模式对于这种情况不是很有用。其目的是允许您在运行时附加行为 。您正试图在编译时附加行为(或多或少)

请参阅:http://en.wikipedia.org/wiki/Decorator_pattern

此模式允许用户创建这些类型的代码实例:

  • 有意未修饰的类型,仅具有基本行为
  • 允许您提供重写行为的任意装饰类型

为此,它会产生大量的设计开销。对于一个装饰,您需要实现四个类。

如果您不关心实例化故意未修饰的类型,这将是一个很大的开销。

您可能根本不需要担心这个

如果您已经推出了一个界面,我只会担心这种情况,一次重新部署所有内容(客户端和服务)会很昂贵。如果您可以一次性重新部署整个世界,那么只需修改界面即可。

多个界面更适合您的场景

如果您只想在交错部署时保持服务和客户端运行,我建议您利用.Net允许接口的多重继承以及接口版本的事实。

发布修改界面的新版本时,添加新界面并实现两者。使您的对象实现两个接口。

这将允许您错开展开并保持向后兼容。您可以在对时间表最有意义的任何时间弃用和删除旧接口。

这是一个例子。请注意,除了解决您的具体问题外,我根本没有考虑过服务的设计:

namespace Version1
{
    public interface IOpenDefectService
    {
        void Submit(string title, string description, int severity);
        void Bump(int issueId);
    }
}

namespace Version2
{
    public interface IOpenDefectService
    {
        void Submit(string title, string description, int severity);

        // Removed Bump method - it was a bad idea

        // Added an optional priority field
        void Submit(string title, string description, int severity,
            int priority);

        // Added support for deleting 
        void Delete(int id);
    }
}

public class OpenDefectService : Version1.IOpenDefectService,
    Version2.IOpenDefectService
{
    // Still must support it until you no longer have any clients using it.
    // Here to support staggered rollouts
    [Obsolete("This method is deprecated.  Don't use it")]
    public void Bump(int issueId) { /* Still has implementation... */ }

    public void Submit(string title, string description,
        int severity) { /* ... */ }
    public void Submit(string title, string description,
        int severity, int priority) { /* ... */ }
    public void Delete(int id) { /* ... */ }
}