Skip to content
aipatterns.dev

Category

Moving Features between Objects

Techniques that relocate methods, fields and whole classes so each responsibility lives next to the data it uses.

What these techniques solve

Code ends up in the wrong place. A method uses another class's fields more than its own, a class has grown two unrelated jobs, or callers walk long chains of getters to reach what they need. These techniques move behavior and data across class boundaries, or redraw the boundaries themselves.

Put each piece of behavior where the data it reads lives, and hide how objects are connected from the code that uses them.

Members

  • Move Method: move a method to the class it uses most and leave a delegate or update callers.
  • Move Field: move a field to the class whose methods actually read and write it.
  • Extract Class: split a class that does two jobs into two classes.
  • Inline Class: fold a class that does almost nothing into the class that uses it.
  • Hide Delegate: add a method on the server so clients stop reaching through it to a delegate.
  • Remove Middle Man: let clients call the delegate directly when a class only forwards calls.
  • Introduce Foreign Method: add a helper in your code that takes an instance of a library class you can't edit.
  • Introduce Local Extension: create a subclass or wrapper of a library class to hold several missing methods.

How to choose within this category

  • A method reads another object's data more than its own: Move Method. If only part of the method envies the other class, extract that part first.
  • A class has fields and methods that cluster into two groups: Extract Class. The opposite case, a class too thin to justify itself, calls for Inline Class.
  • Callers write a.getB().getC().doThing(): Hide Delegate. If the server then becomes all forwarding methods, go the other way with Remove Middle Man. The right balance shifts as code changes, so expect to move between them.
  • A library class lacks one method: Introduce Foreign Method. In Kotlin, C# or Swift, an extension function does this natively. In TypeScript, Python and Go, a module-level function is the idiomatic form.
  • A library class lacks several methods: Introduce Local Extension. Prefer a wrapper over a subclass when the library class is final or its constructor is complex.
  • A field is used mostly by another class: Move Field, usually before moving the methods that use it.

Move one member at a time and run tests after each move. Cross-class moves are where silent behavior changes hide, especially with inheritance and overrides.

See also

Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.