-
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.
- Loading branch information
Showing
1 changed file
with
347 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,347 @@ | ||
--- | ||
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 | ||
|
||
# Creational Patterns | ||
|
||
**Abstract Factory**: | ||
|
||
Creates families of related objects without specifying their concrete classes. | ||
It provides a higher level of abstraction, enabling the client to produce multiple types of objects from a single factory. | ||
|
||
```java | ||
public interface ButtonFactory { | ||
Button createButton(); | ||
} | ||
|
||
public class WindowsButtonFactory implements ButtonFactory { | ||
public Button createButton() { | ||
return new WindowsButton(); | ||
} | ||
} | ||
|
||
public class MacButtonFactory implements ButtonFactory { | ||
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"); | ||
} | ||
} | ||
``` | ||
|
||
**Factory Method**: | ||
|
||
Defines a method for creating objects, but allows subclasses to decide which object to instantiate. | ||
This pattern promotes loose coupling by relying on the subclass for the specific implementation. | ||
|
||
```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 UIFactory { | ||
public void renderInterface() { | ||
Button button = createButton(); | ||
button.render(); | ||
} | ||
|
||
protected abstract Button createButton(); | ||
} | ||
|
||
public class WindowsUIFactory extends UIFactory { | ||
@Override | ||
protected Button createButton() { | ||
return new WindowsButton(); | ||
} | ||
} | ||
|
||
public class MacUIFactory extends UIFactory { | ||
@Override | ||
protected Button createButton() { | ||
return new MacButton(); | ||
} | ||
} | ||
``` | ||
|
||
# Structural Patterns | ||
|
||
**Adapter**: | ||
|
||
Acts as a bridge between two incompatible interfaces. | ||
It allows existing classes to work together without modifying their source code by adapting their interfaces. | ||
|
||
```java | ||
public interface USB { | ||
void connectWithUSBPort(); | ||
} | ||
|
||
public class HDMI { | ||
public void connectWithHDMIPort() { | ||
System.out.println("Connected using HDMI port"); | ||
} | ||
} | ||
|
||
public class HDMIToUSBAdapter implements USB { | ||
private HDMI hdmiDevice; | ||
|
||
public HDMIToUSBAdapter(HDMI hdmiDevice) { | ||
this.hdmiDevice = hdmiDevice; | ||
} | ||
|
||
@Override | ||
public void connectWithUSBPort() { | ||
System.out.print("Adapting HDMI to USB: "); | ||
hdmiDevice.connectWithHDMIPort(); | ||
} | ||
} | ||
``` | ||
|
||
**Composite**: | ||
|
||
Enables you to compose objects into tree structures to represent part-whole hierarchies. | ||
The pattern treats individual objects and compositions of objects uniformly, simplifying client code. | ||
|
||
```java | ||
public interface FileSystemItem { | ||
void display(String indent); | ||
} | ||
|
||
public class File implements FileSystemItem { | ||
private String name; | ||
|
||
public File(String name) { | ||
this.name = name; | ||
} | ||
|
||
@Override | ||
public void display(String indent) { | ||
System.out.println(indent + "File: " + name); | ||
} | ||
} | ||
|
||
public class Directory implements FileSystemItem { | ||
private String name; | ||
private List<FileSystemItem> items = new ArrayList<>(); | ||
|
||
public Directory(String name) { | ||
this.name = name; | ||
} | ||
|
||
public void add(FileSystemItem item) { | ||
items.add(item); | ||
} | ||
|
||
public void remove(FileSystemItem item) { | ||
items.remove(item); | ||
} | ||
|
||
@Override | ||
public void display(String indent) { | ||
System.out.println(indent + "Directory: " + name); | ||
for (FileSystemItem item : items) { | ||
item.display(indent + " "); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
**Decorator**: | ||
|
||
Attaches additional behavior or responsibilities to an object dynamically. | ||
It provides a flexible alternative to subclassing for extending functionality. | ||
|
||
```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 EncryptionDecorator implements DataSource { | ||
private DataSource wrappee; | ||
|
||
public EncryptionDecorator(DataSource source) { | ||
this.wrappee = source; | ||
} | ||
|
||
@Override | ||
public void writeData(String data) { | ||
wrappee.writeData(encrypt(data)); | ||
} | ||
|
||
@Override | ||
public String readData() { | ||
return decrypt(wrappee.readData()); | ||
} | ||
|
||
private String encrypt(String data) { | ||
return "Encrypted[" + data + "]"; | ||
} | ||
|
||
private String decrypt(String data) { | ||
return data.replace("Encrypted[", "").replace("]", ""); | ||
} | ||
} | ||
``` | ||
|
||
# Behavioral Patterns | ||
|
||
**Observer**: | ||
|
||
Defines a one-to-many dependency between objects, where a change in the state of one object automatically notifies and updates all its dependents. | ||
This pattern is particularly useful for implementing event-driven systems. | ||
|
||
```java | ||
public interface Observer { | ||
void update(String message); | ||
} | ||
|
||
public class NewsSubscriber implements Observer { | ||
private String name; | ||
|
||
public NewsSubscriber(String name) { | ||
this.name = name; | ||
} | ||
|
||
@Override | ||
public void update(String message) { | ||
System.out.println(name + " received update: " + message); | ||
} | ||
} | ||
|
||
public class NewsPublisher { | ||
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); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
**Strategy**: | ||
|
||
Encapsulates interchangeable behaviors and algorithms, allowing you to select them at runtime. | ||
This pattern promotes the Open/Closed Principle by enabling new strategies to be introduced without altering existing code. | ||
|
||
```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 PayPalPayment implements PaymentStrategy { | ||
@Override | ||
public void pay(int amount) { | ||
System.out.println("Paid " + amount + " using PayPal."); | ||
} | ||
} | ||
``` | ||
|
||
**Template Method**: | ||
|
||
Defines the skeleton of an algorithm in a base class and allows subclasses to override specific steps of the algorithm without changing its structure. | ||
This ensures code reuse while maintaining flexibility for customization. | ||
|
||
```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!"); | ||
} | ||
} | ||
``` |