Skip to content

Commit b437934

Browse files
committed
Structural edits
1 parent 6ceeaec commit b437934

10 files changed

+443
-215
lines changed

docs/structural_adapter.md

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,77 @@
11
# The Adapter Pattern (Structural)
22

3-
## Intent
3+
## Purpose
44

5-
The Adapter pattern is a design pattern that allows the interface of an existing class to be used as another interface without modifying the original class. It's often used when you want to use an existing class, but its interface isn't what you need. The Adapter makes the incompatible classes work together by wrapping its own methods and properties into a new format that is compatible with the client’s code.
5+
The Adapter pattern allows objects with incompatible interfaces to work together. It acts as a bridge between a class you already have and the interface you need. Instead of changing existing code, you create an adapter that translates one interface into another.
66

7-
## Problem It Solves
7+
## The Problem It Solves
88

9-
The problem this pattern addresses is when we have a class with an incompatible interface, but we want to use it as if it had a different interface. This can be due to various reasons like legacy systems, third-party libraries or simply because you're trying to follow the Single Responsibility Principle (SRP).
9+
Sometimes you want to use a class, but its methods don't match what your code expects. This often happens when working with legacy code, third-party libraries, or when following the Single Responsibility Principle (SRP) and keeping classes focused. Rather than modifying the original class (which might be risky or impossible), the Adapter pattern lets you create a wrapper that makes it compatible with your code.
1010

1111
## When to Use It
1212

13-
This pattern is best used when:
13+
Use the Adapter pattern when:
1414

15-
1. You want to use an existing class but its interface isn’t what you need.
16-
2. You have a working class, but it lacks some of the functionality that you require.
17-
3. The classes are incompatible and cannot work together as they do not share the same interface.
18-
4. You're trying to follow the Single Responsibility Principle (SRP).
15+
* You want to use an existing class, but its interface doesn’t match what you need.
16+
* You’re integrating with legacy code or third-party libraries.
17+
* You want to follow SRP by not changing the original class.
18+
* Classes with different interfaces need to work together.
1919

2020
## When NOT to Use It
2121

22-
This pattern should be avoided when:
22+
Avoid the Adapter pattern if:
2323

24-
1. If you have control over the original class, it would be better to modify that class directly.
25-
2. The classes are highly compatible and there's no need for an adapter.
26-
3. You're working with a legacy system where modifying the code is not feasible or desirable.
27-
4. When the Adapter introduces complexity into your design.
24+
* You control the original class and can safely change its interface.
25+
* The classes already work together without modification.
26+
* Adding an adapter would unnecessarily complicate your design.
2827

2928
## How It Works
3029

31-
The Adapter wraps the original class in an interface that clients expect, and it translates between the client’s expectations and the original class's interface. This translation is done by implementing a set of methods that make sense for the client to use.
30+
The Adapter pattern works by wrapping the incompatible class in a new class that implements the desired interface. This wrapper (the adapter) translates method calls or data between the expected interface and the actual one.
3231

3332
## Real-World Analogy
3433

35-
Imagine you have a foreign book which has words in your language but written in another language. The Adapter pattern provides an interface where this foreign book can be read, even though it's not understandable yet. It acts as a translator between the original (foreign) and desired (native) languages.
34+
Imagine you have a power plug from Europe, but you're in the U.S. The plug won't fit the outlet directly. An adapter converts the shape of the plug so you can use your device without modifying it. The same idea applies in software—adapters help mismatched parts work together without changing their internal workings.
3635

3736
## Simplified Example
3837

39-
Here is a simplified example of how the Adapter pattern works:
38+
Here's a simple Python example:
4039

4140
```python
42-
class Target: # The domain-specific interface that clients expect
41+
# The interface expected by the client
42+
class Target:
4343
def request(self):
4444
return "Target: The default behavior."
4545

46-
class Adaptee: # A class with an incompatible interface
46+
# An existing class with a different interface
47+
class Adaptee:
4748
def specific_request(self):
48-
return ".eetpadA eht fo roivaheb laicepS" # reversed string
49+
return ".eetpadA eht fo roivaheb laicepS" # reversed string
4950

50-
class Adapter(Target): # Adapts the Adaptee to the Target interface
51+
# Adapter converts the interface of Adaptee to match Target
52+
class Adapter(Target):
5153
def __init__(self, adaptee: Adaptee):
5254
self.adaptee = adaptee
5355

5456
def request(self):
5557
return f"Adapter: (TRANSLATED) {self.adaptee.specific_request()[::-1]}"
5658
```
5759

