diff --git a/structural/flyweight/flyweight.py b/structural/flyweight/flyweight.py new file mode 100644 index 0000000..ff7ee61 --- /dev/null +++ b/structural/flyweight/flyweight.py @@ -0,0 +1,20 @@ +import json +from typing import List + + +class Flyweight: + """ + The Flyweight stores a common portion of the state (also called intrinsic + state) that belongs to multiple real business entities. The Flyweight + accepts the rest of the state (extrinsic state, unique for each entity) via + its method parameters. + """ + + def __init__(self, shared_state: List[str]) -> None: + self._shared_state = shared_state + + def operation(self, unique_state: List[str]) -> None: + s = json.dumps(self._shared_state) + u = json.dumps(unique_state) + print(f"Flyweight: Displaying shared ({s}) and unique ({u}) state.", + end="") diff --git a/structural/flyweight/flyweight_factory.py b/structural/flyweight/flyweight_factory.py new file mode 100644 index 0000000..5ad7853 --- /dev/null +++ b/structural/flyweight/flyweight_factory.py @@ -0,0 +1,44 @@ +from typing import Dict, List + +from structural.flyweight.flyweight import Flyweight + + +class FlyweightFactory: + """ + The Flyweight Factory creates and manages the Flyweight objects. It ensures + that flyweights are shared correctly. When the client requests a flyweight, + the factory either returns an existing instance or creates a new one, if it + doesn't exist yet. + """ + + _flyweights: Dict[str, Flyweight] = {} + + def __init__(self, initial_flyweights: List) -> None: + for state in initial_flyweights: + self._flyweights[self.get_key(state)] = Flyweight(state) + + def get_key(self, state: List) -> str: + """ + Returns a Flyweight's string hash for a given state. + """ + + return "_".join(sorted(state)) + + def get_flyweight(self, shared_state: List) -> Flyweight: + """ + Returns an existing Flyweight with a given state or creates a new one. + """ + key = self.get_key(shared_state) + + if not self._flyweights.get(key): + print("FlyweightFactory: Can't find a flyweight, creating new one.") + self._flyweights[key] = Flyweight(shared_state) + else: + print("FlyweightFactory: Reusing existing flyweight.") + + return self._flyweights[key] + + def list_flyweights(self) -> None: + count = len(self._flyweights) + print(f"FlyweightFactory: I have {count} flyweights:") + print("\n".join(map(str, self._flyweights.keys())), end="") diff --git a/structural/flyweight/main.py b/structural/flyweight/main.py new file mode 100644 index 0000000..4e06067 --- /dev/null +++ b/structural/flyweight/main.py @@ -0,0 +1,37 @@ +from structural.flyweight.flyweight_factory import FlyweightFactory + + +def add_car_to_police_database(factory: FlyweightFactory, plates: str, + owner: str, brand: str, model: str, + color: str) -> None: + + print("\n\nClient: Adding a car to database.") + flyweight = factory.get_flyweight([brand, model, color]) + # The client code either stores or calculates extrinsic state and passes it + # to the flyweight's methods. + flyweight.operation([plates, owner]) + + +if __name__ == '__main__': + """ + The client code usually creates a bunch of pre-populated flyweights in the + initialization stage of the application. + """ + factory = FlyweightFactory([ + ["Chevrolet", "Camaro2018", "pink"], + ["Mercedes Benz", "C300", "black"], + ["Mercedes Benz", "C500", "red"], + ["BMW", "M5", "red"], + ["BMW", "X6", "white"], + ]) + + factory.list_flyweights() + + add_car_to_police_database( + factory, "CL234IR", "James Doe", "BMW", "M5", "red") + add_car_to_police_database( + factory, "CL234IR", "James Doe", "BMW", "X1", "red") + + print("\n") + + factory.list_flyweights()