Advanced composition patterns

One of the classic books of design patterns, Design Patterns: Elements of Reusable Object-Oriented Software, identified a number of common patterns of object composition. Some of these patterns are more relevant to C++ or Java programming, and less relevant to Python programming. For example, the Singleton pattern is a first-class aspect of a Python module and a Python class definition; the complexities of Java static variables aren't necessary to implement this pattern.

A better source for Python design patterns is available at https://python-patterns.guide. The pattern descriptions on this Python Patterns website are focused on Python specifically. It's important to recognize that some of the complexity in object-oriented design pattern literature stems from creating elegant ways to create a runtime behavior in the presence of very strict compile-time checking. Python doesn't suffer from the same kinds of type management issues, making Python programming simpler. 

A central concept in Python is duck typing. The concept is based on the following quote:

"When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."

In Python, an object is usable when the methods and attributes fit a needed protocol. The actual base type doesn't matter; the available methods are what defines a class's suitability in a particular context.

We can, for example, define two similar-looking classes as shown in the following code. The first uses the typing.NamedTuple as a base class:

from typing import NamedTuple
from dataclasses import dataclass

class Domino_1(NamedTuple):
v1: int
v2: int

@property
def double(self):
return self.v1 == self.v2

This alternative version uses a @dataclass decorator to create a frozen object, similar to a tuple:

from dataclasses import dataclass

@dataclass
(frozen=True, eq=True, order=True)
class Domino_2:
v1: int
v2: int

@property
def double(self):
return self.v1 == self.v2

These two classes have nearly identical behavior. The only class they have in common is the superclass for all objects, the object class. Yet, these two classes are functionally interchangeable, and can be freely substituted for each other.

This ability to have equivalent types without a common superclass permits flexibility, but can also lead to a difficulty when trying to check types with mypy. In some cases, we may find the need to define an abstract superclass purely for the purposes of providing assurance that several distinct implementations all provide common features. In other cases, we may need to add a type hint like the following:

Domino = Union[Domino_1, Domino_2]

This definition provides a type name, Domino, with two concrete implementations. This provides information mypy can use to validate our software without the needless complexity of creating an abstract superclass. We can introduce new classes to this Union type without having to worry about inheritance. In order for this to work, the only requirement is for the classes to support the methods actually used by the application.

With this definition, we can use the following kind of factory method for building Domino instances:

class DominoBoneYard:

domino_class: Type[Domino] = Domino_1

def __init__(self, limit: int = 6) -> None:
self._dominoes: List[Domino] = [
self.domino_class(x, y)
for x in range(limit + 1)
for y in range(x + 1)
]
random.shuffle(self._dominoes)

The __init__() method builds the self._dominoes object with a type hint of List[Domino]. This hint embraces all of the classes in the Union[] type hint for the Domino type name. 

If we were to make a terrible mistake in using this class and try to use some code like DominoBoneYard.domino_class = tuple to create tuple objects, the mypy program would spot the type incompatibility and report an error with a message along the lines of Incompatible types in assignment (expression has type "Type[Tuple[Any, ...]]", variable has type "Union[Type[Domino_1], Type[Domino_2]]"). This message would inform us that the configuration choice of tuple is unlikely to work correctly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset