init
This commit is contained in:
commit
c5cd492449
475 changed files with 27928 additions and 0 deletions
|
@ -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.
|
Loading…
Add table
Add a link
Reference in a new issue