fflajom
fflajom

Reputation: 39

Design Pattern Decorator

I have first seen the decorator pattern in the IO classes. Now, I am watching a course in pluralsight, Encapsulation and SOLID, in which the course discussed another use of the decorator pattern. Here is the class diagram.

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

The Message store is a class that can read from any file or database through the interface IStoreReader. Also the class has a way of caching the data return by the underlying IStoreReader (FileStore). Lastly, the whole operation of the MessageStore is being logged, before and after reading through the StoreCache. He applied decorator pattern so the classes where initialized like this:

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

The IStoreReader's sole method is read(int id).

The code is something like this :

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
    }

Since MessageStore only interacts to one IStoreReader, only the instance of the StoreLogger was passed and that's the thing that's been bugging me. Isn't the solution be better if the 3 classes were passed to the MessageStore (because I believe this is somewhat a service class?) and the sole interface can be replaced by 3 more specific interfaces? Readability is another concern in this design.

Also, when I first saw how the decorator pattern is being used I noticed that its purpose is to add new value to the one that was passed; as observed in the IO like InputStream, BufferedInputStream, etc.

Please help me gain insight on this one.

Upvotes: 1

Views: 80

Answers (2)

user2512323
user2512323

Reputation:

The whole point of decorator pattern is to have dynamic composition of 'actions', chained together. For your example, lets see how it benefits you (in comparison with MessageStore taking 3 specific interfaces instead of 1 more abstract.

Consider that you want to provide some 'backup' store, if the first one fails. Then you can easily create StoreBackup taking two IStoreRead instances with following read function:

public List<String> read(id){
    List<String> result = this.mainStore.read(id);

    if(result.isEmpty()){
        result = this.backupStore.read(id);
    }

    return result;
}

Another example: let's say you don't want to log 'cache hits', and only real store accesses. Then you simply change composition order to be

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

This can be done with modified MessageStore too, of course, but requires you to change its code (introduce new control variables, methods, etc), instead of just changing client code.

Upvotes: 1

Narendra Pathai
Narendra Pathai

Reputation: 41945

Decorator is a very powerful design pattern as it wraps the existing classes and adds more functionality over it, such as logging and caching in your case. And the user of the interface does not need to know which particular implementation is being used.

Isn't the solution be better if the 3 classes were passed to the MessageStore (because I believe this is somewhat a service class?) and the sole interface can be replaced by 3 more specific interfaces?

No, because think what would you do if you needed to disable the caching using configuration. Does MessageStore need to know that caching is disabled? It would be better if the object that is provided to MessageStore is such that it does not cache. That is the power of polymorphism and decorator.

With caching:

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

Without caching:

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

So the only change that occured when you use decorator is how you create the objects and wired them together. I think it is a better design as just a single object needed to be passed to MessageStore.

As far as the logging is concerned it can be done using AOP as well but it is a good example to get started with decorator.

Let me know if you have any further doubts.

Upvotes: 1

Related Questions