I am designing a library that provides access to the Bug Tracker application our company uses.
For the time being, we simply need access to simple functions:
- Open Defect
- Query defect (search by given criteria)
- Connect/Disconnect from Bug Tracker
I designed the library to provide an interface with these operations, so that we could transparently replace implementations when a new version comes out.
In order to support more operations in the future we could:
- Expand the interface; Make all implementing classes implement the added members.
- Use the Decorator pattern to add operations/functionality at runtime.
The problem is — Decorator seems to be too tied to the underlying base class/interface.
What i mean is, it relies on the fact that the object it decorates provides enough access to let it add operations easily.
In this example, if i do NOT expose the underlying 3rd party object providing the API for the bug tracker in my interface, a decorator will not be able to add more operations.
How can i overcome this issue by designing it better?
You don’t need the Decorator pattern
The decorator pattern isn’t very useful for this scenario. Its purpose is to allow you to append behaviors at runtime. You’re trying to append behavior (more or less) at compile time.
See: http://en.wikipedia.org/wiki/Decorator_pattern
This pattern allows the user to create these types of instances of your code:
To do this, it incurs a lot of design overhead. For only one decoration, you need to implement four classes.
This is a lot of overhead if you don’t care about instantiating intentionally undecorated types.
You might not need to worry about this at all
I would only worry about this scenario if you’ve already rolled out an interface, and it would be costly to redeploy everything at once (clients and services). If you have the luxury of redeploying the entire world at once, for all users, just modify the interface instead.
Multiple interfaces are a better match for your scenario
If you simply want to keep your services and clients running while doing a staggered rollout, I’d recommend you take advantage of the fact that .Net allows multiple inheritance for interfaces, and version your interfaces.
When releasing a new version that modifies the interface, add a new interface and implement both. Make your objects implement both interfaces.
This will allow you to stagger rollouts and remain backward compatible. You can deprecate and remove old interfaces at whatever time makes the most sense for your schedule.
Here’s an example. Note that I didn’t think out the design of the services at all, except with respect to solving your specific problem: