init
This commit is contained in:
commit
c5cd492449
475 changed files with 27928 additions and 0 deletions
34
technology/programming/patterns/Programming Patterns.md
Normal file
34
technology/programming/patterns/Programming Patterns.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
obj: meta
|
||||
---
|
||||
|
||||
# Creational Patterns
|
||||
- [Abstract Factory](creational/Abstract%20Factory%20Pattern.md)
|
||||
- [Builder](creational/Builder%20Pattern.md)
|
||||
- [Dependency Injection](creational/Dependency%20Injection%20Pattern.md)
|
||||
- [Factory Method](creational/Factory%20Method%20Pattern.md)
|
||||
- [Lazy Instantiation](creational/Lazy%20Instantiation%20Pattern.md)
|
||||
- [Multiton](creational/Multiton%20Pattern.md)
|
||||
- [Object Pool](creational/Object%20Pool%20Pattern.md)
|
||||
- [Prototype](creational/Prototype%20Pattern.md)
|
||||
- [RAII](creational/RAII%20Pattern.md)
|
||||
- [Singleton](creational/Singleton%20Pattern.md)
|
||||
|
||||
# Structural Patterns
|
||||
- [Adapter](structural/Adapter%20Pattern.md)
|
||||
- [Bridge](structural/Bridge%20Patterns.md)
|
||||
- [Composite](structural/Composite%20Pattern.md)
|
||||
- [Decorator](structural/Decorator%20Pattern.md)
|
||||
- [Facade](structural/Facade%20Pattern.md)
|
||||
- [Flyweight](structural/Flyweight%20Pattern.md)
|
||||
- [Proxy](structural/Proxy%20Pattern.md)
|
||||
|
||||
# Behavioral Patterns
|
||||
- [Iterator](behavioral/Iterator%20Pattern.md)
|
||||
- [Memento](behavioral/Memento%20Pattern.md)
|
||||
- [Null Object](behavioral/Null%20Object%20Pattern.md)
|
||||
- [Observer](behavioral/Observer%20Patterns.md)
|
||||
- [State](behavioral/State%20Patterns.md)
|
||||
- [Strategy](behavioral/Strategy%20Pattern.md)
|
||||
- [Template Method](behavioral/Template%20Method%20Pattern.md)
|
||||
- [Visitor](behavioral/Visitor%20Pattern.md)
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
|
||||
# Iterator Pattern
|
||||
|
||||
## **Description:**
|
||||
The Iterator Pattern is a behavioral design pattern that provides a way to access the elements of a collection sequentially without exposing its underlying representation. It defines a common interface for iterating over various types of collections, such as arrays, lists, trees, and more. The pattern separates the responsibility of iteration from the collection itself, promoting encapsulation and flexibility.
|
||||
|
||||
## **Example:**
|
||||
[Rust](../../languages/Rust.md) has iterators which can be extended by functional programming methods like `map()`, etc.
|
||||
```rust
|
||||
trait Iterator<T> {
|
||||
fn has_next(&self) -> bool;
|
||||
fn next(&mut self) -> Option<T>;
|
||||
}
|
||||
```
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Memento Pattern
|
||||
|
||||
## **Description:**
|
||||
The Memento Pattern is a behavioral design pattern that allows you to capture and externalize an object's internal state so that the object can be restored to that state later. It is often used to implement undo functionality, snapshots, or checkpoints in an application without exposing the object's internal structure.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Originator**: Define an originator class (or object) that has an internal state that needs to be saved and restored.
|
||||
2. **Memento**: Create a memento class that represents the state of the originator object. This class should provide methods to get and set the originator's state.
|
||||
3. **Caretaker**: Implement a caretaker class that is responsible for keeping track of and managing the memento objects. The caretaker should have a collection to store multiple mementos if needed.
|
||||
4. **Save and Restore**: The originator can create memento objects to save its state, and the caretaker can store these mementos. When needed, the originator can restore its state from a memento provided by the caretaker.
|
||||
|
||||
## **Example:**
|
||||
Let's create an example of the Memento Pattern in [Rust](../../languages/Rust.md) to implement a simple text editor with undo functionality.
|
||||
|
||||
```rust
|
||||
// Originator: TextEditor
|
||||
struct TextEditor {
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl TextEditor {
|
||||
fn new() -> Self {
|
||||
TextEditor { text: String::new() }
|
||||
}
|
||||
|
||||
fn set_text(&mut self, text: String) {
|
||||
self.text = text;
|
||||
}
|
||||
|
||||
fn get_text(&self) -> String {
|
||||
self.text.clone()
|
||||
}
|
||||
|
||||
fn create_memento(&self) -> EditorMemento {
|
||||
EditorMemento::new(self.text.clone())
|
||||
}
|
||||
|
||||
fn restore_from_memento(&mut self, memento: EditorMemento) {
|
||||
self.text = memento.get_saved_text();
|
||||
}
|
||||
}
|
||||
|
||||
// Memento: EditorMemento
|
||||
struct EditorMemento {
|
||||
saved_text: String,
|
||||
}
|
||||
|
||||
impl EditorMemento {
|
||||
fn new(text: String) -> Self {
|
||||
EditorMemento { saved_text: text }
|
||||
}
|
||||
|
||||
fn get_saved_text(&self) -> String {
|
||||
self.saved_text.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// Caretaker: EditorHistory
|
||||
struct EditorHistory {
|
||||
mementos: Vec<EditorMemento>,
|
||||
}
|
||||
|
||||
impl EditorHistory {
|
||||
fn new() -> Self {
|
||||
EditorHistory { mementos: Vec::new() }
|
||||
}
|
||||
|
||||
fn add_memento(&mut self, memento: EditorMemento) {
|
||||
self.mementos.push(memento);
|
||||
}
|
||||
|
||||
fn get_memento(&self, index: usize) -> Option<&EditorMemento> {
|
||||
self.mementos.get(index)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut editor = TextEditor::new();
|
||||
let mut history = EditorHistory::new();
|
||||
|
||||
editor.set_text(String::from("First version"));
|
||||
history.add_memento(editor.create_memento());
|
||||
|
||||
editor.set_text(String::from("Second version"));
|
||||
history.add_memento(editor.create_memento());
|
||||
|
||||
editor.restore_from_memento(history.get_memento(0).unwrap().clone());
|
||||
println!("Restored version: {}", editor.get_text());
|
||||
}
|
||||
```
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Null Object Pattern
|
||||
|
||||
## **Description:**
|
||||
The Null Object Pattern is a behavioral design pattern that provides an object as a surrogate for the lack of an object of a given type. It allows you to handle the absence of an object gracefully without explicit null checks, improving code readability and robustness. The null object typically provides default behavior or no-op implementations for the methods of the original object.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Interface or Abstract Class**: Define an interface or abstract class that represents the common behavior of the objects in question.
|
||||
2. **Concrete Implementations**: Create concrete implementations of the interface or abstract class for real objects that provide meaningful functionality.
|
||||
3. **Null Object**: Create a special null object class that also implements the interface or extends the abstract class. This null object should provide default or no-op implementations for the methods.
|
||||
4. **Client Code**: In client code, use the null object when you expect an object of the interface type but want to gracefully handle the case when no real object is available.
|
||||
|
||||
## **Example:**
|
||||
Let's create an example of the Null Object Pattern in [Python](../../languages/Python.md) for handling logging. We'll define an interface for a logger, create real logger and null logger implementations, and demonstrate their use.
|
||||
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
# Step 1: Define an interface for the logger
|
||||
class Logger(ABC):
|
||||
@abstractmethod
|
||||
def log(self, message):
|
||||
pass
|
||||
|
||||
# Step 2: Create a real logger implementation
|
||||
class ConsoleLogger(Logger):
|
||||
def log(self, message):
|
||||
print(f"Console Log: {message}")
|
||||
|
||||
# Step 3: Create a null logger implementation
|
||||
class NullLogger(Logger):
|
||||
def log(self, message):
|
||||
pass
|
||||
|
||||
# Step 4: Client code
|
||||
def main():
|
||||
# Real logger
|
||||
logger = ConsoleLogger()
|
||||
logger.log("This is a real log message.")
|
||||
|
||||
# Null logger
|
||||
null_logger = NullLogger()
|
||||
null_logger.log("This will not be logged.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
In this [Python](../../languages/Python.md) example, we define a `Logger` interface (Step 1) and create a real logger implementation (`ConsoleLogger`) that prints log messages to the console (Step 2). We also create a null logger implementation (`NullLogger`) that does nothing when `log` is called (Step 3).
|
||||
|
||||
In the `main` function (Step 4), we demonstrate using both the real logger and the null logger. When you use the null logger, you can safely handle the case where logging is not required without explicit null checks. This pattern is particularly useful when you want to avoid conditional statements throughout your code for handling null cases.
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Observer Pattern
|
||||
|
||||
## **Description:**
|
||||
The Observer Pattern is a behavioral design pattern that defines a one-to-many dependency between objects so that when one object (the subject) changes state, all its dependents (observers) are notified and updated automatically. It's used to establish dynamic, distributed, and loosely coupled communication between objects, making it easy to maintain consistency and synchronization.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Subject Interface**: Define a subject interface or abstract class that declares methods for attaching, detaching, and notifying observers. It should also have a method to update its state.
|
||||
2. **Concrete Subject**: Create a concrete subject class that implements the subject interface. This class maintains a list of its observers and notifies them when its state changes.
|
||||
3. **Observer Interface**: Define an observer interface with an update method that subjects will call to notify observers of changes.
|
||||
4. **Concrete Observer**: Create concrete observer classes that implement the observer interface. These classes define how they respond to updates from the subject.
|
||||
5. **Client Code**: In client code, create instances of concrete subjects and observers, attach observers to subjects, and let the system handle the rest of the communication.
|
||||
|
||||
**Example:** Let's create an example of the Observer Pattern in [Python](../../languages/Python.md) to model a simple weather monitoring system where weather stations act as subjects and display panels act as observers.
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
# Step 1: Subject Interface
|
||||
class Subject(ABC):
|
||||
@abstractmethod
|
||||
def attach(self, observer):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def detach(self, observer):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def notify(self):
|
||||
pass
|
||||
|
||||
# Step 2: Concrete Subject (WeatherStation)
|
||||
class WeatherStation(Subject):
|
||||
def __init__(self):
|
||||
self.observers = []
|
||||
self.temperature = 0.0
|
||||
|
||||
def attach(self, observer):
|
||||
self.observers.append(observer)
|
||||
|
||||
def detach(self, observer):
|
||||
self.observers.remove(observer)
|
||||
|
||||
def notify(self):
|
||||
for observer in self.observers:
|
||||
observer.update(self.temperature)
|
||||
|
||||
def set_temperature(self, temperature):
|
||||
self.temperature = temperature
|
||||
self.notify()
|
||||
|
||||
# Step 3: Observer Interface
|
||||
class Observer(ABC):
|
||||
@abstractmethod
|
||||
def update(self, temperature):
|
||||
pass
|
||||
|
||||
# Step 4: Concrete Observer (DisplayPanel)
|
||||
class DisplayPanel(Observer):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def update(self, temperature):
|
||||
print(f"{self.name} Display: Temperature is {temperature}°C")
|
||||
|
||||
# Step 5: Client Code
|
||||
def main():
|
||||
weather_station = WeatherStation()
|
||||
|
||||
panel1 = DisplayPanel("Panel 1")
|
||||
panel2 = DisplayPanel("Panel 2")
|
||||
|
||||
weather_station.attach(panel1)
|
||||
weather_station.attach(panel2)
|
||||
|
||||
weather_station.set_temperature(25.5)
|
||||
weather_station.set_temperature(30.0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
In this [Python](../../languages/Python.md) example, we define a `Subject` interface (Step 1) with `attach`, `detach`, and `notify` methods. The `WeatherStation` class (Step 2) implements the subject interface, maintains a list of observers, and notifies them when the temperature changes.
|
||||
|
||||
We also define an `Observer` interface (Step 3) with an `update` method. The `DisplayPanel` class (Step 4) implements the observer interface and defines how it responds to updates.
|
||||
|
||||
In the `main` function (Step 5), we create a weather station and two display panels. We attach the display panels to the weather station, and when the weather station's temperature changes, it automatically notifies the attached display panels to update their displays. This allows for real-time synchronization between the weather station and the display panels.
|
82
technology/programming/patterns/behavioral/State Patterns.md
Normal file
82
technology/programming/patterns/behavioral/State Patterns.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# State Pattern
|
||||
|
||||
## **Description:**
|
||||
The State Pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. The object appears to change its class, making it useful for modeling objects with complex state-dependent behaviors. It encapsulates the states as separate classes and allows the context (the object whose state is changing) to delegate state-specific behavior to these state classes.
|
||||
|
||||
**How it's Used:**
|
||||
1. **State Interface/Abstract Class**: Define a state interface or abstract class that declares methods for the various actions or behaviors that can change based on the state.
|
||||
2. **Concrete States**: Create concrete state classes that implement the state interface/abstract class. Each concrete state class represents a specific state and provides its own implementation of the state-specific behaviors.
|
||||
3. **Context**: Define a context class that maintains a reference to the current state object. The context class delegates state-specific behavior to the current state object.
|
||||
4. **State Transition**: Implement methods in the context class for transitioning between states. These methods change the current state to a different state as needed.
|
||||
|
||||
## **Example:**
|
||||
Let's create an example of the State Pattern in [Python](../../languages/Python.md) to model the behavior of a traffic light with three states: "Green," "Yellow," and "Red."
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
# Step 1: State Interface
|
||||
class TrafficLightState(ABC):
|
||||
@abstractmethod
|
||||
def display(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def transition(self, traffic_light):
|
||||
pass
|
||||
|
||||
# Step 2: Concrete States
|
||||
class GreenLightState(TrafficLightState):
|
||||
def display(self):
|
||||
return "Green"
|
||||
|
||||
def transition(self, traffic_light):
|
||||
traffic_light.set_state(YellowLightState())
|
||||
|
||||
class YellowLightState(TrafficLightState):
|
||||
def display(self):
|
||||
return "Yellow"
|
||||
|
||||
def transition(self, traffic_light):
|
||||
traffic_light.set_state(RedLightState())
|
||||
|
||||
class RedLightState(TrafficLightState):
|
||||
def display(self):
|
||||
return "Red"
|
||||
|
||||
def transition(self, traffic_light):
|
||||
traffic_light.set_state(GreenLightState())
|
||||
|
||||
# Step 3: Context (TrafficLight)
|
||||
class TrafficLight:
|
||||
def __init__(self):
|
||||
self.state = GreenLightState()
|
||||
|
||||
def set_state(self, state):
|
||||
self.state = state
|
||||
|
||||
def change_state(self):
|
||||
self.state.transition(self)
|
||||
|
||||
def display_state(self):
|
||||
return self.state.display()
|
||||
|
||||
# Step 4: Client Code
|
||||
def main():
|
||||
traffic_light = TrafficLight()
|
||||
|
||||
for _ in range(5):
|
||||
print(f"Current Light: {traffic_light.display_state()}")
|
||||
traffic_light.change_state()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
In this [Python](../../languages/Python.md) example, we define a `TrafficLightState` interface (Step 1) with `display` and `transition` methods. We create three concrete state classes (`GreenLightState`, `YellowLightState`, and `RedLightState`) (Step 2) that implement this interface, representing the states of the traffic light.
|
||||
|
||||
The `TrafficLight` class (Step 3) is the context class that maintains a reference to the current state and delegates state-specific behavior to that state. The `change_state` method transitions to the next state in the sequence.
|
||||
|
||||
In the `main` function (Step 4), we create a traffic light, display its state, and change the state to simulate the behavior of a traffic light changing from green to yellow to red and back to green. The State Pattern allows us to manage the traffic light's behavior in a flexible and maintainable way.
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Strategy Pattern
|
||||
|
||||
## **Description:**
|
||||
The Strategy Pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one of them, and makes them interchangeable. It allows clients to choose an appropriate algorithm from a family of algorithms at runtime, without altering the code that uses the algorithm. This pattern promotes flexibility, extensibility, and the ability to switch between different algorithms or strategies.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Strategy Interface/Abstract Class**: Define a strategy interface or abstract class that declares a method or a set of methods for the algorithms to be encapsulated.
|
||||
2. **Concrete Strategies**: Create concrete strategy classes that implement the strategy interface/abstract class. Each concrete strategy represents a specific algorithm.
|
||||
3. **Context**: Define a context class that contains a reference to a strategy object and can switch between different strategies. The context class uses the strategy object to perform its operations.
|
||||
4. **Client Code**: Clients create instances of the context class and configure them with the desired strategy. Clients can change the strategy at runtime, and the context will use the currently configured strategy to perform its tasks.
|
||||
|
||||
## **Example:**
|
||||
Let's create an example of the Strategy Pattern in [Python](../../languages/Python.md) to model a payment system where customers can choose different payment methods.
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
# Step 1: Strategy Interface
|
||||
class PaymentStrategy(ABC):
|
||||
@abstractmethod
|
||||
def pay(self, amount):
|
||||
pass
|
||||
|
||||
# Step 2: Concrete Strategies
|
||||
class CreditCardPayment(PaymentStrategy):
|
||||
def pay(self, amount):
|
||||
print(f"Paying ${amount} using Credit Card.")
|
||||
|
||||
class PayPalPayment(PaymentStrategy):
|
||||
def pay(self, amount):
|
||||
print(f"Paying ${amount} using PayPal.")
|
||||
|
||||
class BankTransferPayment(PaymentStrategy):
|
||||
def pay(self, amount):
|
||||
print(f"Paying ${amount} using Bank Transfer.")
|
||||
|
||||
# Step 3: Context (PaymentContext)
|
||||
class PaymentContext:
|
||||
def __init__(self, strategy):
|
||||
self.strategy = strategy
|
||||
|
||||
def set_payment_strategy(self, strategy):
|
||||
self.strategy = strategy
|
||||
|
||||
def perform_payment(self, amount):
|
||||
self.strategy.pay(amount)
|
||||
|
||||
# Step 4: Client Code
|
||||
def main():
|
||||
credit_card_strategy = CreditCardPayment()
|
||||
paypal_strategy = PayPalPayment()
|
||||
bank_transfer_strategy = BankTransferPayment()
|
||||
|
||||
context = PaymentContext(credit_card_strategy)
|
||||
|
||||
context.perform_payment(100.0)
|
||||
|
||||
# Change payment strategy at runtime
|
||||
context.set_payment_strategy(paypal_strategy)
|
||||
context.perform_payment(50.0)
|
||||
|
||||
context.set_payment_strategy(bank_transfer_strategy)
|
||||
context.perform_payment(75.0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
In this [Python](../../languages/Python.md) example, we define a `PaymentStrategy` interface (Step 1) with a `pay` method. We create three concrete strategy classes (`CreditCardPayment`, `PayPalPayment`, and `BankTransferPayment`) (Step 2) that implement this interface, representing different payment methods.
|
||||
|
||||
The `PaymentContext` class (Step 3) is the context class that contains a reference to a strategy object and uses it to perform payments. Clients (Step 4) create instances of the context class and configure them with the desired payment strategy. Clients can change the strategy at runtime, and the context will use the currently configured strategy to perform payments. This allows for easy swapping of payment methods without changing the client code.
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Template Method Pattern
|
||||
|
||||
## **Description:**
|
||||
The Template Method Pattern is a behavioral design pattern that defines the skeleton of an algorithm in the base class but allows subclasses to override specific steps of the algorithm without changing its structure. It provides a way to define the overall algorithm's structure while allowing customization of certain steps by subclasses. This pattern promotes code reuse and follows the "Don't Call Us, We'll Call You" principle, where the base class controls the flow of the algorithm.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Abstract Template Class**: Create an abstract base class (or interface) that defines the template method, which is the skeleton of the algorithm. The template method consists of a series of steps, some of which may be abstract or have default implementations.
|
||||
2. **Concrete Subclasses**: Create concrete subclasses that inherit from the abstract template class. These subclasses provide concrete implementations for the abstract steps of the template method.
|
||||
3. **Template Method**: The template method in the abstract base class controls the overall algorithm's flow by calling the individual steps. It should be marked as final or not overridable to prevent subclasses from altering the algorithm's structure.
|
||||
|
||||
## **Example:**
|
||||
Let's create an example of the Template Method Pattern in [Python](../../languages/Python.md) for a beverage preparation process with a template method for brewing beverages.
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
# Step 1: Abstract Template Class
|
||||
class Beverage(ABC):
|
||||
def prepare(self):
|
||||
self.boil_water()
|
||||
self.brew()
|
||||
self.pour_in_cup()
|
||||
self.add_condiments()
|
||||
|
||||
@abstractmethod
|
||||
def brew(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def add_condiments(self):
|
||||
pass
|
||||
|
||||
def boil_water(self):
|
||||
print("Boiling water")
|
||||
|
||||
def pour_in_cup(self):
|
||||
print("Pouring into cup")
|
||||
|
||||
# Step 2: Concrete Subclasses
|
||||
class Tea(Beverage):
|
||||
def brew(self):
|
||||
print("Steeping the tea")
|
||||
|
||||
def add_condiments(self):
|
||||
print("Adding lemon")
|
||||
|
||||
class Coffee(Beverage):
|
||||
def brew(self):
|
||||
print("Dripping coffee through filter")
|
||||
|
||||
def add_condiments(self):
|
||||
print("Adding sugar and milk")
|
||||
|
||||
# Step 3: Client Code
|
||||
def main():
|
||||
tea = Tea()
|
||||
coffee = Coffee()
|
||||
|
||||
print("Making tea:")
|
||||
tea.prepare()
|
||||
|
||||
print("\nMaking coffee:")
|
||||
coffee.prepare()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
In this [Python](../../languages/Python.md) example, we define a `Beverage` abstract base class (Step 1) with a `prepare` method that represents the template method. The `prepare` method calls four steps: `boil_water`, `brew`, `pour_in_cup`, and `add_condiments`. The `brew` and `add_condiments` methods are declared as abstract, allowing concrete subclasses (e.g., `Tea` and `Coffee`) to provide their implementations for these steps.
|
||||
|
||||
Concrete subclasses (Step 2) such as `Tea` and `Coffee` inherit from the `Beverage` class and provide their implementations for the `brew` and `add_condiments` steps.
|
||||
|
||||
In the `main` function (Step 3), we create instances of `Tea` and `Coffee` and invoke the `prepare` method on each, which follows the template method's algorithm for preparing beverages. Subclasses customize the steps to produce specific types of beverages while reusing the common template method structure.
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Visitor Pattern
|
||||
|
||||
## **Description:**
|
||||
The Visitor Pattern is a behavioral design pattern that allows you to add new operations to a set of related classes without modifying their code. It separates the algorithm (or operation) from the object structure, enabling you to define new behaviors (visitors) independently and apply them to existing objects. This pattern is useful when you have a stable set of classes but need to frequently introduce new operations on those classes.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Element Interface**: Define an element interface that declares an `accept` method which accepts a visitor.
|
||||
2. **Concrete Elements**: Create concrete element classes that implement the element interface. These are the objects that will be visited by visitors.
|
||||
3. **Visitor Interface**: Define a visitor interface that declares visit methods for each concrete element type. The visitor interface should have one visit method for each type of element it can visit.
|
||||
4. **Concrete Visitors**: Create concrete visitor classes that implement the visitor interface. Each concrete visitor provides implementations for the visit methods to perform specific operations on the elements.
|
||||
5. **Element Structure**: Create a structure (e.g., a collection) of concrete elements that need to be visited.
|
||||
6. **Accept Visitor**: In the concrete elements, implement the `accept` method to call the appropriate visit method on the visitor.
|
||||
7. **Client Code**: In client code, create instances of concrete elements and visitors, and apply visitors to elements as needed.
|
||||
|
||||
## **Example:**
|
||||
Let's create an example of the Visitor Pattern in [Python](../../languages/Python.md) to model a document structure with elements (e.g., paragraphs and tables) and a visitor that counts the elements.
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
# Step 1: Element Interface
|
||||
class Element(ABC):
|
||||
@abstractmethod
|
||||
def accept(self, visitor):
|
||||
pass
|
||||
|
||||
# Step 2: Concrete Elements
|
||||
class Paragraph(Element):
|
||||
def accept(self, visitor):
|
||||
visitor.visit_paragraph(self)
|
||||
|
||||
class Table(Element):
|
||||
def accept(self, visitor):
|
||||
visitor.visit_table(self)
|
||||
|
||||
# Step 3: Visitor Interface
|
||||
class Visitor(ABC):
|
||||
@abstractmethod
|
||||
def visit_paragraph(self, paragraph):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def visit_table(self, table):
|
||||
pass
|
||||
|
||||
# Step 4: Concrete Visitors
|
||||
class ElementCounter(Visitor):
|
||||
def __init__(self):
|
||||
self.paragraph_count = 0
|
||||
self.table_count = 0
|
||||
|
||||
def visit_paragraph(self, paragraph):
|
||||
self.paragraph_count += 1
|
||||
|
||||
def visit_table(self, table):
|
||||
self.table_count += 1
|
||||
|
||||
# Step 5: Element Structure
|
||||
class Document:
|
||||
def __init__(self, elements):
|
||||
self.elements = elements
|
||||
|
||||
def accept(self, visitor):
|
||||
for element in self.elements:
|
||||
element.accept(visitor)
|
||||
|
||||
# Step 7: Client Code
|
||||
def main():
|
||||
paragraph1 = Paragraph()
|
||||
paragraph2 = Paragraph()
|
||||
table1 = Table()
|
||||
table2 = Table()
|
||||
|
||||
elements = [paragraph1, table1, paragraph2, table2]
|
||||
|
||||
document = Document(elements)
|
||||
counter = ElementCounter()
|
||||
|
||||
document.accept(counter)
|
||||
|
||||
print(f"Paragraph Count: {counter.paragraph_count}")
|
||||
print(f"Table Count: {counter.table_count}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
In this [Python](../../languages/Python.md) example, we define an `Element` interface (Step 1) with an `accept` method. We create two concrete element classes (`Paragraph` and `Table`) that implement this interface (Step 2).
|
||||
|
||||
We define a `Visitor` interface (Step 3) with `visit_paragraph` and `visit_table` methods. We then create a concrete visitor class (`ElementCounter`) that implements the visitor interface (Step 4) and counts the elements it visits.
|
||||
|
||||
The `Document` class (Step 5) contains a collection of elements, and it implements the `accept` method to iterate through the elements and call their `accept` method to let the visitor visit them.
|
||||
|
||||
In the `main` function (Step 7), we create instances of elements and visitors, apply the visitor to the document, and display the counts of paragraphs and tables visited. This separation allows us to add new visitors to perform different operations on the document structure without modifying the element classes.
|
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Abstract Factory Pattern
|
||||
## **Description:**
|
||||
The Abstract Factory Pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. It is often used to ensure that a system is compatible with multiple families of objects, allowing you to switch between these families without changing the client code.
|
||||
|
||||
**How it's Used:**
|
||||
|
||||
1. **Abstract Factory Interface**: Define an abstract factory interface that declares a set of creation methods for creating abstract product objects. Each method corresponds to a different type of product.
|
||||
|
||||
2. **Concrete Factories**: Create concrete factory classes that implement the abstract factory interface. Each concrete factory is responsible for creating a specific family of related products.
|
||||
|
||||
3. **Abstract Product Interface**: Define an abstract product interface that declares the common methods that concrete product classes must implement. Each product family will have its own set of product interfaces.
|
||||
|
||||
4. **Concrete Products**: Create concrete product classes that implement the abstract product interfaces. These classes provide specific implementations for the products within each family.
|
||||
|
||||
5. **Client Code**: In the client code, use the abstract factory to create families of objects. Clients interact with the abstract factory and abstract product interfaces rather than directly with concrete classes.
|
||||
|
||||
|
||||
## **Example:**
|
||||
Let's say you're building a user interface (UI) framework that can be used on both desktop and mobile platforms. You want to ensure that UI components like buttons and text fields are consistent within each platform but can be easily switched between platforms. Here's how the Abstract Factory Pattern can be applied:
|
||||
|
||||
- **Abstract Factory Interface**: Define an `AbstractUIFactory` interface with methods like `createButton` and `createTextField`.
|
||||
|
||||
- **Concrete Factories**: Create two concrete factory classes: `DesktopUIFactory` and `MobileUIFactory`, both implementing the `AbstractUIFactory`. The `DesktopUIFactory` creates desktop-style UI components, while the `MobileUIFactory` creates mobile-style UI components.
|
||||
|
||||
- **Abstract Product Interfaces**: Define abstract product interfaces like `Button` and `TextField` that specify the methods these UI components should have.
|
||||
|
||||
- **Concrete Products**: Create concrete product classes like `DesktopButton`, `MobileButton`, `DesktopTextField`, and `MobileTextField`, each implementing the corresponding abstract product interfaces.
|
||||
|
||||
- **Client Code**: In your application, use the appropriate factory (either `DesktopUIFactory` or `MobileUIFactory`) to create UI components. For example:
|
||||
|
||||
|
||||
```java
|
||||
AbstractUIFactory factory = getUIFactoryForPlatform();
|
||||
Button button = factory.createButton();
|
||||
TextField textField = factory.createTextField();
|
||||
```
|
||||
|
||||
By using the Abstract Factory Pattern, you can switch between desktop and mobile UI components seamlessly by changing the factory you use, without modifying the client code.
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Builder Pattern
|
||||
|
||||
## **Description:**
|
||||
The Builder Pattern is a creational design pattern used to construct a complex object step by step. It separates the construction of an object from its representation, allowing you to create different variations of the same object with a clear and consistent API. This pattern is particularly useful when an object has many optional components or configuration settings.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Builder Interface**: Define an abstract builder interface that declares methods for constructing different parts of the complex object. These methods should cover all possible configurations.
|
||||
2. **Concrete Builders**: Create concrete builder classes that implement the builder interface. Each concrete builder is responsible for building a specific variant of the complex object.
|
||||
3. **Product**: Define the complex object (the product) that you want to create. This object should have a way to accept values or components set by the builder.
|
||||
4. **Director (Optional)**: You can create a director class (optional) that orchestrates the construction process using a specific builder. The director simplifies client code by providing a higher-level interface to construct the object.
|
||||
5. **Client Code**: In your application, use the builder to construct the complex object step by step. You can chain method calls together for a more fluent and readable construction process.
|
||||
|
||||
## **Example:**
|
||||
Consider building a `Computer` object with various optional components such as a processor, memory, storage, and graphics card. Here's how the Builder Pattern can be applied:
|
||||
- **Builder Interface**: Define an `ComputerBuilder` interface with methods like `setProcessor`, `setMemory`, `setStorage`, and `setGraphicsCard`.
|
||||
- **Concrete Builders**: Create concrete builder classes like `GamingComputerBuilder` and `OfficeComputerBuilder`, both implementing the `ComputerBuilder` interface. Each builder knows how to construct a specific type of computer.
|
||||
- **Product**: Define the `Computer` class with fields for processor, memory, storage, and graphics card, and a constructor to initialize these fields.
|
||||
- **Director (Optional)**: Create a `ComputerDirector` class that takes a `ComputerBuilder` as a parameter. The director can provide higher-level methods like `buildGamingComputer` or `buildOfficeComputer`.
|
||||
- **Client Code**: In your application, use the builder to construct a computer with the desired components:
|
||||
|
||||
```java
|
||||
ComputerBuilder builder = new GamingComputerBuilder();
|
||||
Computer gamingComputer = builder.setMemory(16).buildComputer();
|
||||
```
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Dependency Injection
|
||||
|
||||
## **Description:**
|
||||
Dependency Injection (DI) is a design pattern used in software development to achieve Inversion of Control (IoC). It helps manage the dependencies between components or classes by externalizing the dependencies and injecting them rather than having the components create their own dependencies. This promotes loose coupling, testability, and flexibility in your code.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Dependency**: Identify the dependencies that a class or component needs to function properly. These dependencies can be other objects, services, or configurations.
|
||||
2. **Injection Mechanism**: Create a mechanism to provide these dependencies to the class that needs them. This can be done in several ways, such as constructor injection, setter injection, or method injection.
|
||||
3. **Injection Container (Optional)**: In larger applications, you may use an injection container or a framework (e.g., Spring, Guice) to manage and automatically inject dependencies. These containers can handle the creation and wiring of objects.
|
||||
4. **Client Code**: In your application, create instances of the classes and inject the required dependencies. This can be done manually or by using an injection container.
|
||||
|
||||
## **Example:**
|
||||
Consider a simple Java application where you have a `UserService` class that depends on a `UserRepository` to retrieve user data from a database. Here's how Dependency Injection can be applied:
|
||||
|
||||
- **Identify Dependencies**: Recognize that `UserService` depends on `UserRepository`.
|
||||
- **Injection Mechanism**: Use constructor injection to inject the `UserRepository` dependency into the `UserService` class.
|
||||
|
||||
```java
|
||||
public class UserService {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public UserService(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
// Use userRepository to perform user-related operations
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your application code, create instances of `UserRepository` and `UserService`, and inject the `UserRepository` into the `UserService`.
|
||||
|
||||
```java
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
UserRepository userRepository = new UserRepository(); // or use an injection container
|
||||
UserService userService = new UserService(userRepository);
|
||||
|
||||
// Use the userService to work with users
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using Dependency Injection, you've decoupled the `UserService` and `UserRepository`, making it easier to replace or test these components independently. You can change the `UserRepository` implementation or switch to a mock repository for testing without modifying the `UserService` class. This promotes modularity and maintainability in your code.
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Factory Method Pattern
|
||||
|
||||
## **Description:**
|
||||
The Factory Method Pattern is a creational design pattern that defines an interface for creating objects but allows subclasses to alter the type of objects that will be created. It provides a way to delegate the responsibility of instantiating objects to subclasses, promoting flexibility and extensibility in your code.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Creator Interface or Abstract Class**: Define an abstract class or interface, often referred to as the "creator," that declares a method for creating objects. This method is the factory method.
|
||||
2. **Concrete Creators**: Create concrete subclasses of the creator class, each of which implements the factory method. These subclasses decide which class of objects to create and return.
|
||||
3. **Product**: Define an abstract product class or interface that represents the type of object created by the factory method.
|
||||
4. **Concrete Products**: Create concrete classes that implement the product interface. Each concrete product represents a specific variant or type of object.
|
||||
5. **Client Code**: In your application, use the creator class and its factory method to create objects. The client code typically works with the creator interface or abstract class rather than concrete classes.
|
||||
|
||||
## **Example:**
|
||||
Imagine you are developing a document processing application that can handle different types of documents, such as `TextDocument` and `SpreadsheetDocument`. Here's how the Factory Method Pattern can be applied:
|
||||
|
||||
- **Creator Interface or Abstract Class**: Define an abstract class called `DocumentCreator` with a factory method named `createDocument`.
|
||||
```java
|
||||
public abstract class DocumentCreator {
|
||||
public abstract Document createDocument();
|
||||
}
|
||||
```
|
||||
- **Concrete Creators**: Create concrete subclasses of `DocumentCreator`, such as `TextDocumentCreator` and `SpreadsheetDocumentCreator`. Each subclass implements the `createDocument` method to return a specific type of document
|
||||
```java
|
||||
public class TextDocumentCreator extends DocumentCreator {
|
||||
@Override
|
||||
public Document createDocument() {
|
||||
return new TextDocument();
|
||||
}
|
||||
}
|
||||
|
||||
public class SpreadsheetDocumentCreator extends DocumentCreator {
|
||||
@Override
|
||||
public Document createDocument() {
|
||||
return new SpreadsheetDocument();
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Product**: Define an abstract `Document` class or interface that represents the common operations for documents.
|
||||
```java
|
||||
public abstract class Document {
|
||||
public abstract void open();
|
||||
public abstract void save();
|
||||
}
|
||||
```
|
||||
- **Concrete Products**: Create concrete classes like `TextDocument` and `SpreadsheetDocument` that implement the `Document` interface with specific implementations.
|
||||
```java
|
||||
public class TextDocument extends Document {
|
||||
@Override
|
||||
public void open() {
|
||||
System.out.println("Opening Text Document");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
System.out.println("Saving Text Document");
|
||||
}
|
||||
}
|
||||
|
||||
public class SpreadsheetDocument extends Document {
|
||||
@Override
|
||||
public void open() {
|
||||
System.out.println("Opening Spreadsheet Document");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
System.out.println("Saving Spreadsheet Document");
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Client Code**: In your application, use the creator classes and their factory methods to create documents without knowing the concrete document classes:
|
||||
```java
|
||||
DocumentCreator textDocumentCreator = new TextDocumentCreator();
|
||||
Document textDocument = textDocumentCreator.createDocument();
|
||||
textDocument.open();
|
||||
textDocument.save();
|
||||
|
||||
DocumentCreator spreadsheetDocumentCreator = new SpreadsheetDocumentCreator();
|
||||
Document spreadsheetDocument = spreadsheetDocumentCreator.createDocument();
|
||||
spreadsheetDocument.open();
|
||||
spreadsheetDocument.save();
|
||||
```
|
||||
|
||||
The Factory Method Pattern allows you to extend your application with new document types by creating additional concrete creators and products without modifying existing client code, promoting code reuse and maintainability.
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Lazy Instantiation
|
||||
|
||||
## **Description:**
|
||||
Lazy Instantiation is a design pattern that defers the creation of an object until it is actually needed. This pattern is particularly useful when creating or initializing an object is expensive in terms of time or resources, and you want to postpone it until the last possible moment to improve performance or save resources.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Identify Expensive Objects**: Determine which objects or resources in your application are expensive to create or initialize.
|
||||
2. **Initialization on First Use**: Instead of creating these objects when the application starts, initialize them only when they are first requested by a client or when they are needed for a specific task.
|
||||
3. **Lazy Initialization Mechanism**: Implement a mechanism to perform lazy initialization. Common techniques include using a null check, a flag, or a synchronized block to ensure that the object is created only once and when it's required.
|
||||
4. **Client Code**: In your application, access or request the object as needed. The lazy initialization mechanism will ensure that the object is created the first time it's requested and reused for subsequent requests.
|
||||
|
||||
## **Example:**
|
||||
Consider an application that manages a large collection of images, and loading each image into memory is resource-intensive. To save resources and improve performance, you can implement Lazy Instantiation for image loading:
|
||||
|
||||
- **Identify Expensive Objects**: Recognize that loading images into memory is resource-intensive.
|
||||
- **Lazy Initialization Mechanism**: Create a class called `ImageLoader` that manages the loading of images. In this class, implement a lazy initialization mechanism for loading images on-demand.
|
||||
```java
|
||||
public class ImageLoader {
|
||||
private Map<String, Image> loadedImages = new HashMap<>();
|
||||
|
||||
public Image getImage(String filename) {
|
||||
if (!loadedImages.containsKey(filename)) {
|
||||
// Load the image from disk or a remote source
|
||||
Image image = loadImageFromFile(filename);
|
||||
loadedImages.put(filename, image);
|
||||
}
|
||||
return loadedImages.get(filename);
|
||||
}
|
||||
|
||||
private Image loadImageFromFile(String filename) {
|
||||
// Code to load the image from disk or a remote source
|
||||
// ...
|
||||
return new Image(filename);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your application, when you need to display an image, use the `ImageLoader` to obtain the image. The `ImageLoader` will load the image from the source only when requested and cache it for future use:
|
||||
```java
|
||||
ImageLoader imageLoader = new ImageLoader();
|
||||
Image image1 = imageLoader.getImage("image1.jpg"); // Loaded when first requested
|
||||
Image image2 = imageLoader.getImage("image2.jpg"); // Loaded when first requested
|
||||
|
||||
// Later in the application
|
||||
Image image3 = imageLoader.getImage("image1.jpg"); // Reused from cache
|
||||
```
|
||||
|
||||
By applying Lazy Instantiation in this example, you ensure that images are loaded into memory only when needed, conserving resources and improving the application's responsiveness. The images are cached for reuse, further enhancing performance when the same image is requested multiple times.
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Multiton Pattern
|
||||
|
||||
## **Description:**
|
||||
The Multiton Pattern is a variation of the [Singleton Pattern](Singleton%20Pattern.md) that ensures a class has only a limited number of instances, where each instance is uniquely identified by a key. Unlike the [Singleton Pattern](Singleton%20Pattern.md), which allows only one instance of a class, the Multiton Pattern allows multiple instances, each associated with a specific key.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Multiton Class**: Define a class that you want to limit the number of instances for. This class will manage multiple instances, each associated with a unique key.
|
||||
2. **Private Constructor**: Ensure that the constructor of the class is private to prevent external instantiation.
|
||||
3. **Internal Registry**: Create an internal registry (usually a dictionary or map) within the class to store instances associated with their respective keys.
|
||||
4. **Factory Method**: Implement a factory method or a static method within the class to create or retrieve instances based on keys.
|
||||
5. **Use Keys**: When you need an instance of the class, provide a key to the factory method. The method will either create a new instance or return an existing instance associated with the provided key.
|
||||
|
||||
## **Example:**
|
||||
Let's say you're creating a logging system for your application, and you want to have separate loggers for different modules. You can use the Multiton Pattern to ensure that each module has its own logger:
|
||||
|
||||
- **Multiton Class**: Define a `Logger` class.
|
||||
```java
|
||||
public class Logger {
|
||||
private static final Map<String, Logger> instances = new HashMap<>();
|
||||
|
||||
private Logger() {
|
||||
// Private constructor
|
||||
}
|
||||
|
||||
public static Logger getInstance(String module) {
|
||||
if (!instances.containsKey(module)) {
|
||||
instances.put(module, new Logger());
|
||||
}
|
||||
return instances.get(module);
|
||||
}
|
||||
|
||||
public void log(String message) {
|
||||
// Logging implementation
|
||||
System.out.println(message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Private Constructor**: Ensure that the constructor of the `Logger` class is private to prevent external instantiation.
|
||||
- **Internal Registry**: Use a `Map<String, Logger>` (`instances`) to store logger instances associated with their respective modules (keys).
|
||||
- **Factory Method**: Implement a `getInstance` static method that takes a `module` parameter and returns a `Logger` instance associated with that module. If an instance for the module does not exist, create one and store it in the registry.
|
||||
- **Use Keys**: In your application, when you need a logger for a specific module, use the `Logger.getInstance(module)` method to obtain or create the logger instance.
|
||||
```java
|
||||
Logger loggerA = Logger.getInstance("ModuleA");
|
||||
Logger loggerB = Logger.getInstance("ModuleB");
|
||||
|
||||
loggerA.log("Log message for ModuleA");
|
||||
loggerB.log("Log message for ModuleB");
|
||||
```
|
||||
|
||||
By using the Multiton Pattern, you ensure that each module gets its own unique logger instance, identified by the module name. This allows you to have separate loggers for different parts of your application while keeping the logging code centralized and manageable.
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Object Pool Pattern
|
||||
|
||||
## **Description:**
|
||||
The Object Pool Pattern is a creational design pattern that manages a pool of reusable objects to avoid the overhead of creating and destroying objects frequently. It provides a way to efficiently manage and reuse a limited number of instances of a class, especially when creating and initializing these instances is resource-intensive.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Create a Pool**: Create a pool of objects that you want to reuse. These objects could be expensive to create, initialize, or destroy.
|
||||
2. **Initialization**: Initialize the pool by creating a predefined number of objects and adding them to the pool.
|
||||
3. **Object Request**: When an object is needed, instead of creating a new one, request an object from the pool.
|
||||
4. **Object Return**: After using an object, return it to the pool for reuse. This is typically done by marking the object as available.
|
||||
5. **Manage Pool Size (Optional)**: Implement a mechanism to ensure that the pool doesn't exceed a maximum size. If the pool is empty and an object is requested, you can decide whether to create a new object or wait until an object becomes available.
|
||||
|
||||
## **Example:**
|
||||
Consider a scenario where you have a web server that handles incoming HTTP requests. Creating a new connection to a database for each request can be expensive. You can use the Object Pool Pattern to manage a pool of database connections efficiently:
|
||||
|
||||
- **Create a Pool**: Define a `DatabaseConnection` class to represent database connections.
|
||||
- **Initialization**: Create and initialize a fixed number of database connections when the application starts. Add these connections to the pool.
|
||||
- **Object Request**: When a new HTTP request arrives and requires a database connection, request a connection from the pool.
|
||||
- **Object Return**: After handling the request, return the database connection to the pool for reuse.
|
||||
- **Manage Pool Size (Optional)**: Implement mechanisms to control the pool size, such as setting a maximum number of connections or waiting for a connection to become available if the pool is empty.
|
||||
|
||||
|
||||
Here's a simplified example in Java:
|
||||
```java
|
||||
public class DatabaseConnection {
|
||||
private boolean inUse;
|
||||
|
||||
public DatabaseConnection() {
|
||||
// Initialize the database connection
|
||||
// ...
|
||||
}
|
||||
|
||||
public void markInUse() {
|
||||
inUse = true;
|
||||
}
|
||||
|
||||
public void markAvailable() {
|
||||
inUse = false;
|
||||
}
|
||||
|
||||
public boolean isInUse() {
|
||||
return inUse;
|
||||
}
|
||||
}
|
||||
|
||||
public class ConnectionPool {
|
||||
private List<DatabaseConnection> connections;
|
||||
|
||||
public ConnectionPool(int poolSize) {
|
||||
connections = new ArrayList<>();
|
||||
for (int i = 0; i < poolSize; i++) {
|
||||
connections.add(new DatabaseConnection());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized DatabaseConnection getConnection() {
|
||||
for (DatabaseConnection connection : connections) {
|
||||
if (!connection.isInUse()) {
|
||||
connection.markInUse();
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
return null; // or create a new connection if allowed
|
||||
}
|
||||
|
||||
public synchronized void releaseConnection(DatabaseConnection connection) {
|
||||
connection.markAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
// In your web server code
|
||||
ConnectionPool pool = new ConnectionPool(10); // Initialize a pool with 10 database connections
|
||||
|
||||
// When handling an HTTP request
|
||||
DatabaseConnection connection = pool.getConnection();
|
||||
if (connection != null) {
|
||||
// Use the connection for database operations
|
||||
// ...
|
||||
// Return the connection to the pool when done
|
||||
pool.releaseConnection(connection);
|
||||
}
|
||||
```
|
||||
|
||||
By using the Object Pool Pattern, you can efficiently manage and reuse database connections, reducing the overhead of creating and destroying connections for each HTTP request. This can improve the performance and resource usage of your web server.
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Prototype Pattern
|
||||
|
||||
## **Description:**
|
||||
The Prototype Pattern is a creational design pattern that allows you to create new objects by copying an existing object, known as the "prototype." It is particularly useful when the cost of creating an object is more expensive or complex than copying it, or when you want to create new objects with the same initial state as an existing one.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Prototype Interface or Abstract Class**: Define a common interface or abstract class for all concrete prototypes. This interface should include a method for cloning the object.
|
||||
2. **Concrete Prototypes**: Create concrete classes that implement the prototype interface or extend the prototype abstract class. These classes define the specific properties and behaviors of the objects you want to clone.
|
||||
3. **Clone Method**: Implement the clone method in each concrete prototype class. This method should create a new instance of the same class and copy the state from the current instance to the new one.
|
||||
4. **Client Code**: In your application, use the prototype to create new objects by cloning the prototype object. The client code should not be responsible for creating objects from scratch but should rely on the prototype to provide new instances.
|
||||
|
||||
## **Example:**
|
||||
Let's say you are developing a game where characters can be customized with various attributes, such as name, appearance, and abilities. Instead of creating each character from scratch, you can use the Prototype Pattern to clone a prototype character with default attributes and customize the clones as needed:
|
||||
|
||||
- **Prototype Interface or Abstract Class**: Define a `Character` interface or abstract class that includes a `clone` method.
|
||||
```java
|
||||
public interface Character {
|
||||
Character clone();
|
||||
void customize(String name, String appearance, String abilities);
|
||||
void displayInfo();
|
||||
}
|
||||
```
|
||||
|
||||
- **Concrete Prototypes**: Create concrete character classes that implement the `Character` interface or extend the `Character` abstract class. Each class represents a specific type of character.
|
||||
```java
|
||||
public class Warrior implements Character {
|
||||
private String name;
|
||||
private String appearance;
|
||||
private String abilities;
|
||||
|
||||
public Warrior() {
|
||||
// Default attributes for a warrior
|
||||
this.name = "Warrior";
|
||||
this.appearance = "Strong and armored";
|
||||
this.abilities = "Melee combat skills";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character clone() {
|
||||
Warrior clone = new Warrior();
|
||||
clone.customize(this.name, this.appearance, this.abilities);
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(String name, String appearance, String abilities) {
|
||||
this.name = name;
|
||||
this.appearance = appearance;
|
||||
this.abilities = abilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayInfo() {
|
||||
System.out.println("Character: " + name);
|
||||
System.out.println("Appearance: " + appearance);
|
||||
System.out.println("Abilities: " + abilities);
|
||||
}
|
||||
}
|
||||
|
||||
// Create similar classes for other character types (e.g., Mage, Rogue)
|
||||
```
|
||||
|
||||
- **Client Code**: In your game code, use the prototype to create new character instances and customize them as needed:
|
||||
```java
|
||||
Character prototype = new Warrior();
|
||||
Character warrior1 = prototype.clone();
|
||||
warrior1.customize("Sir Lancelot", "Shiny armor", "Swordsmanship");
|
||||
|
||||
Character warrior2 = prototype.clone();
|
||||
warrior2.customize("Lady Guinevere", "Elegant armor", "Archery");
|
||||
|
||||
warrior1.displayInfo();
|
||||
warrior2.displayInfo();
|
||||
```
|
||||
|
||||
By using the Prototype Pattern, you can create new character instances by cloning the prototype, customizing them for different characters in your game, and displaying their individual attributes. This pattern promotes code reusability and flexibility when creating objects with similar initial states.
|
57
technology/programming/patterns/creational/RAII Pattern.md
Normal file
57
technology/programming/patterns/creational/RAII Pattern.md
Normal file
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Resource Acquisition Is Initialization (RAII)
|
||||
|
||||
## **Description:**
|
||||
Resource Acquisition Is Initialization (RAII) is an idiom rather than a design pattern, but it's an important and widely used programming technique. RAII ties the lifetime of a resource (e.g., memory, files, locks) to the scope of an object. It ensures that the resource is automatically acquired when an object is created and automatically released when the object goes out of scope, thus preventing resource leaks and simplifying resource management.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Resource Management Class**: Create a resource management class (often referred to as a "wrapper" or "smart" class) that encapsulates the resource's allocation and deallocation logic.
|
||||
2. **Constructor**: In the constructor of the resource management class, acquire the resource (e.g., allocate memory, open a file, acquire a lock).
|
||||
3. **Destructor (or Dispose Method)**: In the destructor (or a dispose method for languages like C#), release the resource (e.g., deallocate memory, close a file, release a lock). The destructor is automatically called when the object goes out of scope.
|
||||
4. **Use in Client Code**: In your client code, create an instance of the resource management class. The resource will be automatically acquired when the object is created and automatically released when it goes out of scope.
|
||||
|
||||
## **Example:**
|
||||
A common example of RAII is managing dynamic memory in C++ using the `std::unique_ptr` smart pointer:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
class MyResource {
|
||||
public:
|
||||
MyResource() {
|
||||
// Acquire a resource (e.g., allocate memory)
|
||||
data = new int[10];
|
||||
std::cout << "Resource acquired." << std::endl;
|
||||
}
|
||||
|
||||
~MyResource() {
|
||||
// Release the resource (e.g., deallocate memory)
|
||||
delete[] data;
|
||||
std::cout << "Resource released." << std::endl;
|
||||
}
|
||||
|
||||
void performOperation() {
|
||||
// Perform some operation with the resource
|
||||
std::cout << "Operation performed." << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
int* data;
|
||||
};
|
||||
|
||||
int main() {
|
||||
// Create an instance of MyResource
|
||||
MyResource resource;
|
||||
|
||||
// Use the resource
|
||||
resource.performOperation();
|
||||
|
||||
// Resource is automatically released when 'resource' goes out of scope
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
In this C++ example, the `MyResource` class acquires and releases a resource (dynamic memory) in its constructor and destructor, respectively. When you create an instance of `MyResource` in the `main` function, the resource is automatically acquired, and when `resource` goes out of scope at the end of `main`, the resource is automatically released. This ensures that you don't have to worry about explicitly managing the resource's lifecycle in your client code, reducing the risk of resource leaks.
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Singleton Pattern
|
||||
|
||||
## **Description:**
|
||||
The Singleton Pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance. It is commonly used when you want to restrict the instantiation of a class to a single object and control access to that unique instance across the application.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Private Constructor**: Ensure that the class has a private constructor to prevent direct instantiation from external code.
|
||||
2. **Static Instance**: Create a static instance of the class within the class itself.
|
||||
3. **Lazy or Eager Initialization**: Decide whether to initialize the instance lazily (on-demand) or eagerly (at the time of class loading). Lazy initialization is preferred if resource usage should be minimized.
|
||||
4. **Static Method**: Provide a static method within the class to access the unique instance. This method should handle the creation of the instance if it doesn't exist or return the existing instance.
|
||||
5. **Thread Safety (Optional)**: If your application is multithreaded, ensure that the creation of the instance and access to it is thread-safe, especially if using lazy initialization.
|
||||
|
||||
## **Example:**
|
||||
A classic example of the Singleton Pattern is creating a configuration manager that provides access to application-wide configuration settings:
|
||||
|
||||
```java
|
||||
public class ConfigurationManager {
|
||||
private static ConfigurationManager instance; // Static instance variable
|
||||
private String configurationData;
|
||||
|
||||
// Private constructor to prevent external instantiation
|
||||
private ConfigurationManager() {
|
||||
// Initialize configurationData from a configuration file or other source
|
||||
configurationData = "Default configuration";
|
||||
}
|
||||
|
||||
// Static method to provide access to the unique instance
|
||||
public static ConfigurationManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ConfigurationManager(); // Lazy initialization
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getConfigurationData() {
|
||||
return configurationData;
|
||||
}
|
||||
|
||||
public void setConfigurationData(String data) {
|
||||
configurationData = data;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this Java example:
|
||||
- The `ConfigurationManager` class has a private constructor to prevent external instantiation.
|
||||
- It contains a private static instance variable.
|
||||
- The `getInstance` static method is used to access the unique instance of the class. It initializes the instance lazily when called for the first time.
|
||||
- You can use `ConfigurationManager.getInstance()` to access the configuration manager from anywhere in your application.
|
||||
|
||||
```java
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
ConfigurationManager configManager1 = ConfigurationManager.getInstance();
|
||||
ConfigurationManager configManager2 = ConfigurationManager.getInstance();
|
||||
|
||||
// Both instances refer to the same unique object
|
||||
System.out.println(configManager1 == configManager2); // true
|
||||
|
||||
// Access and modify configuration data
|
||||
System.out.println(configManager1.getConfigurationData()); // Default configuration
|
||||
configManager1.setConfigurationData("New configuration");
|
||||
System.out.println(configManager2.getConfigurationData()); // New configuration
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Singleton Pattern ensures that there's only one instance of `ConfigurationManager` throughout the application, providing a centralized and controlled way to manage configuration data.
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Adapter Pattern
|
||||
|
||||
## **Description:**
|
||||
The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces, enabling them to collaborate without altering their source code. The Adapter Pattern is useful when you want to reuse existing classes or integrate third-party libraries that don't quite fit your desired interface.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Identify Interfaces**: Identify the two interfaces that need to work together but are incompatible.
|
||||
2. **Adapter Class**: Create an adapter class that implements the interface expected by the client code (the target interface) while containing an instance of the class with the incompatible interface (the adaptee).
|
||||
3. **Adapter Methods**: Within the adapter class, implement methods that map the methods of the target interface to the corresponding methods of the adaptee interface.
|
||||
4. **Client Code**: In the client code, use the adapter class to interact with the adaptee class as if it implements the target interface.
|
||||
|
||||
## **Example:**
|
||||
Suppose you have a legacy class `OldSystem` with an interface that is incompatible with your modern system, but you need to integrate the legacy functionality. Here's how the Adapter Pattern can be applied:
|
||||
|
||||
- **Incompatible Legacy Class (Adaptee)**: Define the `OldSystem` class with its existing interface.
|
||||
```java
|
||||
public class OldSystem {
|
||||
public void doOldStuff() {
|
||||
System.out.println("Doing old stuff in the legacy system.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Target Interface**: Define the `NewSystem` interface that your modern system expects.
|
||||
```java
|
||||
public interface NewSystem {
|
||||
void doNewStuff();
|
||||
}
|
||||
```
|
||||
|
||||
- **Adapter Class**: Create an adapter class `Adapter` that implements the `NewSystem` interface and contains an instance of the `OldSystem` class.
|
||||
```java
|
||||
public class Adapter implements NewSystem {
|
||||
private OldSystem oldSystem;
|
||||
|
||||
public Adapter(OldSystem oldSystem) {
|
||||
this.oldSystem = oldSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doNewStuff() {
|
||||
// Delegate the new system's request to the old system's method
|
||||
oldSystem.doOldStuff();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your modern system, use the `Adapter` class to work with the `OldSystem` as if it were a `NewSystem`.
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
OldSystem legacySystem = new OldSystem();
|
||||
NewSystem adapter = new Adapter(legacySystem);
|
||||
|
||||
// Use the adapter to perform new system operations
|
||||
adapter.doNewStuff();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using the Adapter Pattern, you can integrate the legacy `OldSystem` class seamlessly into your modern system without changing the code of the legacy class. The adapter serves as a bridge, translating calls from the new system's interface to the old system's interface, allowing them to work together.
|
107
technology/programming/patterns/structural/Bridge Patterns.md
Normal file
107
technology/programming/patterns/structural/Bridge Patterns.md
Normal file
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Bridge Pattern
|
||||
|
||||
## **Description:**
|
||||
The Bridge Pattern is a structural design pattern that separates an object's abstraction (high-level logic) from its implementation (low-level details). It allows you to vary both the abstraction and the implementation independently, making it easier to extend and maintain complex systems. This pattern is particularly useful when you have a hierarchy of classes with multiple dimensions of variation.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Identify Abstraction and Implementation**: Identify the components of your system that have multiple dimensions of variation. This typically involves defining an abstraction (high-level logic) and one or more implementations (low-level details).
|
||||
2. **Abstraction Class**: Create an abstraction class that contains a reference to an implementation object. The abstraction class defines high-level methods and delegates the implementation details to the implementation object.
|
||||
3. **Implementation Interface or Abstract Class**: Define an interface or an abstract class for the implementation, which will be implemented by concrete implementation classes.
|
||||
4. **Concrete Implementation Classes**: Create concrete implementation classes that implement the interface or extend the abstract class defined for the implementation. These classes contain the low-level implementation details.
|
||||
5. **Client Code**: In your client code, use the abstraction class to interact with the high-level logic while providing or selecting the specific implementation at runtime.
|
||||
|
||||
## **Example:**
|
||||
Consider a drawing application where you have different shapes (e.g., circles, rectangles) that can be drawn using different rendering techniques (e.g., vector graphics, raster graphics). Here's how the Bridge Pattern can be applied:
|
||||
|
||||
- **Abstraction Class**: Create an abstraction class `Shape` that contains a reference to an implementation object `Renderer`.
|
||||
```java
|
||||
public abstract class Shape {
|
||||
protected Renderer renderer;
|
||||
|
||||
public Shape(Renderer renderer) {
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
public abstract void draw();
|
||||
}
|
||||
```
|
||||
|
||||
- **Implementation Interface**: Define an interface `Renderer` for the rendering implementation.
|
||||
```java
|
||||
public interface Renderer {
|
||||
void render();
|
||||
}
|
||||
```
|
||||
|
||||
- **Concrete Implementation Classes**: Create concrete implementation classes for different rendering techniques, such as `VectorRenderer` and `RasterRenderer`.
|
||||
```java
|
||||
public class VectorRenderer implements Renderer {
|
||||
@Override
|
||||
public void render() {
|
||||
System.out.println("Rendering as vector graphics.");
|
||||
}
|
||||
}
|
||||
|
||||
public class RasterRenderer implements Renderer {
|
||||
@Override
|
||||
public void render() {
|
||||
System.out.println("Rendering as raster graphics.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Concrete Abstraction Classes**: Create concrete abstraction classes for different shapes, such as `Circle` and `Rectangle`, that extend the `Shape` class and provide specific implementations for the `draw` method.
|
||||
```java
|
||||
public class Circle extends Shape {
|
||||
private int radius;
|
||||
|
||||
public Circle(Renderer renderer, int radius) {
|
||||
super(renderer);
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
System.out.println("Drawing a circle with radius " + radius);
|
||||
renderer.render();
|
||||
}
|
||||
}
|
||||
|
||||
public class Rectangle extends Shape {
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public Rectangle(Renderer renderer, int width, int height) {
|
||||
super(renderer);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
System.out.println("Drawing a rectangle with width " + width + " and height " + height);
|
||||
renderer.render();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your application, use the abstraction classes to create shapes and specify the rendering technique at runtime.
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
Renderer vectorRenderer = new VectorRenderer();
|
||||
Renderer rasterRenderer = new RasterRenderer();
|
||||
|
||||
Shape circle = new Circle(vectorRenderer, 5);
|
||||
Shape rectangle = new Rectangle(rasterRenderer, 10, 20);
|
||||
|
||||
circle.draw();
|
||||
rectangle.draw();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using the Bridge Pattern, you can vary the shapes and rendering techniques independently, making it easier to add new shapes or rendering methods without affecting existing code. This pattern promotes flexibility and maintainability in complex systems with multiple dimensions of variation.
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Composite Pattern
|
||||
|
||||
## **Description:**
|
||||
The Composite Pattern is a structural design pattern that allows you to compose objects into tree structures to represent part-whole hierarchies. It lets clients treat individual objects and compositions of objects uniformly. In other words, you can use the same interface to work with both single objects and compositions of objects. This pattern is particularly useful when dealing with hierarchical structures and you want to apply operations recursively.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Component Interface**: Define an abstract class or interface, often called `Component`, that represents the common interface for both leaf and composite objects. This interface includes methods that are common to all objects in the hierarchy.
|
||||
2. **Leaf Class**: Create a leaf class that implements the `Component` interface. Leaf objects are the individual objects that do not have children in the hierarchy.
|
||||
3. **Composite Class**: Create a composite class that also implements the `Component` interface. Composite objects can contain child components, including both leaf and other composite objects.
|
||||
4. **Add and Remove Methods**: In the composite class, provide methods to add and remove child components. These methods allow you to build and modify the hierarchical structure dynamically.
|
||||
5. **Client Code**: In your client code, work with objects through the `Component` interface. You can treat individual objects and compositions of objects uniformly, as they share a common interface.
|
||||
|
||||
## **Example:**
|
||||
Imagine you are building a graphical user interface (GUI) library that needs to handle a hierarchy of UI elements, including individual controls (buttons, text fields) and composite elements (e.g., windows containing other controls). Here's how the Composite Pattern can be applied:
|
||||
|
||||
- **Component Interface**: Define an abstract `UIComponent` interface that represents the common interface for all UI element
|
||||
```java
|
||||
public interface UIComponent {
|
||||
void render();
|
||||
}
|
||||
```
|
||||
|
||||
- **Leaf Class**: Create leaf classes like `Button` and `TextField` that implement the `UIComponent` interface.
|
||||
```java
|
||||
public class Button implements UIComponent {
|
||||
private String label;
|
||||
|
||||
public Button(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
System.out.println("Rendered Button: " + label);
|
||||
}
|
||||
}
|
||||
|
||||
public class TextField implements UIComponent {
|
||||
@Override
|
||||
public void render() {
|
||||
System.out.println("Rendered TextField");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Composite Class**: Create a composite class, such as `Window`, that implements the `UIComponent` interface and can contain child components.
|
||||
```java
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Window implements UIComponent {
|
||||
private List<UIComponent> children = new ArrayList<>();
|
||||
|
||||
public void add(UIComponent component) {
|
||||
children.add(component);
|
||||
}
|
||||
|
||||
public void remove(UIComponent component) {
|
||||
children.remove(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
System.out.println("Rendered Window");
|
||||
for (UIComponent child : children) {
|
||||
child.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your GUI library or application, create instances of leaf objects and composite objects and use them through the `UIComponent` interface.
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
UIComponent button1 = new Button("OK");
|
||||
UIComponent button2 = new Button("Cancel");
|
||||
UIComponent textField = new TextField();
|
||||
|
||||
Window window = new Window();
|
||||
window.add(button1);
|
||||
window.add(button2);
|
||||
window.add(textField);
|
||||
|
||||
window.render();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using the Composite Pattern, you can build complex UI hierarchies by composing individual UI elements (leaf objects) and containers (composite objects). Clients can work with these elements uniformly, regardless of whether they are individual controls or nested containers. This promotes code reusability and flexibility when dealing with hierarchical structures.
|
106
technology/programming/patterns/structural/Decorator Pattern.md
Normal file
106
technology/programming/patterns/structural/Decorator Pattern.md
Normal file
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Decorator Pattern
|
||||
|
||||
## **Description:**
|
||||
The Decorator Pattern is a structural design pattern that allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class. It is used to extend the functionality of classes in a flexible and reusable way, promoting the principle of open-closed design.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Component Interface**: Define an abstract class or interface, often called `Component`, that represents the base object's interface. This is the interface that decorators and concrete components will implement.
|
||||
2. **Concrete Component**: Create a concrete component class that implements the `Component` interface. This class represents the base object to which additional behavior can be added.
|
||||
3. **Decorator**: Create abstract decorator classes that also implement the `Component` interface. These classes have a reference to a `Component` object, and they can add, modify, or extend the behavior of the base component.
|
||||
4. **Concrete Decorators**: Create concrete decorator classes that extend the abstract decorator classes. These classes provide specific functionality that can be added to the base component.
|
||||
5. **Composition**: Use composition to combine the base component with one or more decorators. The decorators can be stacked, allowing multiple responsibilities to be added to the base component in a flexible manner.
|
||||
|
||||
## **Example:**
|
||||
Let's say you are building a text editor application, and you want to add text formatting features like bold, italic, and underline. You can use the Decorator Pattern to add these features dynamically to the text without changing the text object itself:
|
||||
|
||||
- **Component Interface**: Define an interface `Text` that represents the basic text functionality.
|
||||
```java
|
||||
public interface Text {
|
||||
String getContent();
|
||||
}
|
||||
```
|
||||
|
||||
- **Concrete Component**: Create a concrete component class `PlainText` that implements the `Text` interface.
|
||||
```java
|
||||
public class PlainText implements Text {
|
||||
private String content;
|
||||
|
||||
public PlainText(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Decorator**: Create an abstract decorator class `TextDecorator` that also implements the `Text` interface and has a reference to a `Text` object.
|
||||
```java
|
||||
public abstract class TextDecorator implements Text {
|
||||
protected Text decoratedText;
|
||||
|
||||
public TextDecorator(Text decoratedText) {
|
||||
this.decoratedText = decoratedText;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Concrete Decorators**: Create concrete decorator classes for formatting, such as `BoldText`, `ItalicText`, and `UnderlineText`, which extend `TextDecorator` and add specific formatting.
|
||||
```java
|
||||
public class BoldText extends TextDecorator {
|
||||
public BoldText(Text decoratedText) {
|
||||
super(decoratedText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
return "<b>" + decoratedText.getContent() + "</b>";
|
||||
}
|
||||
}
|
||||
|
||||
public class ItalicText extends TextDecorator {
|
||||
public ItalicText(Text decoratedText) {
|
||||
super(decoratedText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
return "<i>" + decoratedText.getContent() + "</i>";
|
||||
}
|
||||
}
|
||||
|
||||
public class UnderlineText extends TextDecorator {
|
||||
public UnderlineText(Text decoratedText) {
|
||||
super(decoratedText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
return "<u>" + decoratedText.getContent() + "</u>";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your text editor application, create an instance of the base `PlainText` class and decorate it with one or more formatting decorators.
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
Text text = new PlainText("Hello, world!");
|
||||
|
||||
// Decorate the text with formatting
|
||||
text = new BoldText(text);
|
||||
text = new ItalicText(text);
|
||||
text = new UnderlineText(text);
|
||||
|
||||
// Output the formatted text
|
||||
System.out.println(text.getContent());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using the Decorator Pattern, you can add text formatting features to the base `PlainText` object dynamically. You can easily stack and combine different formatting decorators to achieve various formatting options without modifying the base text object's code. This pattern promotes flexibility and maintainability when extending the functionality of objects.
|
66
technology/programming/patterns/structural/Facade Pattern.md
Normal file
66
technology/programming/patterns/structural/Facade Pattern.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Facade Pattern
|
||||
|
||||
## **Description:**
|
||||
The Facade Pattern is a structural design pattern that provides a simplified and unified interface to a set of interfaces in a subsystem. It acts as a front-facing interface to a complex system, making it easier for clients to interact with that system. The Facade Pattern promotes loose coupling and encapsulation by hiding the complexities of the subsystem behind a simple interface.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Subsystem**: Identify a complex subsystem or set of related classes that perform various tasks but have a complex and possibly fragmented interface.
|
||||
2. **Facade Class**: Create a facade class that encapsulates the subsystem's functionality and provides a simplified, high-level interface for clients.
|
||||
3. **Facade Methods**: Define methods in the facade class that correspond to the common operations or workflows that clients need to perform within the subsystem.
|
||||
4. **Client Code**: Clients interact with the subsystem through the facade class by calling its methods. Clients are shielded from the complexities of the subsystem.
|
||||
|
||||
## **Example:**
|
||||
Imagine you are developing a multimedia processing library with various components for image processing, audio processing, and video processing. Each component has its own set of classes and interfaces, and interacting with them individually can be complex. You can use the Facade Pattern to create a simplified interface for clients:
|
||||
|
||||
- **Subsystem Classes**: Define classes for image processing, audio processing, and video processing, each with its own set of methods and complexities.
|
||||
- **Facade Class**: Create a `MultimediaProcessor` facade class that provides high-level methods for common multimedia operations, such as `processImage`, `processAudio`, and `processVideo`.
|
||||
```java
|
||||
public class MultimediaProcessor {
|
||||
private ImageProcessor imageProcessor;
|
||||
private AudioProcessor audioProcessor;
|
||||
private VideoProcessor videoProcessor;
|
||||
|
||||
public MultimediaProcessor() {
|
||||
this.imageProcessor = new ImageProcessor();
|
||||
this.audioProcessor = new AudioProcessor();
|
||||
this.videoProcessor = new VideoProcessor();
|
||||
}
|
||||
|
||||
public void processImage(String imagePath) {
|
||||
imageProcessor.loadImage(imagePath);
|
||||
imageProcessor.applyFilters();
|
||||
imageProcessor.saveImage();
|
||||
}
|
||||
|
||||
public void processAudio(String audioPath) {
|
||||
audioProcessor.loadAudio(audioPath);
|
||||
audioProcessor.adjustVolume();
|
||||
audioProcessor.saveAudio();
|
||||
}
|
||||
|
||||
public void processVideo(String videoPath) {
|
||||
videoProcessor.loadVideo(videoPath);
|
||||
videoProcessor.extractAudio();
|
||||
videoProcessor.convertToFormat();
|
||||
videoProcessor.saveVideo();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your client code, use the `MultimediaProcessor` facade class to perform multimedia operations without needing to interact with the individual image, audio, and video processing components directly.
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
MultimediaProcessor multimediaProcessor = new MultimediaProcessor();
|
||||
|
||||
multimediaProcessor.processImage("sample.jpg");
|
||||
multimediaProcessor.processAudio("sample.mp3");
|
||||
multimediaProcessor.processVideo("sample.mp4");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using the Facade Pattern, you provide a simplified and unified interface (`MultimediaProcessor`) for clients to perform multimedia processing operations. Clients don't need to be aware of the individual complexities and intricacies of the subsystem components (image processing, audio processing, video processing), promoting code modularity and ease of use. The Facade Pattern is particularly valuable when dealing with complex systems or libraries with numerous components.
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
## Flyweight Pattern
|
||||
|
||||
## **Description:**
|
||||
The Flyweight Pattern is a structural design pattern that is used to minimize memory usage or computational expenses by sharing as much as possible with related objects. It is particularly useful when you need to create a large number of similar objects efficiently and the objects have common, intrinsic (shared) properties and extrinsic (unique) properties that can be separated.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Identify Shared and Non-Shared Parts**: Identify the parts of objects that can be shared among multiple instances (intrinsic state) and the parts that must remain unique to each instance (extrinsic state).
|
||||
2. **Flyweight Factory**: Create a flyweight factory that manages a pool of shared flyweight objects. The factory is responsible for creating and maintaining the flyweight objects.
|
||||
3. **Flyweight Interface**: Define a flyweight interface that declares methods for accessing and manipulating the intrinsic state.
|
||||
4. **Concrete Flyweight Classes**: Create concrete flyweight classes that implement the flyweight interface and represent the shared intrinsic state.
|
||||
5. **Client Code**: In your client code, use the flyweight factory to obtain flyweight objects. For the extrinsic state (unique to each object), pass it as a parameter when requesting a flyweight object.
|
||||
|
||||
## **Example:**
|
||||
Consider a text editor where you want to efficiently represent and display a large number of characters. Most characters share common properties like font, size, and color, while their positions and content are unique. Here's how the Flyweight Pattern can be applied:
|
||||
|
||||
- **Flyweight Interface**: Define a `Character` interface that represents the common properties of characters.
|
||||
```java
|
||||
public interface Character {
|
||||
void display(int positionX, int positionY);
|
||||
}
|
||||
```
|
||||
|
||||
- **Concrete Flyweight Class**: Create a concrete flyweight class `SharedCharacter` that implements the `Character` interface and represents the shared intrinsic state (font, size, color).
|
||||
```java
|
||||
public class SharedCharacter implements Character {
|
||||
private char symbol;
|
||||
private Font font;
|
||||
private int size;
|
||||
private Color color;
|
||||
|
||||
public SharedCharacter(char symbol, Font font, int size, Color color) {
|
||||
this.symbol = symbol;
|
||||
this.font = font;
|
||||
this.size = size;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(int positionX, int positionY) {
|
||||
System.out.println("Symbol: " + symbol +
|
||||
", Font: " + font.getName() +
|
||||
", Size: " + size +
|
||||
", Color: " + color +
|
||||
", Position: (" + positionX + ", " + positionY + ")");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Flyweight Factory**: Create a flyweight factory `CharacterFactory` that manages a pool of shared flyweight objects.
|
||||
```java
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CharacterFactory {
|
||||
private Map<Character, Character> characterPool = new HashMap<>();
|
||||
|
||||
public Character getCharacter(char symbol, Font font, int size, Color color) {
|
||||
Character sharedCharacter = new SharedCharacter(symbol, font, size, color);
|
||||
characterPool.put(symbol, sharedCharacter);
|
||||
return sharedCharacter;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your client code, use the `CharacterFactory` to obtain flyweight objects and pass the unique extrinsic state (position) as a parameter when displaying characters.
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
CharacterFactory characterFactory = new CharacterFactory();
|
||||
|
||||
// Display characters at different positions with shared properties
|
||||
Character charA = characterFactory.getCharacter('A', Font.BOLD, 12, Color.BLACK);
|
||||
charA.display(10, 20);
|
||||
|
||||
Character charB = characterFactory.getCharacter('B', Font.ITALIC, 14, Color.RED);
|
||||
charB.display(30, 40);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using the Flyweight Pattern, you efficiently represent a large number of characters while sharing common properties, thus reducing memory usage. The extrinsic state (position) remains unique to each character instance, and the pattern helps manage shared and non-shared aspects effectively. This pattern is especially valuable in scenarios where many objects have a significant amount of shared state.
|
83
technology/programming/patterns/structural/Proxy Pattern.md
Normal file
83
technology/programming/patterns/structural/Proxy Pattern.md
Normal file
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
obj: concept
|
||||
---
|
||||
# Proxy Pattern
|
||||
|
||||
## **Description:**
|
||||
The Proxy Pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. It allows you to create a level of indirection to an object, which can be useful for various purposes such as controlling access, lazy loading, logging, monitoring, or adding a security layer to the real object.
|
||||
|
||||
**How it's Used:**
|
||||
1. **Real Subject**: Define an interface that both the real subject and the proxy implement. This interface represents the common operations that the real subject and proxy can perform.
|
||||
2. **Proxy Class**: Create a proxy class that implements the same interface as the real subject. This proxy class contains a reference to the real subject.
|
||||
3. **Proxy Methods**: In the proxy methods, you can add additional behavior before or after delegating the request to the real subject. This additional behavior can include access control, logging, or lazy loading.
|
||||
4. **Client Code**: Clients interact with the proxy object, thinking it's the real subject. The proxy object handles the interaction with the real subject.
|
||||
|
||||
## **Example:**
|
||||
Consider a scenario where you have a large collection of images, and you want to load and display them only when needed to improve performance. You can use the Proxy Pattern to implement lazy loading:
|
||||
|
||||
- **Subject Interface**: Define an interface `Image` that represents the common operations for images.
|
||||
```java
|
||||
public interface Image {
|
||||
void display();
|
||||
}
|
||||
```
|
||||
|
||||
- **Real Subject**: Create a real subject class `RealImage` that implements the `Image` interface. This class represents the actual image and loads it when required.
|
||||
```java
|
||||
public class RealImage implements Image {
|
||||
private String filename;
|
||||
|
||||
public RealImage(String filename) {
|
||||
this.filename = filename;
|
||||
loadFromDisk();
|
||||
}
|
||||
|
||||
private void loadFromDisk() {
|
||||
System.out.println("Loading image: " + filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display() {
|
||||
System.out.println("Displaying image: " + filename);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Proxy Class**: Create a proxy class `ProxyImage` that also implements the `Image` interface. This class contains a reference to the real image but loads it lazily when requested.
|
||||
```java
|
||||
public class ProxyImage implements Image {
|
||||
private RealImage realImage;
|
||||
private String filename;
|
||||
|
||||
public ProxyImage(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display() {
|
||||
if (realImage == null) {
|
||||
realImage = new RealImage(filename);
|
||||
}
|
||||
realImage.display();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Client Code**: In your client code, use the `ProxyImage` class to interact with images. Images are loaded and displayed only when the `display` method is called.
|
||||
```java
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
Image image1 = new ProxyImage("image1.jpg");
|
||||
Image image2 = new ProxyImage("image2.jpg");
|
||||
|
||||
// Images are loaded and displayed when requested
|
||||
image1.display();
|
||||
image2.display();
|
||||
|
||||
// No additional loading for the already displayed image
|
||||
image1.display();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using the Proxy Pattern, you can improve the performance of your application by loading and displaying images lazily. The proxy object (`ProxyImage`) handles the loading and displaying of the real images (`RealImage`) as needed. This pattern is also useful for scenarios where you need to control access to sensitive resources, log actions, or add additional functionality to an object without changing its core behavior.
|
Loading…
Add table
Add a link
Reference in a new issue