As you might remember, Joshua Kerievsky came up with "Refactoring To Patterns" in 2004. The basic idea behind his excellent book is to identify places in an implementation that use proprietary solutions for problems which should be better solved using patterns. I also want to give you an example for this approach. Suppose, you have implemented a class ShareValues that contains a list of consumers. Whenever a share value changes, you iterate through the list and call event handler methods on each of these objects. While simple to implement, the flip side of this approach is the tight coupling unnecessarily introduced. As soon as new consumers are added, we have to manually modify the ShareValues's list of consumers. Obviously, applying the Observer pattern comes to our rescue here. The advantage of refactoring to patterns is that we are able to introduce a higher abstraction level.
If you think that even further, we enter the world of what I call Architecture Refactoring. In Architecture Refactoring we refactor the architecture itself.
- Partition Responsibilities: If a component or subsystem got too many responsibilities, partition the component or subsystem into multiple parts, each of which with semantically related functionality.
- Extract Service: If a subsystem does not provide any interfaces to its environment but is subject of external integration, extract service interface.
- Introduce decoupling layer: If components directly depend on system details, introduce decoupling layer(s).
- Rename Entity: If entities got unintuitive names, introduce appropriate naming scheme.
- Break Cycle: When encountering a cycle on subsystem level, break it.
- Merge functionality: If there is broad coheshion between two modules, merge them.
- Orthogonalize: If two parts of an architecture introduce different solutions for the same problem, choose one preferred solution and eliminate the other.
- Introduce strict layering: If in a layered system, a layer accesses lower layers without necessity (relaxed layering), enforce strict layering.
- Introduce hierarchies: If several entities are only variants of a particular entity, introduce a hierarchy.
- Introduce Interceptor hooks: If we have to open an architecture for out-of-band functionality according to the Open/Close principle interceptors should be introduced.
- Eliminate dependencies by dependency injection: Reduce direct and wide-spread dependencies of Parts in a Whole/Part setting by introducing a central runtime component (Whole´) that centralizes dependency handling with dependency injection.
So far, these are only suggestions of potential candidates. But you can see the possibilites now. Obviously, we should collect a whole catalog of such architecture refactorings. Note, that higher-level refactorings might incur lower-level refactorings the same way architecture patterns often refer to design patterns. Extract Service will very likely lead to an Extract Interface refactoring if the implementation already exists. Orthogonalize is heavily leveraging refactoring to patterns.
From my viewpoint, we have just started to understand and apply refactorings in a more holistic context. As the old saying goes, we have just seen the tip of the iceberg. Architecture Refactorings will guide architects to identify potential problems in a software architecture and also provide them with refactorings to solve those issues.