servant/README.md
The Servant pattern is used to perform specific operations for a group of objects without changing the classes of the elements on which it operates.
Real-world example
In a restaurant, a waiter (Servant) serves multiple tables (objects) by taking orders, delivering food, and handling payments. The waiter provides these common services without altering the fundamental nature of the tables or customers, allowing the restaurant to efficiently manage customer service while keeping the roles of the tables and customers unchanged.
In plain words
The Java Servant pattern effectively centralizes common functionality for multiple classes, enabling decoupled and reusable operations in software development without altering the original classes, promoting efficient Java design practices.
Wikipedia says
In software engineering, the servant pattern defines an object used to offer some functionality to a group of classes without defining that functionality in each of them. A Servant is a class whose instance (or even just class) provides methods that take care of a desired service, while objects for which (or with whom) the servant does something, are taken as parameters.
Sequence diagram
The Servant design pattern is a behavioral design pattern that defines a class that provides some sort of service to a group of classes. This pattern is particularly useful when these classes lack some common functionality that can't be added to the superclass. The Servant class brings this common functionality to a group of classes.
In the provided code, we have a Servant class that provides services to the Royalty class. The Servant class has methods like feed(), giveWine(), and giveCompliments() which are services provided to the Royalty class.
Here is the Servant class:
public class Servant {
public String name;
public Servant(String name) {
this.name = name;
}
public void feed(Royalty r) {
r.getFed();
}
public void giveWine(Royalty r) {
r.getDrink();
}
public void giveCompliments(Royalty r) {
r.receiveCompliments();
}
public boolean checkIfYouWillBeHanged(List<Royalty> tableGuests) {
return tableGuests.stream().allMatch(Royalty::getMood);
}
}
The Royalty class is an interface that is implemented by the classes that use the services of the Servant. Here is a simplified version of the Royalty interface:
public interface Royalty {
void getFed();
void getDrink();
void receiveCompliments();
void changeMood();
boolean getMood();
}
The App class uses the Servant to provide services to the Royalty objects. Here is a simplified version of the App class:
public class App {
private static final Servant jenkins = new Servant("Jenkins");
private static final Servant travis = new Servant("Travis");
public static void main(String[] args) {
scenario(jenkins, 1);
scenario(travis, 0);
}
public static void scenario(Servant servant, int compliment) {
var k = new King();
var q = new Queen();
var guests = List.of(k, q);
servant.feed(k);
servant.feed(q);
servant.giveWine(k);
servant.giveWine(q);
servant.giveCompliments(guests.get(compliment));
guests.forEach(Royalty::changeMood);
if (servant.checkIfYouWillBeHanged(guests)) {
LOGGER.info("{} will live another day", servant.name);
} else {
LOGGER.info("Poor {}. His days are numbered", servant.name);
}
}
}
Running the application produces:
09:10:38.795 [main] INFO com.iluwatar.servant.App -- Jenkins will live another day
09:10:38.797 [main] INFO com.iluwatar.servant.App -- Poor Travis. His days are numbered
In this example, the Servant class provides services to the Royalty objects. The Servant class doesn't know about the specific implementation of the Royalty objects, it only knows that it can provide certain services to them. This is a good example of the Servant design pattern.
Benefits:
Trade-offs: