【问题标题】:Design Pattern Decorator设计模式装饰器
【发布时间】:2015-03-08 05:59:52
【问题描述】:

我第一次看到 IO 类中的装饰器模式。现在,我正在观看有关复数视图、封装和 SOLID 的课程,该课程讨论了装饰器模式的另一种用法。这是类图。

                            MessageStore
                                 |
                            <interface>
                            IStoreReader
                                 | 
    ---------------------------------------------------------
    |                            |                          |
StoreLogger                   StoreCache                  FileStore

消息存储是一个可以通过接口 IStoreReader 从任何文件或数据库中读取的类。该类还有一种缓存底层 IStoreReader (FileStore) 返回的数据的方法。最后,在读取 StoreCache 之前和之后记录 MessageStore 的整个操作。他应用了装饰器模式,所以初始化的类是这样的:

IStoreReader fileStore = new FileStore( some arguments); 
IStoreReader cache = new FileStore(fileStore);
IStoreReader storeLogger = new StoreLogger(cache);
MessageStore messageStore = new MessageStore(storeLogger);

IStoreReader 的唯一方法是 read(int id)。

代码是这样的:

StoreLogger :
    public List<String> read(int id){
         log.information("Reading ...");
         return this.storeReader.read(id);
         log.information("Done reading ...");
    }

StoreCache :
    public List<String> read(id){
         List<String> result = this.cache.find(id);

         if(result.isEmpty()){
            return this.storeReader.read(id);
         }

         return result;
    }

FileStore:

    public List<String> read(id){
         //implementation
    }

由于 MessageStore 仅与一个 IStoreReader 交互,因此仅传递了 StoreLogger 的实例,这就是困扰我的事情。如果将 3 个类传递给 MessageStore(因为我相信这在某种程度上是一个服务类?)并且唯一的接口可以用 3 个更具体的接口代替,解决方案不是更好吗?可读性是此设计中的另一个问题。

另外,当我第一次看到如何使用装饰器模式时,我注意到它的目的是为传递的那个添加新值;正如在 IO 中观察到的,如 InputStream、BufferedInputStream 等。

请帮助我深入了解这一点。

【问题讨论】:

    标签: design-patterns decorator


    【解决方案1】:

    装饰器是一种非常强大的设计模式,因为它包装了现有的类并在其上添加了更多功能,例如在您的情况下进行日志记录和缓存。并且接口的用户不需要知道正在使用哪个特定的实现。

    如果将 3 个类传递给 MessageStore(因为我相信这在某种程度上是一个服务类?)和 唯一的接口可以换成3个更具体的接口吗?

    不,因为如果您需要使用配置禁用缓存,您会怎么做。 MessageStore 是否需要知道缓存已禁用?如果提供给MessageStore 的对象不缓存会更好。这就是多态和装饰器的威力。

    使用缓存:

    IStoreReader reader = new StoreCache(new StoreLogger(new FileStore("file")));
    

    没有缓存:

    IStoreReader reader = new StoreLogger(new FileStore("file"));
    

    因此,使用装饰器时发生的唯一变化是如何创建对象并将它们连接在一起。我认为这是一个更好的设计,因为只需将单个对象传递给MessageStore

    就日志记录而言,它也可以使用 AOP 完成,但它是开始使用装饰器的一个很好的例子。

    如果您还有任何疑问,请告诉我。

    【讨论】:

      【解决方案2】:

      装饰器模式的重点是动态组合“动作”,链接在一起。对于您的示例,让我们看看它如何使您受益(与 MessageStore 采用 3 个特定接口而不是 1 个抽象接口相比。

      如果第一个失败,请考虑您要提供一些“备份”存储。然后,您可以使用以下 read 函数轻松创建 StoreBackup 并获取两个 IStoreRead 实例:

      public List<String> read(id){
          List<String> result = this.mainStore.read(id);
      
          if(result.isEmpty()){
              result = this.backupStore.read(id);
          }
      
          return result;
      }
      

      另一个例子:假设您不想记录“缓存命中”,而只记录真实的存储访问。然后你只需将组合顺序更改为

      new StoreCache(new StoreLogger(new FileStore(...)))
      

      当然,这也可以通过修改 MessageStore 来完成,但需要您更改其代码(引入新的控制变量、方法等),而不仅仅是更改客户端代码。

      【讨论】:

        猜你喜欢
        • 2010-10-05
        • 2013-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-19
        相关资源
        最近更新 更多