mirror of
https://github.com/rjNemo/design-patterns
synced 2026-06-06 02:26:40 +00:00
parent
973cdbc89f
commit
11b03fee22
10 changed files with 129 additions and 2 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
**__pycache__
|
||||
*.pyc
|
||||
.vscode
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
.mypy_cache
|
||||
|
|
@ -12,3 +12,4 @@
|
|||
- [Singleton](creational/singleton/README.md)
|
||||
- [Structural Patterns](structural/README.md)
|
||||
- [Adapter](structural/adapter/README.md)
|
||||
- [Bridge](structural/bridge/README.md)
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@
|
|||
Structural patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient.
|
||||
|
||||
- [Adapter](adapter/README.md)
|
||||
- [Bridge](bridge/README.md)
|
||||
|
|
|
|||
|
|
@ -54,4 +54,4 @@ if __name__ == "__main__":
|
|||
|
||||
print("Client: But I can work with it via the Adapter:")
|
||||
adapter = Adapter(adaptee)
|
||||
client_code(adapter)
|
||||
client_code(adapter)
|
||||
|
|
|
|||
15
structural/bridge/IImplementation.py
Normal file
15
structural/bridge/IImplementation.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class Implementation(ABC):
|
||||
"""
|
||||
The Implementation defines the interface for all implementation classes. It
|
||||
doesn't have to match the Abstraction's interface. In fact, the two
|
||||
interfaces can be entirely different. Typically the Implementation
|
||||
interface provides only primitive operations, while the Abstraction defines
|
||||
higher-level operations based on those primitives.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def operation_implementation(self) -> str:
|
||||
pass
|
||||
35
structural/bridge/README.md
Normal file
35
structural/bridge/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Bridge
|
||||
|
||||
Bridge is a structural design pattern that divides business logic or huge class into separate class hierarchies that can be developed independently.
|
||||
|
||||
## Summary
|
||||
|
||||
Bridge is a structural design pattern that lets you split a large class or a set of closely related classes into two separate hierarchies—abstraction and implementation—which can be developed independently of each other.
|
||||
|
||||
## Problem
|
||||
|
||||
Say you have a geometric `Shape` class with a pair of subclasses: `Circle` and `Square`. You want to extend this class hierarchy to incorporate colors, so you plan to create `Red` and `Blue` shape subclasses. However, since you already have two subclasses, you’ll need to create four class combinations such as `BlueCircle` and `RedSquare`.
|
||||
|
||||
Adding new shape types and colors to the hierarchy will grow it exponentially. For example, to add a triangle shape you’d need to introduce two subclasses, one for each color. And after that, adding a new color would require creating three subclasses, one for each shape type. The further we go, the worse it becomes.
|
||||
|
||||
## Solution
|
||||
|
||||
This problem occurs because we’re trying to extend the shape classes in two independent dimensions: by form and by color. That’s a very common issue with class inheritance.
|
||||
|
||||
The Bridge pattern attempts to solve this problem by switching from inheritance to the object composition. What this means is that you extract one of the dimensions into a separate class hierarchy, so that the original classes will reference an object of the new hierarchy, instead of having all of its state and behaviors within one class.
|
||||
|
||||
## How to Implement
|
||||
|
||||
1. Identify the orthogonal dimensions in your classes. These independent concepts could be: abstraction/platform, domain/infrastructure, front-end/back-end, or interface/implementation.
|
||||
|
||||
1. See what operations the client needs and define them in the base abstraction class.
|
||||
|
||||
1. Determine the operations available on all platforms. Declare the ones that the abstraction needs in the general implementation interface.
|
||||
|
||||
1. For all platforms in your domain create concrete implementation classes, but make sure they all follow the implementation interface.
|
||||
|
||||
1. Inside the abstraction class, add a reference field for the implementation type. The abstraction delegates most of the work to the implementation object that’s referenced in that field.
|
||||
|
||||
1. If you have several variants of high-level logic, create refined abstractions for each variant by extending the base abstraction class.
|
||||
|
||||
1. The client code should pass an implementation object to the abstraction’s constructor to associate one with the other. After that, the client can forget about the implementation and work only with the abstraction object.
|
||||
0
structural/bridge/__init__.py
Normal file
0
structural/bridge/__init__.py
Normal file
27
structural/bridge/abstractions.py
Normal file
27
structural/bridge/abstractions.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from IImplementation import Implementation
|
||||
|
||||
|
||||
@dataclass
|
||||
class Abstraction:
|
||||
"""
|
||||
The Abstraction defines the interface for the "control" part of the two
|
||||
class hierarchies. It maintains a reference to an object of the
|
||||
Implementation hierarchy and delegates all of the real work to this object.
|
||||
"""
|
||||
|
||||
implementation: Implementation
|
||||
|
||||
def operation(self) -> str:
|
||||
return f"Abstraction: Base operation with:\n{self.implementation.operation_implementation()}"
|
||||
|
||||
|
||||
class ExtendedAbstraction(Abstraction):
|
||||
"""
|
||||
You can extend the Abstraction without changing the Implementation classes.
|
||||
"""
|
||||
|
||||
def operation(self) -> str:
|
||||
return f"""ExtendedAbstraction: Extended operation with:\n
|
||||
{self.implementation.operation_implementation()}"""
|
||||
15
structural/bridge/implementations.py
Normal file
15
structural/bridge/implementations.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
"""
|
||||
Each Concrete Implementation corresponds to a specific platform and implements
|
||||
the Implementation interface using that platform's API.
|
||||
"""
|
||||
from IImplementation import Implementation
|
||||
|
||||
|
||||
class ConcreteImplementationA(Implementation):
|
||||
def operation_implementation(self) -> str:
|
||||
return "ConcreteImplementationA: Here's the result on the platform A."
|
||||
|
||||
|
||||
class ConcreteImplementationB(Implementation):
|
||||
def operation_implementation(self) -> str:
|
||||
return "ConcreteImplementationB: Here's the result on the platform B."
|
||||
32
structural/bridge/main.py
Normal file
32
structural/bridge/main.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
"""
|
||||
The client code should be able to work with any pre-configured abstraction-
|
||||
implementation combination.
|
||||
"""
|
||||
from abstractions import Abstraction
|
||||
from IImplementation import Implementation
|
||||
from implementations import ConcreteImplementationA, ConcreteImplementationB
|
||||
|
||||
|
||||
def client_code(abstraction: Abstraction) -> None:
|
||||
"""
|
||||
Except for the initialization phase, where an Abstraction object gets
|
||||
linked with a specific Implementation object, the client code should only
|
||||
depend on the Abstraction class. This way the client code can support any
|
||||
abstraction-implementation combination.
|
||||
"""
|
||||
|
||||
print(abstraction.operation(), end="")
|
||||
|
||||
|
||||
implementation = ConcreteImplementationA()
|
||||
abstraction = Abstraction(implementation)
|
||||
|
||||
client_code(abstraction)
|
||||
|
||||
|
||||
print("\n")
|
||||
|
||||
implementation = ConcreteImplementationB()
|
||||
abstraction = Abstraction(implementation)
|
||||
|
||||
client_code(abstraction)
|
||||
Loading…
Reference in a new issue