Hexmvc Adapters - Appendix
Back to main HexMVC Pattern.
Adapter Design Patterns
In HexMvc the adapter implementing interfaces is everywhere. What pattern is this? Obviously Adapter Pattern is a leading candidate.
Talking to Interfaces vs Adapter Pattern
You could just call it the talking to Interfaces pattern. As GOF says in its introduction, one of the main two OO principles is “Program to an interface, not an implementation” (Gang of Four 1995:18). This allows clients to be decoupled from the implementation.
The swappability of implementation we gain in ‘talking to interfaces’ is not enough for HexMvc purposes. Adapters give us more flexibility e.g. the Adaptee need not know about our HexMvc architecture and can remain dumb. This is critical. The Adaptee (e.g. a model, a gui form, a utility class) shouldn’t need to change in order to participate in HexMvc.
Let’s take a step back…
Both the talking to an interface pattern and the adapter pattern have some aspects of strategy. With strategy pattern, the client talks to a ‘context’ class which uses different strategy objects to delegate the work to.
Talking to an interface is a type of strategy pattern viz. the ‘context’ that the client talks to is the interface abstraction and the strategy is the choice of implementation.
With adapter pattern, the ‘context’ that the client talks to is the adapter class (which may or may not implement an interface abstraction) and the strategy is some concrete code the adapter refers to (either by delegation or by inheritance).
There are differences in when the strategy gets swapped in. With classic strategy you can swap strategies at runtime, whilst talking to the same context object all the time. With talking to an interface pattern you swap ‘interface implementation’ strategies at compile time only. With a family of adapters pattern, this too is typically compile time only - you choose one adapter or another for your client code to talk to. Of course in dynamic languages there is a little bit more flexibility - but that’s the gist of the intents involved with these patterns.
The adapter pattern allows for some absorbtion/level of indirection between the client code and the implementation code - whereas the talking to interfaces pattern does not.
The adapter pattern, classically, allows us to adapt the implementation into a more useful interface that the client sees. This may turn out to mean a larger or smaller number of methods than the underlying implementation - depending on the situation. Furthermore, it allows for added value in the adapter, for example a higher level function in the adapter that loops and repeatedly calls a lower level function in the implementation.
Sure you could add this high level functionality into the implementation directly and not bother with the adapter pattern, sticking with the talking to an interface pattern. You may not need the
‘absorbtion/level of indirection’ between the client code and the implementation. Often it turns out that this absorbtion/level of indirection is critical, because often we don’t want to touch our implementation code. In the case of GUIs the view code is often generated by a third party tool, thus cannot be altered anyway, so the adapter gives us a place for adding our own view logic. Also the view adapter is a great place to translate from GUI events to application eventing. In the case of Models, the adapter code is a great place to add application eventing/broadcasting without contaminating the models with this stuff. In Services, the adapter is running in the same thread as the application whereas the server services thread is catering for incoming requests.
Arguably the adding of eventing is really more of a proxy pattern, as we are not so much changing an interface but adding a transparent layer of eventing around it. We might even be said to be decorating it. Then as we find ourselves adding further functionality to our adapter, we could say our adapters are in fact mediating not just adapting - PureMvc argues this. There is a valid point here e.g. my model oo adapter coordinates the underlying model and persistence objects. And the view adapter will often coordinate and update several gui controls in response to a update application event. Then as our adapters take on even more functionality, they perhaps become less adapters or mediators and become fully fledged classes doing heavy work. But pushing back on the argument a little from the other direction - just because a class becomes complex doesn’t mean it loses its ‘nature’ and role in terms of design patterns. You can have simple or complex mediators. You can have a class that performs the role of a mediator whilst at the same time performing other roles - multiple roles per class is perfectly ok. Similarly, you could argue that a thick adapter is still an adapter, and that an adapter that mediates is still an adapter - as long as the essential role of adaptation is is being performed somewhere.
The adapter pattern may be at first glance only for thin adaptation of one api for another. But when you get into the idea of a family of adapters implementing a common interface, then you are starting to get into ‘Bridge Pattern’ territory where adapters become more like ‘drivers’. When you swap out an implementation you swap out its driver as well. Then you swap in a new driver that supports some other implementation - and the rest of the system is none the wiser. This use of adapter fits perfectly with the use of adapter in HexMvc.
The adapter is the place where you implement the promised functionality of the adapter interface - only you do it in different ways depending on the implementation. Try doing that with a mere ‘talking to interfaces’ pattern - you would have to actually get into your implementation code and add the promised methods to it - a big ask. You may not have access to the adaptee implementation source code, or you may not want to change it. On the other hand with an adapter absorbing these differences, you are saved. E.g. my model adapter actually hides model specific operations that are done in different ways e.g. FindThings() lives in the model adapter and is implemented totally differently in the OO model adapter implementation vs. the SQL model adapter implementation (which utilises a fast sql query).
Adapter Pattern sequence diagram
Here is the Adapter Pattern sequence diagram:
You must use a different adapter whenever you have a different implementation behind it. What stays the same is the rest of the app which simply expects the interface of the adapter to be there.
Adapter Design Pattern
There are two classic implementations of adapter – inheritance vs. delegation. Adapt by
- Class Adapter - inheriting or
- Object Adapter - composition/wrapping more common
typically the latter is used. This is all explained in the GOF design pattern book.
Object Adapter vs. Class Adapter
- Object Adapter adapts through delegation. Class Adapter adapts through subclassing. Sometimes known as the delegation flavour and the inheritance flavor.
- Class Adapter: Why inherit – well, target may have attributes and other methods that we still want to use. Thus in the new adapter class we get to use, the old methods are still available (both a benefit and a liability).
- Though… class adaption may not work if you are not able to inherit from the old class (e.g. the adapter already iherits from something). Typically the new adapter class inherits from the old class (the one you are adapting) and implements the new adapter interface.
- Class Adapter lets you update to a smarter API without existing client code knowing (since the old methods are still there).
- Object Adapter: Wrapper. Gets its work done by forwarding requests to the helper/adaptee. Delegation.
A ‘family of adapters’ - Strategy Pattern
Having a an adapter implement an interface is not necessary for the adapter pattern - though it is common (known as the ‘Target’).
You can go further and have a family of adapters implementing an interface:
Using a family of adapters - each concrete adapter adapts a specific class.
Client talks to the Adapter interface.
When you do have a family of adapters implementing an interface, you get the potential ability to swap out adapter implementations. Thus we end up with something that has an aspect of strategy pattern here. The adapter can act as a strategy.
Just take several classes implementing the same functionality, write adapters for them, and ensure that the adapters are implementing the same interface.
We can then replace the adapters objects at run time because they implements the same interface. Client code talks to the concrete adapter through the Adapter Interface (or Adapter Base class) and thus is insulated from change – you can swap in different implementations without the client code knowing.
The family of adapters strategy pattern is not a creational pattern like the Factory Method design pattern, although the creation of the objects might be done using factory design pattern - nor is it Abstract Factory design pattern. Its more of a behavioural or structural pattern. Strategy is behavioural.
There are some similarities to the Bridge design pattern (structural) which is traditionally used for hooking up different ‘drivers’ to a system. In fact each ‘driver instance’ is an implementation of the Adapter pattern - so that fits. But HexMvc doesn’t have Bridge’s abstraction of the l.h.s. client side of things (which allows for both the client and services to vary independently) - so perhaps we are using a half-bridge, or “Bridge Essentials” pattern.
But then these are not always simple adapters - they can be complex implementations of an interface. How thin does an adapter have to be to be called an adapter? Probably my model and view adapters are too complex to be called mere adapters. The smaller utility adapters are proper adapters. The larger adapters are doing a lot more work, and are therefore not mere adapters.
But I’ll continue to use the word adapters.
PureMvc calls them proxys and mediators
Note that PureMvc shares a love of adapters, except it calls adapters by many different names. In PureMvc the model is wrapped by a proxy, the gui is wrapped by a mediator. In HexMvc I drop these distinctions and name them all adapters. We may lose some nuanced meanings (which people debate anyway - is a view mediator really a mediator and is it any different to the role done by a model proxy - is that really a proxy too?) Its simpler and more orthogonal to just make everything an adapter. Yes, adapters shouldn’t do too much work and should only adapt - so in some cases mediation would be a better word etc. But hey, its worth it to get the symmetry.
Further Adapter Discussion
Types of Adapters in HexMvc
- Major Adapters - these are Adapters for ring objects - real deep functionality e.g. two different servers or two different guis
- Minor Adapters - which wrap slightly different class libraries - insulating the app from the cold hard implementation world.
- Mock adapters - can be anywhere - even mocking a major GUI adapter is possible.
Common functionality in Adapter Base classes
If you find that different adapters are doing the same thing, Adapter Base classes can reduce duplication.
The Adapter Base class can house common implementation or virtual methods that need to be overridden by concrete adapters.
Common functionality in the controller
Another, possibly even more important place for common functionality is in the controller. For example in my sample implementation of server addapters - they don’t share a base class. Instead the controller houses deep, commong functionality like getting model info into a suitable structure for the server adpaters.
The server adapters also use utility functions for converting to json or xml etc.
Smaller Adapters not plugged into the hub
Worlds within worlds, moons…
Only the main adapters are attached directly to the central app hub - otherwise there are localised injections of adapters directly where they are needed.
Thus you sort of replicate an adapter ring around sattelite adapters, as needed. It perhaps doesn’t have the same semantics as the overall architecture, but there is some degree of similarity in that the core talks to interfaces, and those interfaces are implemented by adapters, which can be swapped in and out.
As you can see in the gui adapters (as well as in the server case, and also in the persistence case) - there are smaller adapters throughout HexMvc which are not plugged into the central hub. Examples are random functionality, json conversion, xml conversion, persistence functionality etc. These are all injected as needed into the subsystem that needs it.
Back to main HexMVC Pattern.