Designing Classes and Objects

1. Defining Class Responsibilities

ToolUse
CRC cardClass, Responsibility, Collaborators
Single sentence"This class X by Y" — if "and" appears, split
Responsibility list≤ 3 cohesive actions

2. Implementing Constructors

PatternWhen
All-argsFew required fields
Static factoryNamed creation, return subtype
Builder≥ 4 fields or many optional
Copy constructorDefensive copies
TelescopingAvoid; use builder

Example: Validating constructor

public Email(String value) {
    Objects.requireNonNull(value, "email");
    if (!value.contains("@")) throw new IllegalArgumentException("invalid email");
    this.value = value.toLowerCase(Locale.ROOT);
}

3. Implementing Encapsulation

RuleImplementation
Private fieldsAlways
No settersPrefer behavior methods (deposit, not setBalance)
Return immutableList.copyOf on getters
Hide collaboratorsDon't expose internal collections directly

4. Designing Method Signatures

AspectGuideline
NameVerb phrase, intention-revealing
Param count≤ 3; group with object if more
Param orderRequired → optional → callbacks
Boolean flagsAvoid; split into two methods
Return typeMost abstract type useful to caller
Null returnsUse Optional or empty collection

5. Implementing Static vs Instance Members

MemberUse StaticUse Instance
FieldConstants, shared cachePer-object state
MethodStateless utilities, factoriesOperates on fields
MockHard (needs Mockito static)Easy

6. Designing Immutable Classes

StepDetail
Classfinal
Fieldsprivate final
SettersNone
Mutable refsDefensive copy in/out
"Modify"Return new instance (with*)

Example: Record as immutable

public record Money(long cents, Currency currency) {
    public Money plus(Money o) {
        if (!currency.equals(o.currency)) throw new IllegalArgumentException();
        return new Money(cents + o.cents, currency);
    }
}

7. Implementing Utility Classes

ConventionWhy
final classPrevent subclass
Private constructorPrevent instantiation
All staticStateless helpers
Pure functionsPredictable behavior

8. Implementing Value Objects

TraitDetail
IdentityBy value (equals/hashCode on fields)
ImmutableRequired
Self-validatingThrow in constructor
ExamplesMoney, Email, DateRange

9. Implementing Entities

TraitDetail
IdentityBy stable ID, not field values
LifecycleCreated → modified → archived/deleted
equals/hashCodeUse ID only
BehaviorMethods enforce invariants

10. Understanding Composition vs Aggregation

RelationshipLifetimeUML
CompositionOwned (dies with parent)Filled diamond
AggregationShared (independent)Hollow diamond
AssociationUses, no ownershipPlain line

11. Implementing Data Classes

Java OptionUse Case
record Java 16+Immutable data carriers
Lombok @ValuePre-Java 16 immutables
Lombok @DataMutable DTOs (use carefully)
Plain classNeed custom equality / behavior

12. Designing Object Lifecycle

Created → Initialized → Active ↔ Suspended → Archived → Disposed
   ↑                              ↑
   constructor                 invariants enforced
  
PhaseConcern
ConstructValidate all required state
InitAvoid escaping this in ctor
UseMaintain invariants per method
DisposeAutoCloseable + try-with-resources