Reputation: 39
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
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
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