Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Assume I have a simple interface for translating text (sample code in C#):

public interface ITranslationService
{
    string GetTranslation(string key, CultureInfo targetLanguage);
    // some other methods...
}

A first simple implementation of this interface already exists and simply goes to the database for every method call. Assuming a UI that is being translated at start up this results in one database call per control.

To improve this, I want to add the following behavior:

As soon as a request for one language comes in, fetch all translations from this language and cache them. All translation requests are served from the cache.

I thought about implementing this new behavior as a decorator, because all other methods of that interface implemented by the decorater would simple delegate to the decorated instance.
However, the implementation of GetTranslation wouldn't use GetTranslation of the decorated instance at all to get all translations of a certain language. It would fire its own query against the database.
This breaks the decorator pattern, because every functionality provided by the decorated instance is simply skipped. This becomes a real problem if there are other decorators involved.

My understanding is that a Decorator should be additive. In this case however, the decorator is replacing the behavior of the decorated instance.

I can't really think of a nice solution for this - how would you solve it? Everything is allowed, even a complete re-design of ITranslationService itself.

share|improve this question
    
Is this a decorator? It seems like a repository to me. In my opinion the easiest solution would be a simple greedy cache (memory or otherwise) in the implementation of the translation service. When the first request comes through it checks the cache, if the language it not cached it fetches the whole thing, and then returns the single value from said cache. –  Mike Jun 25 '13 at 16:18
    
@Mike: I agree, it's not a decorator. That's why I asked for a different design :-) Your comment is similar to JustinC's answer –  Daniel Hilgarth Jun 25 '13 at 16:19

2 Answers 2

up vote 1 down vote accepted

Keep the ITranslationService, but instead of implementing calls directly against to the cache or the database, send to an object IRepository that uses a list of preferred IProviders which provide access to the underlying persistence mechanisms.

The first, or generally "preferred provider", is a CacheProvider. The second, is your primary LocalDatabaseTranslationProvider. Conceivable, there could be a third or more providers. The last provider in the list is least preferred, but a reliable fallback provider. For example one might take the form of a 3rd party translation service. It would be implemented as an object proxy service like RemotePartnerTranslationProvider.

Each provider will implement a common interface that allows the strategy to differ, so no matter which provider is chosen and no matter how each provides for reads and writes, the repository acts on each and receives from each in the same way. The repository is responsible for providing coordination and conflict resolution amongst the providers when necessary.

Among other concerns, you might look at an observable interface for each provider too, for when you need to propagate/push state/signal amongst all/most providers from the repository, for instance to properly close or dispose of them when cleaning up/shutting down.

share|improve this answer
    
As far as I understand your answer, each Provider is a separate Strategy. Is this correct? –  Daniel Hilgarth Jun 25 '13 at 13:39
1  
Essentially yes. However a provider 'is a' special kind of bridge, and 'has a' unique strategy for x, y, z. The are a fundamentally a bridge because a database context is typically far different than a cache context vs a 3rd party remote service. –  JustinC Jun 25 '13 at 16:28

The feasibility of a lot of the possible options depends on how closed the current implementation of the ITranslationService interface is.

  • If the current implementation is completely closed (final class, no modifications will be accepted), then your only real options are

    • Create a separate, independent implementation of the interface
    • use your current 'decorator' approach. This can be extended to call the GetTranslation method of the decorated object if the translation could not be found in the cache.
  • If the current implementation allows sub-classes, you could provide your cached version as a derived class

  • If the current implementation is still open for modification, you could change the GetTranslation implementation to use the Strategy pattern, where one strategy always goes to the database and the other uses caching.
share|improve this answer
    
The current implementation is open for modification as it has just been developed and the iteration is not yet finished, so it hasn't been deployed yet. –  Daniel Hilgarth Jun 25 '13 at 11:44
    
Thanks for the suggestion of Strategy. That sounds like a good idea. I will have to evaluate it in the light of other requirements I have. –  Daniel Hilgarth Jun 25 '13 at 11:46

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.