58-
In this example, `Target` is the interface that we expect to use. However, `Adaptee` has a different interface and cannot be used directly. The `Adapter` class wraps `Adaptee` in such a way that it can be used as if it had the expected interface.
60+
### Usage
5961

60-
## See Also
62+
```python
63+
adaptee = Adaptee()
64+
adapter = Adapter(adaptee)
65+
print(adapter.request()) # Output: Adapter: (TRANSLATED) Special behavior of the Adaptee.
66+
```
67+
68+
In this example:
69+
70+
* `Target` is the expected interface.
71+
* `Adaptee` has an incompatible method.
72+
* `Adapter` wraps `Adaptee` and translates its output to match what the client expects.
73+
74+
## Learn More
6175

62-
The corresponding Python file can be found [here](https://github.com/taggedzi/python-design-pattern-rag/blob/main/patterns/structural/adapter.py).
76+
You can find the complete implementation in Python here:
77+
[Adapter Pattern on GitHub](https://github.com/taggedzi/python-design-pattern-rag/blob/main/patterns/structural/adapter.py)

docs/structural_bridge.md

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,84 @@
11
# The Bridge Pattern (Structural)
22

3-
## Intent
3+
## Purpose
44

5-
The Bridge pattern is a design pattern designed to "decouple an abstraction from its implementation so that the two can vary independently". It's useful when you want to avoid permanent binding between abstraction and implementation, which means one shouldn't have to change the other.
5+
The Bridge pattern is used to separate an abstraction from its implementation, so that both can evolve independently. It helps reduce tight coupling between code that defines what something does (abstraction) and code that defines how it does it (implementation).
66

7-
## Problem It Solves
7+
## The Problem It Solves
88

9-
Imagine you are designing a software system where there are different ways of rendering shapes (e.g., as lines or pixels). The problem is that this could lead to tight coupling if every shape has its own renderer, making it hard to add new shapes without modifying existing ones. This pattern helps to decouple the abstraction from the implementation so they can vary independently.
9+
Imagine you’re building shapes (like circles and squares) that can be rendered in different ways (like using pixels or lines). Without the Bridge pattern, you'd need a separate class for every combination—`PixelCircle`, `LineCircle`, `PixelSquare`, and so on. This quickly becomes unmanageable as the number of shapes and renderers grows. The Bridge pattern solves this by allowing shapes and renderers to be combined flexibly at runtime.
1010

1111
## When to Use It
1212

13-
The Bridge Pattern should be used when you want to abstract and separate the implementation details from the high-level interface or abstraction, which makes them independent of each other.
13+
Use the Bridge pattern when:
14+
15+
* You want to avoid creating a large number of subclasses to support combinations of features.
16+
* You want to be able to change the implementation details without touching the abstraction.
17+
* You need flexibility in assigning different behaviors (implementations) to high-level logic (abstractions).
1418

1519
## When NOT to Use It
1620

17-
You shouldn't use the Bridge pattern if the "abstraction" and "implementation" are too tightly coupled. The key idea is that they should be able to vary independently, so a change in one should not require a change in the other.
21+
Avoid this pattern if:
22+
23+
* The abstraction and implementation are already tightly bound and unlikely to change.
24+
* Introducing the pattern would add unnecessary complexity to a simple design.
1825

1926
## How It Works
2027

21-
The Bridge Pattern involves an Abstraction (Shape) which has a reference to its Implementor (Renderer). This allows for different implementations of Renderer and Shape can use any implementation at runtime.
28+
The Bridge pattern breaks a class into two parts:
29+
30+
1. **Abstraction** – The high-level control or logic (e.g., a shape like `Circle`).
31+
2. **Implementor** – The low-level implementation (e.g., how the shape is rendered).
32+
33+
The abstraction holds a reference to the implementor. This means you can swap out implementations without modifying the abstraction.
2234

2335
## Real-World Analogy
2436

25-
You could think of the Bridge pattern as a road bridge that connects two different types of roads (abstractions) with varying degrees of difficulty to navigate (implementations). The bridge itself is designed in such a way that it allows both types of vehicles to pass through, without having to change either type.
37+
Think of a remote control (abstraction) that can operate different types of TVs (implementation). The remote sends commands, but the way the TV responds depends on the model. You can change the TV or the remote without having to redesign the other.
2638

2739
## Simplified Example
2840

29-
Here's a simplified example:
41+
Heres a simple Python implementation:
3042

3143
```python
32-
class Shape: # Abstraction
33-
def __init__(self, renderer):
34-
self.renderer = renderer # Implementor
44+
# Implementor
45+
class Renderer:
46+
def what_to_render(self):
47+
raise NotImplementedError
3548

36-
def draw(self):
37-
pass
49+
class VectorRenderer(Renderer):
50+
def what_to_render(self):
51+
return "lines"
52+
53+
class RasterRenderer(Renderer):
54+
def what_to_render(self):
55+
return "pixels"
56+
57+
# Abstraction
58+
class Shape:
59+
def __init__(self, name, renderer: Renderer):
60+
self.name = name
61+
self.renderer = renderer
3862

39-
class Circle(Shape): # Refined abstraction
4063
def draw(self):
4164
return f"Drawing {self.name} as {self.renderer.what_to_render()}"
4265

43-
class Renderer: # Implementor
44-
@staticmethod
45-
def what_to_render():
46-
pass
47-
48-
class VectorRenderer(Renderer): # Concrete implementor
49-
@staticmethod
50-
def what_to_render():
51-
return "lines"
66+
# Usage
67+
circle = Shape("Circle", VectorRenderer())
68+
print(circle.draw()) # Drawing Circle as lines
5269

53-
class RasterRenderer(Renderer): # Another concrete implementor
54-
@staticmethod
55-
def what_to_render():
56-
return "pixels"
70+
square = Shape("Square", RasterRenderer())
71+
print(square.draw()) # Drawing Square as pixels
5772
```
5873

59-
## See Also
74+
In this example:
75+
76+
* `Shape` is the abstraction.
77+
* `Renderer` is the implementation interface.
78+
* `VectorRenderer` and `RasterRenderer` are concrete implementations.
79+
* The `Shape` can work with any renderer.
80+
81+
## Learn More
6082

61-
The corresponding Python file can be found [here](https://github.com/taggedzi/python-design-pattern-rag/blob/main/patterns/structural/bridge.py).
83+
You can find the full implementation in Python here:
84+
[Bridge Pattern on GitHub](https://github.com/taggedzi/python-design-pattern-rag/blob/main/patterns/structural/bridge.py)

docs/structural_composite.md

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,94 @@
11
# The Composite Pattern (Structural)
22

3-
## Intent
3+
## Purpose
44

5-
The Composite pattern is used to organize and structure complex tree structures made up of objects where each object can be treated individually or collectively. It provides a way to compose objects into tree structures and then work with these structures as if they were individual objects.
5+
The Composite pattern is used to treat individual objects and groups of objects in a uniform way. It allows you to build tree-like structures (like folders, menus, or graphical elements) where each part of the tree can be treated the same—whether it's a single item or a group of items.
66

7-
## Problem It Solves
7+
## The Problem It Solves
88

9-
This design pattern addresses the problem of treating individual objects and compositions uniformly, which simplifies code management and organization. It allows clients to treat complex trees structured as well-defined data structures (individual objects) in a uniform manner.
9+
In many applications, you may need to work with structures made of both simple and complex elements. Without the Composite pattern, you’d have to write separate logic to handle individual items and groups, leading to repetitive code. This pattern unifies them under a common interface, simplifying how they’re managed and manipulated.
1010

1111
## When to Use It
1212

13-
The Composite pattern is ideal when you want to create complex tree structures where each node can be treated individually or collectively, making it suitable for use cases such as file systems, HTML documents, and database queries.
13+
Use the Composite pattern when:
14+
15+
* You need to represent part-whole hierarchies (e.g., file systems, document outlines).
16+
* You want to treat individual objects and groups of objects uniformly.
17+
* You need to support recursive structures, such as trees or nested containers.
1418

1519
## When NOT to Use It
1620

17-
It's not a good idea to overuse the Composite pattern because it might make code more complicated than necessary. If your application doesn’t require complex tree structures or hierarchies, using simpler patterns like classes could be sufficient.
21+
Avoid this pattern if:
22+
23+
* Your structure is flat and doesn’t benefit from a hierarchy.
24+
* Adding abstraction through a component interface makes your code unnecessarily complex.
25+
* Simpler class designs can get the job done more clearly.
1826

1927
## How It Works
2028

21-
The Composite pattern involves three main components: Component (abstract base class), Leaf (concrete component with no children) and Composite (concrete component with sub-components). The Component interface declares common operations for both simple and complex objects of a composition. A client uses these classes to work with the compositions in a uniform manner.
29+
The pattern involves three core parts:
30+
31+
1. **Component** – The base interface or abstract class that defines common operations.
32+
2. **Leaf** – Represents the individual objects with no children.
33+
3. **Composite** – Represents complex objects that can have child components (both leaves and other composites).
34+
35+
The key is that both `Leaf` and `Composite` implement the same interface, allowing client code to interact with them the same way.
2236

2337
## Real-World Analogy
2438

25-
Imagine you have a family tree, where each person can be an individual leaf (you), or they could be part of a larger group (your siblings). If your family grows, it's not uncommon for new members to join the group - just like how a composite object can contain other composites or leaves.
39+
Imagine a company org chart. An individual employee is a leaf, while a department head (composite) manages a group of employees and possibly other departments. Whether you're interacting with an individual or a department, you treat them the same when asking for a task report.
2640

2741
## Simplified Example
2842

29-
Here is a simplified example:
43+
Here’s a basic example in Python:
3044

3145
```python
32-
class Component: # Abstract base class
33-
def operation(self) -> str: pass
46+
from typing import List
3447

35-
class Leaf(Component): # Concrete component with no children
36-
def __init__(self, name: str) -> None: self.name = name
37-
def operation(self) -> str: return f"Leaf({self.name})"
48+
class Component:
49+
def operation(self) -> str:
50+
pass
3851

39-
class Composite(Component): # Concrete component with sub-components
40-
def __init__(self, name: str) -> None:
52+
class Leaf(Component):
53+
def __init__(self, name: str):
54+
self.name = name
55+
56+
def operation(self) -> str:
57+
return f"Leaf({self.name})"
58+
59+
class Composite(Component):
60+
def __init__(self, name: str):
4161
self.name = name
4262
self._children: List[Component] = []
43-
44-
def add(self, component: Component) -> None: self._children.append(component)
45-
def remove(self, component: Component) -> None: self._children.remove(component)
46-
def operation(self) -> str:
63+
64+
def add(self, component: Component) -> None:
65+
self._children.append(component)
66+
67+
def remove(self, component: Component) -> None:
68+
self._children.remove(component)
69+
70+
def operation(self) -> str:
4771
results = [child.operation() for child in self._children]
4872
return f"Composite({self.name})[{' + '.join(results)}]"
4973
```
5074

51-
## See Also
75+
### Example Usage:
76+
77+
```python
78+
root = Composite("root")
79+
leaf1 = Leaf("A")
80+
leaf2 = Leaf("B")
81+
subtree = Composite("branch")
82+
subtree.add(Leaf("C"))
83+
84+
root.add(leaf1)
85+
root.add(subtree)
86+
root.add(leaf2)
87+
88+
print(root.operation()) # Composite(root)[Leaf(A) + Composite(branch)[Leaf(C)] + Leaf(B)]
89+
```
90+
91+
## Learn More
5292

53-
The corresponding Python file can be found [here](https://github.com/taggedzi/python-design-pattern-rag/blob/main/patterns/structural/composite.py).
93+
For the full implementation in Python, visit:
94+
[Composite Pattern on GitHub](https://github.com/taggedzi/python-design-pattern-rag/blob/main/patterns/structural/composite.py)

0 commit comments

Comments
 (0)