Service classes (course slides + memo)
Java. Covers: service classes, visibility/encapsulation, Single Responsibility Principle, combining service classes via composition.
The chef and the ingredient
The chef and the ingredient are both in the kitchen but fulfill different roles. The ingredient is essential material but can't cook itself; the chef cooks the ingredients into the final dish.
- Data classes are the ingredients — they shouldn't manipulate themselves.
- Service classes are the chefs — they contain the logic that manipulates data classes.
public class EnglishToSpanishTranslator {
public String translate(String word) {
if (word.equals("Hello")) {
return "Hola";
}
return "No hablo español";
}
}Visibility / encapsulation (the dishwasher example)
public class Dishwasher {
public void wash(List<Dish> dishes) {
prepareHotWaterWithDetergent();
clean(dishes);
drainWater();
rinse(dishes);
drainWater();
}
private void prepareHotWaterWithDetergent() { ... }
private void clean(List<Dish> dishes) { ... }
private void rinse(List<Dish> dishes) { ... }
private void drainWater() { ... }
}If the individual steps (prepareHotWaterWithDetergent, clean, rinse, drainWater) were public, callers could invoke them out of order (e.g. draining before heating, rinsing before cleaning). Making them private hides them entirely, so that mistake becomes impossible — you can only call wash(...).
- The dishwasher's essential task is
wash()— that's public. - Any method not meant to be offered externally should be private.
- Hiding everything except the essentials prevents misuse. This concept is encapsulation.
- Visibility keywords affect whatever comes right after them (a class or a method here). Java has four visibility levels; the two to know first are
public(accessible from anywhere) andprivate(accessible only within the declaring class).
Suggestion (not a hard rule): generally avoid pulling out private helper methods that are only used once — inlining the body directly in the caller can be just as readable and avoids extra indirection.
Single Responsibility Principle (SRP)
Best practice: a service class should have only one responsibility.
Benefits of keeping responsibilities down to one:
- more readable
- easier to reuse
- less likely to need changes later
- easier to maintain
Example — violates SRP:
public class EverythingDoer {
public File downloadFile() { ... }
public double convertToFahrenheit(double celsius) { ... }
public int countVowels(String text) { ... }
}Split up:
public class FileDownloader {
public File downloadFile() { ... }
}
public class TemperatureConverter {
public double convertToFahrenheit(double celsius) { ... }
}
public class VowelCounter {
public int countVowels(String text) { ... }
}Not always clear-cut:
public class FileHandler {
public File downloadFile() { ... }
public File convertFile(File file, FileFormat format) { ... }
public void uploadFile(File file) { ... }
}Does this violate SRP? Depends. Should it be split into FileDownloader/ FileConverter/FileUploader? Depends. More classes = more complexity/mental overhead; deciding exactly what counts as "one responsibility" is a judgment call built from experience, not a formula.
Combining service classes via composition
public class Restaurant {
private Waiter waiter;
private Chef chef;
private Server server;
public Restaurant(Waiter waiter, Chef chef, Server server) {
this.waiter = waiter;
this.chef = chef;
this.server = server;
}
public void host(Customer customer) {
Order order = waiter.takeOrder(customer);
Dish dish = chef.cook(order);
server.serve(dish, customer);
}
}- A service class can use other service classes to fulfill its responsibility, just like a data class can use other classes.
Restaurant's single responsibility is hosting the customer;Waiter,Chef,Servereach have exactly one responsibility of their own (take order, cook, serve).- Combining several single-responsibility classes achieves a bigger task, without any one of them taking on more than one responsibility.
- Note how the collaborating service classes are passed into
Restaurantthrough its constructor (this is dependency injection, done by hand, without a framework).
Recap: naming/shape of a service class
public classkeyword, name in PascalCase.- The name is preferably a "job noun" matching its responsibility (e.g.
Accountantfor an invoicing method,Translator,Mathematician) — not a generic name. - Essential method(s) are
public; other internal helper methods areprivate.
public class Mathematician {
public int power3(int number) {
return power2(number) * number;
}
private int power2(int number) {
return number * number;
}
}