Skip to content

Data classes & service classes — cheat sheet

From: fundamentals/01-data-classes.md, fundamentals/02-service-classes.md.

Core split

  • Data classes hold the application's information (the "ingredients") — they shouldn't manipulate themselves.
  • Service classes hold the logic that manipulates data classes (the "chefs").
  • Keeping these separate is a deliberate separation of concerns: data stays simple/dumb, logic stays out of the data.

Methods without static

Defined inside a class, callable only on an instance, can use this/the instance's own fields, can take parameters and return values like any method.

Visibility / encapsulation

  • private: accessible only within the declaring class.
  • public: accessible from anywhere.
  • Fields should almost always be private — external code can't read/write them directly, only through the methods the class chooses to expose.
  • Expose only what's essential as public; keep steps/details that could be misused private. Classic example: a Dishwasher.wash() is public, but the individual steps (prepareHotWaterWithDetergent, clean, rinse, drainWater) are private so no one can call them out of order.
  • This idea — hide the internals, expose a safe interface — is encapsulation.
  • Rule of thumb, not a hard rule: don't extract a private helper that's only called once just for the sake of it.

Constructors

  • A constructor has no return type, shares the class's name, and can take arguments.
  • If a class has required fields, its constructor should generally require them as arguments — avoids ending up with an invalid/incomplete instance.
  • Java gives you a no-arg constructor for free only if you don't declare any constructor yourself. As soon as you write one (with or without args), the free one disappears.
  • this refers to the current instance — mainly used to disambiguate a field from a same-named constructor/method parameter (this.name = name).

Getters and setters

  • getter: public method returning a private field's value — one per field you want readable from outside.
  • setter: public method that updates a private field after construction — one per field you want writable from outside. Not every field needs one (that's the point of keeping fields private in the first place — you decide what's exposed).

equals / hashCode / toString

Standard Object methods usually overridden on data classes (from fundamentals/04-equals-hashcode.md):

  • equals — defines value equality between two instances (default is reference/identity equality). Override to compare by field values.
  • hashCode — must stay consistent with equals (equal objects → equal hash codes), or hash-based collections (HashSet/HashMap) will misbehave (e.g. set.contains(equalByValueButDifferentInstance) can wrongly return false).
  • Define the class fully (fields, constructor, getters/setters) before generating these — adding a field afterward means manually updating the generated methods or deleting and regenerating. IntelliJ generates both together (type equals at the end of the class body, accept the override suggestion, walk through the wizard) rather than writing them by hand — worth knowing as a practical shortcut, but be able to explain what the generated code does (compares all fields, Objects.hash(...) for the hash).
  • toString — the string used when printing an instance directly; default is an unhelpful ClassName@hashcode unless overridden.

Single Responsibility Principle (SRP)

  • A service class should do one thing. Benefits: more readable, easier to reuse, less likely to need future changes, easier to maintain.
  • Not always clear-cut where the line is — more classes means more complexity to hold in your head, so deciding the right granularity is judgment, not a formula. Be ready to discuss a borderline example either way (e.g. is downloadFile + convertFile + uploadFile one FileHandler responsibility, or three?).

Composition of service classes

  • A service class can depend on other service classes to fulfill its own single responsibility, typically injected via its constructor (dependency injection, by hand).
  • Example: Restaurant(Waiter, Chef, Server)Restaurant.host() just orchestrates calls to each collaborator, each of which still has exactly one job.

Naming convention

  • Classes in PascalCase.
  • Service classes: name after the job/responsibility ("job noun") — Accountant, Translator, Mathematician — not a generic name like EverythingDoer or Manager.