【问题标题】:Effectively using the Decorator pattern有效地使用装饰器模式
【发布时间】:2011-12-11 06:48:27
【问题描述】:

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

目前,我们只需要访问简单的函数:

  1. 公开缺陷
  2. 查询缺陷(按给定条件搜索)
  3. 与 Bug Tracker 连接/断开连接

我设计了这个库来为这些操作提供一个接口,这样我们就可以在新版本出现时透明地替换实现。

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

  1. 扩展界​​面;让所有实现类实现添加的成员。
  2. 使用装饰器模式在运行时添加操作/功能。

问题是——装饰器似乎过于依赖底层基类/接口。 我的意思是,它依赖于这样一个事实,即它所装饰的对象提供了足够的访问权限以使其轻松添加操作。

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

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

【问题讨论】:

    标签: c# design-patterns architecture decorator


    【解决方案1】:

    你不需要装饰器模式

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

    见: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) { /* ... */ }
    }
    

    【讨论】:

    • 我还没有部署任何接口。部分问题是第一个接口支持多少(我很确定基本的操作集可能就足够了)。为什么我需要引入新界面而不是更新现有界面?
    • 另外,你能举个例子来说明运行时和编译时行为的不同吗?
    • 本回答针对:“为了以后支持更多的操作”。 “我还没有部署任何接口” - 那你就不需要引入一个新的接口,或者一个新的装饰器。只需更改您的界面:)
    • 我的意思是 - 我将使用包含 2-3 个操作的简化界面发布此功能,但希望在更新此功能以支持未来几个月内的更多操作时将工作量降至最低/年。
    • @liortal:最少的工作(如果你可以一次重新部署整个东西)就是编辑你的界面。如果您是唯一的消费者,则无需保持向后兼容,并且您可以一举替换整个应用程序。
    猜你喜欢
    • 2013-05-07
    • 1970-01-01
    • 2019-02-23
    • 2010-12-05
    • 1970-01-01
    • 2012-01-09
    • 2020-02-29
    • 1970-01-01
    • 2018-04-10
    相关资源
    最近更新 更多