Skip to content

Implementing cross-cutting concerns

Technique · Chapter 9

Implementations should honor the design goals: consistency, no scattering, no tangling. Three approaches: dependency injection (DI), the decorator pattern, and AOP.

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.

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.

  • Software Architect’s Handbook (Packt, 2018), Ch.9 “Implementing cross-cutting concerns”, pp. 699-705.