Implementing cross-cutting concerns
Technique · Chapter 9
Implementing cross-cutting concerns
Section titled “Implementing cross-cutting concerns”Implementations should honor the design goals: consistency, no scattering, no tangling. Three approaches: dependency injection (DI), the decorator pattern, and AOP.
Dependency injection (DI)
Section titled “Dependency injection (DI)”Inject cross-cutting dependencies (e.g. a logger, a cache) into the classes that need them, coding against interfaces rather than concrete types.
Benefits
- Loosely coupled code; eliminates scattering (no duplicated implementation).
- Swap the implementation at runtime or compile-time (e.g. by configuration) as long as a common interface is shared — no recompile/redeploy needed to change behavior.
- Improves testability: consumers depend on the abstraction, so dependencies can be mocked in unit tests.
Drawbacks
- You must inject the dependency everywhere it’s needed; tedious for pervasive concerns like logging.
- Eliminates scattering but not tangling — the injected logger/cache calls still sit mixed in with the class’s other logic.
Decorator pattern
Section titled “Decorator pattern”Wrap an object to add behavior dynamically, including cross-cutting behavior.
- A concrete component implements an interface; an abstract decorator implements the same interface and holds a reference to a component.
- Each concrete decorator inherits the abstract decorator and adds its behavior, calling the wrapped component before or after its own cross-cutting logic.
- Because every decorator shares the component’s interface, decorators can be stacked (e.g. a caching decorator wrapping a logging decorator wrapping the real service) to combine multiple concerns.
- Often used together with DI; a DI container can resolve the decorator chain automatically so you receive a fully decorated instance without manual wiring.
Benefits: neither scattered nor tangled with core logic. Drawbacks:
- You must author a decorator class per concern; large systems need many decorators (tedious, repetitive — though code generation can help).
- Cross-cutting logic can only run before/after the wrapped call, not in the middle of core logic. Workaround: keep methods small so there are more seams where advice can run.
The third approach — see Aspect-oriented programming — modularizes each concern into an aspect and weaves it into core logic automatically.
Related
Section titled “Related”- DI is grounded in dependency inversion; see Following SOLID design principles.
- Design goals these approaches serve: General guidelines for cross-cutting concerns.
Citations
Section titled “Citations”- Software Architect’s Handbook (Packt, 2018), Ch.9 “Implementing cross-cutting concerns”, pp. 699-705.