Category
Organizing Data
Techniques that turn raw fields, magic values, arrays and type codes into objects and types that carry meaning and protect their own invariants.
What these techniques solve
Data that is only strings, numbers and loose arrays spreads its rules everywhere. Every caller re-validates an email, remembers that index 2 is the currency, or checks type === 3. These techniques give data a proper home: small value objects, encapsulated fields and collections, named constants, and real types in place of codes.
Make illegal states hard to represent and put the rules for a value next to the value.
Members
- Self Encapsulate Field: access a field through getters and setters even inside its own class.
- Replace Data Value with Object: turn a primitive with behavior attached to it into a small class.
- Change Value to Reference: replace many equal copies of an entity with one shared instance.
- Change Reference to Value: turn a small, rarely changing reference object into an immutable value.
- Replace Array with Object: replace an array whose positions mean different things with an object with named fields.
- Duplicate Observed Data: move domain data out of a UI class and keep the two in sync.
- Change Unidirectional Association to Bidirectional: add a back-reference when both sides need to navigate to each other.
- Change Bidirectional Association to Unidirectional: drop a back-reference that one side no longer needs.
- Replace Magic Number with Symbolic Constant: give an unexplained literal a name.
- Encapsulate Field: make a public field private and add accessors.
- Encapsulate Collection: return a read-only view of a collection and provide add and remove methods.
- Replace Type Code with Class: replace a code that doesn't affect behavior with a dedicated type or enum.
- Replace Type Code with Subclasses: replace a fixed code that drives behavior with one subclass per value.
- Replace Type Code with State/Strategy: replace a code that drives behavior and changes at runtime with a state or strategy object.
- Replace Subclass with Fields: collapse subclasses that differ only in constant values into fields.
How to choose within this category
- A primitive carries rules (format, range, unit): Replace Data Value with Object. In TypeScript, a branded type or a small class works; in Python, a frozen dataclass; in Java, a record.
- Positions in an array or tuple mean different things: Replace Array with Object.
- A literal like
86400or0.2appears in logic: Replace Magic Number with Symbolic Constant. - Outside code writes fields or mutates a list directly: Encapsulate Field and Encapsulate Collection. Language properties (C#, Kotlin, Python
@property, TypeScriptget) let you add the accessor later without breaking callers, so don't add boilerplate getters up front. - Type codes: ask two questions. Does the code change behavior? If not, use Replace Type Code with Class; a language enum or string-literal union is often enough. Does the code change after creation? If not, use Replace Type Code with Subclasses; if it does, use Replace Type Code with State/Strategy. If subclasses exist only to return different constants, reverse it with Replace Subclass with Fields.
- Identity questions: the same customer loaded twice and updated in one place calls for Change Value to Reference. A small object that would be safer immutable calls for Change Reference to Value.
- Associations: add a back-reference only when navigation is needed in both directions, since it doubles the bookkeeping. Remove it as soon as it isn't.
- Domain data trapped in a UI component: Duplicate Observed Data. Modern UI frameworks with a separate state store often give you this split for free.
- Subclasses or lazy-loading need to intercept field access: Self Encapsulate Field. Skip it otherwise.
See also
- Composing MethodsTechniques that split, merge and reshape method bodies so each method does one readable thing and local variables stop blocking change.
- Simplifying Conditional ExpressionsTechniques that flatten, name, merge and replace conditionals so branching logic reads as a list of plain decisions.
- Primitive ObsessionDomain concepts like money, emails, IDs or statuses are passed around as raw strings and numbers, so their rules get re-checked everywhere.
- Data ClumpsThe same group of variables travels together through fields and parameters, signaling a missing object that should own them.
- Data ClassA class holds fields and accessors but no behavior, while other classes reach in to do the work that belongs to it.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.