-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
1 changed file
with
929 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,929 @@ | ||
--- | ||
title: "DP - Design Patterns" | ||
description: "Design Patterns in Java." | ||
date: 2024-12-30T13:00:00Z | ||
draft: false | ||
--- | ||
|
||
Overview of the simplest and most common design patterns according to the book *"Design Patterns Elements of Reusable Object-Oriented Software"*: | ||
|
||
- Abstract Factory | ||
- Factory Method | ||
- Adapter | ||
- Composite | ||
- Decorator | ||
- Observer | ||
- Strategy | ||
- Template Method | ||
|
||
with additional patterns which I personally consider common as well: | ||
|
||
- Singleton | ||
- Builder | ||
- Proxy | ||
- Command | ||
- Chain of Responsibility | ||
- Mediator | ||
- Visitor | ||
|
||
# Creational Patterns | ||
|
||
**Abstract Factory**: | ||
|
||
Use when your system needs to create families of related or dependent objects without specifying their concrete classes. | ||
|
||
**Signs you might need it**: | ||
|
||
If adding new families of related objects requires significant changes to client code or tight coupling to concrete classes makes the system rigid and hard to extend. | ||
|
||
```java | ||
public interface ButtonAbstractFactory { | ||
Button createButton(); | ||
} | ||
|
||
public class WindowsButtonFactory implements ButtonAbstractFactory { | ||
public Button createButton() { | ||
return new WindowsButton(); | ||
} | ||
} | ||
|
||
public class MacButtonFactory implements ButtonAbstractFactory { | ||
public Button createButton() { | ||
return new MacButton(); | ||
} | ||
} | ||
|
||
public interface Button { | ||
void render(); | ||
} | ||
|
||
public class WindowsButton implements Button { | ||
public void render() { | ||
System.out.println("Rendering Windows Button"); | ||
} | ||
} | ||
|
||
public class MacButton implements Button { | ||
public void render() { | ||
System.out.println("Rendering Mac Button"); | ||
} | ||
} | ||
|
||
public class Application { | ||
private final Button button; | ||
|
||
public Application(ButtonAbstractFactory factory) { | ||
this.button = factory.createButton(); | ||
} | ||
|
||
public void render() { | ||
button.render(); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
ButtonAbstractFactory factory = new MacButtonFactory(); | ||
Application app = new Application(factory); | ||
app.render(); | ||
} | ||
} | ||
``` | ||
|
||
**Factory Method**: | ||
|
||
Use when a class can't anticipate the exact type of objects it must create or when the creation logic should be delegated to subclasses. | ||
|
||
**Signs you might need it**: | ||
|
||
If the code is cluttered with hardcoded object creation, making it difficult to modify or extend without altering the client code. | ||
|
||
```java | ||
public interface Button { | ||
void render(); | ||
} | ||
|
||
public class WindowsButton implements Button { | ||
public void render() { | ||
System.out.println("Rendering Windows Button"); | ||
} | ||
} | ||
|
||
public class MacButton implements Button { | ||
public void render() { | ||
System.out.println("Rendering Mac Button"); | ||
} | ||
} | ||
|
||
public abstract class ButtonFactory { | ||
public abstract Button createButton(); | ||
} | ||
|
||
public class WindowsButtonFactory extends ButtonFactory { | ||
@Override | ||
public Button createButton() { | ||
return new WindowsButton(); | ||
} | ||
} | ||
|
||
public class MacButtonFactory extends ButtonFactory { | ||
@Override | ||
public Button createButton() { | ||
return new MacButton(); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
ButtonFactory factory = new WindowsButtonFactory(); | ||
Button button = factory.createButton(); | ||
button.render(); | ||
} | ||
} | ||
``` | ||
|
||
**Singleton** | ||
|
||
Use when you need to ensure only one instance of a class exists and provide a global access point to it. | ||
|
||
**Signs you might need it**: | ||
|
||
If multiple instances of a class lead to inconsistent state or resource conflicts across the application. | ||
|
||
```java | ||
public class Singleton { | ||
private static Singleton instance; | ||
|
||
private Singleton() { | ||
// Private constructor to prevent instantiation | ||
} | ||
|
||
public static Singleton getInstance() { | ||
if (instance == null) { | ||
instance = new Singleton(); | ||
} | ||
return instance; | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
Singleton instance = Singleton.getInstance(); | ||
Singleton anotherInstance = Singleton.getInstance(); | ||
System.out.println("Instances are the same: " + (instance == anotherInstance)); | ||
} | ||
} | ||
``` | ||
|
||
**Builder** | ||
|
||
Use when constructing a complex object with many optional parameters or configurations that should be constructed step-by-step. | ||
|
||
**Signs you might need it**: | ||
|
||
If constructors become unwieldy due to numerous optional parameters or configurations, leading to unclear and error-prone code. | ||
|
||
```java | ||
public class House { | ||
private int rooms; | ||
private boolean hasGarage; | ||
private boolean hasGarden; | ||
|
||
private House(Builder builder) { | ||
this.rooms = builder.rooms; | ||
this.hasGarage = builder.hasGarage; | ||
this.hasGarden = builder.hasGarden; | ||
} | ||
|
||
public static class Builder { | ||
private int rooms; | ||
private boolean hasGarage; | ||
private boolean hasGarden; | ||
|
||
public Builder setRooms(int rooms) { | ||
this.rooms = rooms; | ||
return this; | ||
} | ||
|
||
public Builder setGarage(boolean hasGarage) { | ||
this.hasGarage = hasGarage; | ||
return this; | ||
} | ||
|
||
public Builder setGarden(boolean hasGarden) { | ||
this.hasGarden = hasGarden; | ||
return this; | ||
} | ||
|
||
public House build() { | ||
return new House(this); | ||
} | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
House house = new House.Builder() | ||
.setRooms(3) | ||
.setGarage(true) | ||
.setGarden(false) | ||
.build(); | ||
System.out.println("House built: " + house); | ||
} | ||
} | ||
``` | ||
|
||
# Structural Patterns | ||
|
||
**Adapter**: | ||
|
||
Use when you need to make two incompatible interfaces work together without altering their source code. | ||
|
||
**Signs you might need it**: | ||
|
||
If incompatible interfaces prevent the integration of existing components or systems, forcing duplication or rewriting of code. | ||
|
||
```java | ||
public interface Celsius { | ||
double getCelsiusTemperature(); | ||
} | ||
|
||
public class Fahrenheit { | ||
private double temperature; | ||
|
||
public Fahrenheit(double temperature) { | ||
this.temperature = temperature; | ||
} | ||
|
||
public double getFahrenheitTemperature() { | ||
return temperature; | ||
} | ||
} | ||
|
||
public class FahrenheitToCelsiusAdapter implements Celsius { | ||
private Fahrenheit device; | ||
|
||
public FahrenheitToCelsiusAdapter(Fahrenheit device) { | ||
this.device = device; | ||
} | ||
|
||
@Override | ||
public double getCelsiusTemperature() { | ||
double fahrenheit = device.getFahrenheitTemperature(); | ||
return (fahrenheit - 32) * 5 / 9; | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
Fahrenheit fahrenheitDevice = new Fahrenheit(98.6); | ||
Celsius temperatureAdapter = new FahrenheitToCelsiusAdapter(fahrenheitDevice); | ||
System.out.println("Temperature in Celsius: " + temperatureAdapter.getCelsiusTemperature()); | ||
} | ||
} | ||
``` | ||
|
||
**Composite**: | ||
|
||
Use when you need to represent a part-whole hierarchy and treat individual objects and groups of objects uniformly. | ||
|
||
**Signs you might need it**: | ||
|
||
If managing hierarchies of objects results in duplicated logic or complicated client code that treats individual and composite objects differently. | ||
|
||
```java | ||
public interface JSONElement { | ||
String toJSONString(); | ||
} | ||
|
||
public class JSONValue implements JSONElement { | ||
private Object value; | ||
|
||
public JSONValue(Object value) { | ||
this.value = value; | ||
} | ||
|
||
@Override | ||
public String toJSONString() { | ||
return (value instanceof String) ? "\"" + value + "\"" : value.toString(); | ||
} | ||
} | ||
|
||
public class JSONArray implements JSONElement { | ||
private List<JSONElement> elements = new ArrayList<>(); | ||
|
||
public void add(JSONElement element) { | ||
elements.add(element); | ||
} | ||
|
||
@Override | ||
public String toJSONString() { | ||
return elements.stream() | ||
.map(JSONElement::toJSONString) | ||
.collect(Collectors.joining(", ", "[", "]")); | ||
} | ||
} | ||
|
||
public class JSONObject implements JSONElement { | ||
private Map<String, JSONElement> elements = new HashMap<>(); | ||
|
||
public void add(String key, JSONElement element) { | ||
elements.put(key, element); | ||
} | ||
|
||
@Override | ||
public String toJSONString() { | ||
return elements.entrySet().stream() | ||
.map(e -> "\"" + e.getKey() + "\": " + e.getValue().toJSONString()) | ||
.collect(Collectors.joining(", ", "{", "}")); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
JSONObject root = new JSONObject(); | ||
root.add("title", new JSONValue("Design Patterns")); | ||
|
||
JSONArray authors = new JSONArray(); | ||
authors.add(new JSONValue("Gamma")); | ||
authors.add(new JSONValue("Helm")); | ||
authors.add(new JSONValue("Johnson")); | ||
authors.add(new JSONValue("Vlissides")); | ||
root.add("authors", authors); | ||
|
||
System.out.println(root.toJSONString()); | ||
} | ||
} | ||
``` | ||
|
||
**Decorator**: | ||
|
||
Use when you need to dynamically add or modify behavior to an object without affecting others. | ||
|
||
**Signs you might need it**: | ||
|
||
If extending the functionality of a class leads to an explosion of subclasses for every possible variation. | ||
|
||
```java | ||
public interface DataSource { | ||
void writeData(String data); | ||
String readData(); | ||
} | ||
|
||
public class FileDataSource implements DataSource { | ||
private String filename; | ||
|
||
public FileDataSource(String filename) { | ||
this.filename = filename; | ||
} | ||
|
||
@Override | ||
public void writeData(String data) { | ||
System.out.println("Writing data to " + filename); | ||
} | ||
|
||
@Override | ||
public String readData() { | ||
return "Reading data from " + filename; | ||
} | ||
} | ||
|
||
public class CompressionDecorator implements DataSource { | ||
private DataSource source; | ||
|
||
public CompressionDecorator(DataSource source) { | ||
this.source = source; | ||
} | ||
|
||
@Override | ||
public void writeData(String data) { | ||
source.writeData(compress(data)); | ||
} | ||
|
||
@Override | ||
public String readData() { | ||
return decompress(source.readData()); | ||
} | ||
|
||
private String compress(String data) { | ||
return "Compressed[" + data + "]"; | ||
} | ||
|
||
private String decompress(String data) { | ||
return data.replace("Compressed[", "").replace("]", ""); | ||
} | ||
} | ||
|
||
public class EncryptionDecorator implements DataSource { | ||
private DataSource source; | ||
|
||
public EncryptionDecorator(DataSource source) { | ||
this.source = source; | ||
} | ||
|
||
@Override | ||
public void writeData(String data) { | ||
source.writeData(encrypt(data)); | ||
} | ||
|
||
@Override | ||
public String readData() { | ||
return decrypt(source.readData()); | ||
} | ||
|
||
private String encrypt(String data) { | ||
return "Encrypted[" + data + "]"; | ||
} | ||
|
||
private String decrypt(String data) { | ||
return data.replace("Encrypted[", "").replace("]", ""); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
DataSource dataSource = new FileDataSource("data.txt"); | ||
DataSource compressedAndEncrypted = new CompressionDecorator(new EncryptionDecorator(dataSource)); | ||
|
||
compressedAndEncrypted.writeData("Important Data"); | ||
System.out.println(compressedAndEncrypted.readData()); | ||
} | ||
} | ||
``` | ||
|
||
**Proxy**: | ||
|
||
Use when you need to control access to an object, for purposes like lazy initialization, logging, or access control. | ||
|
||
**Signs you might need it**: | ||
|
||
If direct access to resource-intensive or sensitive objects causes performance issues or security risks. | ||
|
||
```java | ||
public interface Database { | ||
void query(String sql); | ||
} | ||
|
||
public class RealDatabase implements Database { | ||
public RealDatabase() { | ||
connectToDatabase(); | ||
} | ||
|
||
private void connectToDatabase() { | ||
System.out.println("Connecting to the database..."); | ||
} | ||
|
||
@Override | ||
public void query(String sql) { | ||
System.out.println("Executing query: " + sql); | ||
} | ||
} | ||
|
||
public class DatabaseProxy implements Database { | ||
private RealDatabase realDatabase; | ||
|
||
@Override | ||
public void query(String sql) { | ||
if (realDatabase == null) { | ||
realDatabase = new RealDatabase(); | ||
} | ||
realDatabase.query(sql); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
Database db = new DatabaseProxy(); | ||
db.query("SELECT * FROM Users"); | ||
} | ||
} | ||
``` | ||
|
||
# Behavioral Patterns | ||
|
||
**Observer**: | ||
|
||
Use when an object needs to notify multiple dependent objects of state changes, maintaining a one-to-many relationship. | ||
|
||
**Signs you might need it**: | ||
|
||
If manually updating multiple dependent objects becomes error-prone and leads to tight coupling between the subject and its observers. | ||
|
||
```java | ||
public interface Observer { | ||
void update(String message); | ||
} | ||
|
||
public class Subscriber implements Observer { | ||
private String name; | ||
|
||
public Subscriber(String name) { | ||
this.name = name; | ||
} | ||
|
||
@Override | ||
public void update(String message) { | ||
System.out.println(name + " received update: " + message); | ||
} | ||
} | ||
|
||
public class Publisher { | ||
private List<Observer> observers = new ArrayList<>(); | ||
|
||
public void subscribe(Observer observer) { | ||
observers.add(observer); | ||
} | ||
|
||
public void unsubscribe(Observer observer) { | ||
observers.remove(observer); | ||
} | ||
|
||
public void notifyObservers(String message) { | ||
for (Observer observer : observers) { | ||
observer.update(message); | ||
} | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
Publisher newsPublisher = new Publisher(); | ||
|
||
Observer alice = new Subscriber("Alice"); | ||
Observer bob = new Subscriber("Bob"); | ||
|
||
newsPublisher.subscribe(alice); | ||
newsPublisher.subscribe(bob); | ||
|
||
newsPublisher.notifyObservers("Breaking News: Design Patterns Book is Awesome!"); | ||
} | ||
} | ||
``` | ||
|
||
**Strategy**: | ||
|
||
Use when you want to define a family of algorithms, encapsulate them, and make them interchangeable at runtime. | ||
|
||
**Signs you might need it**: | ||
|
||
If hardcoded algorithms or behaviors make the system inflexible and difficult to extend or modify. | ||
|
||
```java | ||
public interface PaymentStrategy { | ||
void pay(int amount); | ||
} | ||
|
||
public class CreditCardPayment implements PaymentStrategy { | ||
@Override | ||
public void pay(int amount) { | ||
System.out.println("Paid " + amount + " using credit card."); | ||
} | ||
} | ||
|
||
public class BankTransferPayment implements PaymentStrategy { | ||
@Override | ||
public void pay(int amount) { | ||
System.out.println("Paid " + amount + " using bank transfer."); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
PaymentStrategy creditCardPayment = new CreditCardPayment(); | ||
PaymentStrategy bankTransferPayment = new BankTransferPayment(); | ||
|
||
creditCardPayment.pay(500); | ||
bankTransferPayment.pay(1000); | ||
} | ||
} | ||
``` | ||
|
||
**Template Method**: | ||
|
||
Use when you need to define the skeleton of an algorithm but allow subclasses to override specific steps. | ||
|
||
**Signs you might need it**: | ||
|
||
If duplicated logic for similar algorithms creates maintenance challenges and increases the likelihood of inconsistencies. | ||
|
||
```java | ||
public abstract class Game { | ||
public final void play() { | ||
initialize(); | ||
startPlay(); | ||
endPlay(); | ||
} | ||
|
||
protected abstract void initialize(); | ||
protected abstract void startPlay(); | ||
protected abstract void endPlay(); | ||
} | ||
|
||
public class Chess extends Game { | ||
@Override | ||
protected void initialize() { | ||
System.out.println("Chess Game Initialized!"); | ||
} | ||
|
||
@Override | ||
protected void startPlay() { | ||
System.out.println("Chess Game Started!"); | ||
} | ||
|
||
@Override | ||
protected void endPlay() { | ||
System.out.println("Chess Game Finished!"); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
Game chess = new Chess(); | ||
chess.play(); | ||
} | ||
} | ||
``` | ||
|
||
**Command**: | ||
|
||
Use when you need to encapsulate requests as objects, enabling parameterization, queuing, or undo operations. | ||
|
||
**Signs you might need it**: | ||
|
||
If request handling logic is tightly coupled to specific actions, making it hard to implement features like queuing, logging, or undo. | ||
|
||
```java | ||
public interface Command { | ||
void execute(); | ||
} | ||
|
||
public class Light { | ||
public void turnOn() { | ||
System.out.println("Light is ON"); | ||
} | ||
|
||
public void turnOff() { | ||
System.out.println("Light is OFF"); | ||
} | ||
} | ||
|
||
public class TurnOnLightCommand implements Command { | ||
private Light light; | ||
|
||
public TurnOnLightCommand(Light light) { | ||
this.light = light; | ||
} | ||
|
||
@Override | ||
public void execute() { | ||
light.turnOn(); | ||
} | ||
} | ||
|
||
public class TurnOffLightCommand implements Command { | ||
private Light light; | ||
|
||
public TurnOffLightCommand(Light light) { | ||
this.light = light; | ||
} | ||
|
||
@Override | ||
public void execute() { | ||
light.turnOff(); | ||
} | ||
} | ||
|
||
public class RemoteControl { | ||
private Command command; | ||
|
||
public void setCommand(Command command) { | ||
this.command = command; | ||
} | ||
|
||
public void pressButton() { | ||
command.execute(); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
Light livingRoomLight = new Light(); | ||
Command turnOn = new TurnOnLightCommand(livingRoomLight); | ||
Command turnOff = new TurnOffLightCommand(livingRoomLight); | ||
|
||
RemoteControl remote = new RemoteControl(); | ||
remote.setCommand(turnOn); | ||
remote.pressButton(); | ||
|
||
remote.setCommand(turnOff); | ||
remote.pressButton(); | ||
} | ||
} | ||
``` | ||
|
||
**Chain of Responsibility**: | ||
|
||
Use when multiple objects might handle a request, but the specific handler is determined at runtime. | ||
|
||
**Signs you might need it**: | ||
|
||
If request handling becomes rigid and changes to handlers require altering the request-sender code. | ||
|
||
```java | ||
public abstract class Handler { | ||
private Handler next; | ||
|
||
public void setNext(Handler next) { | ||
this.next = next; | ||
} | ||
|
||
public void handleRequest(String request) { | ||
if (next != null) { | ||
next.handleRequest(request); | ||
} | ||
} | ||
} | ||
|
||
public class LoggingHandler extends Handler { | ||
@Override | ||
public void handleRequest(String request) { | ||
System.out.println("Logging request: " + request); | ||
super.handleRequest(request); | ||
} | ||
} | ||
|
||
public class AuthenticationHandler extends Handler { | ||
@Override | ||
public void handleRequest(String request) { | ||
System.out.println("Authentication successful."); | ||
super.handleRequest(request); | ||
} | ||
} | ||
|
||
public class AuthorizationHandler extends Handler { | ||
@Override | ||
public void handleRequest(String request) { | ||
System.out.println("Authorization successful."); | ||
super.handleRequest(request); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
Handler logger = new LoggingHandler(); | ||
Handler authenticator = new AuthenticationHandler(); | ||
Handler authorizer = new AuthorizationHandler(); | ||
|
||
logger.setNext(authenticator); | ||
authenticator.setNext(authorizer); | ||
|
||
logger.handleRequest("Request data"); | ||
} | ||
} | ||
``` | ||
|
||
**Mediator**: | ||
|
||
Use when you need to reduce the direct communication dependencies between objects by centralizing interactions. | ||
|
||
**Signs you might need it**: | ||
|
||
If direct communication between objects leads to overly complex and tightly coupled dependencies. | ||
|
||
```java | ||
public interface Mediator { | ||
void sendMessage(String message, Person sender); | ||
} | ||
|
||
public abstract class Person { | ||
protected Mediator mediator; | ||
|
||
public Person(Mediator mediator) { | ||
this.mediator = mediator; | ||
} | ||
|
||
public abstract void receiveMessage(String message); | ||
} | ||
|
||
public class ConcreteMediator implements Mediator { | ||
private Person person1; | ||
private Person person2; | ||
|
||
public void setPerson1(Person person) { | ||
this.person1 = person; | ||
} | ||
|
||
public void setPerson2(Person person) { | ||
this.person2 = person; | ||
} | ||
|
||
@Override | ||
public void sendMessage(String message, Person sender) { | ||
if (sender.equals(person1)) { | ||
person2.receiveMessage(message); | ||
} else { | ||
person1.receiveMessage(message); | ||
} | ||
} | ||
} | ||
|
||
public class User extends Person { | ||
public User(Mediator mediator) { | ||
super(mediator); | ||
} | ||
|
||
@Override | ||
public void receiveMessage(String message) { | ||
System.out.println("User received: " + message); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
ConcreteMediator mediator = new ConcreteMediator(); | ||
User user1 = new User(mediator); | ||
User user2 = new User(mediator); | ||
|
||
mediator.setPerson1(user1); | ||
mediator.setPerson2(user2); | ||
|
||
user1.mediator.sendMessage("Hello from user1", user1); | ||
} | ||
} | ||
``` | ||
|
||
**Visitor**: | ||
|
||
Use when you need to perform new operations on elements of a complex object structure without modifying the elements. | ||
|
||
**Signs you might need it**: | ||
|
||
If adding new operations to a structure of objects requires modifying the objects themselves, breaking encapsulation and increasing rigidity. | ||
|
||
```java | ||
public interface Visitor { | ||
void visit(Book book); | ||
void visit(Game game); | ||
} | ||
|
||
public interface ItemElement { | ||
void accept(Visitor visitor); | ||
} | ||
|
||
public class Book implements ItemElement { | ||
private String title; | ||
|
||
public Book(String title) { | ||
this.title = title; | ||
} | ||
|
||
public String getTitle() { | ||
return title; | ||
} | ||
|
||
@Override | ||
public void accept(Visitor visitor) { | ||
visitor.visit(this); | ||
} | ||
} | ||
|
||
public class Game implements ItemElement { | ||
private String name; | ||
|
||
public game(String name) { | ||
this.name = name; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public void accept(Visitor visitor) { | ||
visitor.visit(this); | ||
} | ||
} | ||
|
||
public class ShoppingCartVisitor implements Visitor { | ||
@Override | ||
public void visit(Book book) { | ||
System.out.println("Buying book: " + book.getTitle()); | ||
} | ||
|
||
@Override | ||
public void visit(Game game) { | ||
System.out.println("Buying game: " + game.getName()); | ||
} | ||
} | ||
|
||
public class Client { | ||
public static void main(String[] args) { | ||
Book book = new Book("Design Patterns"); | ||
Game game = new Game("Chess"); | ||
|
||
Visitor visitor = new ShoppingCartVisitor(); | ||
book.accept(visitor); | ||
game.accept(visitor); | ||
} | ||
} | ||
``` |