Summary

We've looked at using decorators to modify function and class definitions. We've also looked at mixins that allow us to decompose a larger class into components that are knitted together.

The idea of both of these techniques is to separate application-specific features from generic features, such as security, audit, or logging. We're going to distinguish between the inherent features of a class and aspects that aren't inherent but are additional concerns. The inherent features are part of the explicit design. They're part of the inheritance hierarchy; they define what an object is. The other aspects can be mixins or decorations; they define how an object might also act.

In most cases, this division between is-a and acts-as is quite clear. Inherent features are a part of the overall problem domain. When talking about simulating Blackjack play, things such as cards, hands, betting, hitting, and standing are clearly part of the problem domain. Similarly, the data collection and statistical analysis of outcomes is part of the solution. Other things, such as logging, debugging, security checks, and auditing are not part of the problem domain; these other aspects are associated with the solution technology. In some cases, they are part of regulatory compliance or another background context in which the software is used.

While most cases are quite clear, the dividing line between inherent and decorative aspects can be fine. In some cases, it may devolve to an aesthetic judgment. Generally, the decision becomes difficult when writing framework and infrastructure classes because they aren't focused on a specific problem. A general strategy for creating good designs is as follows:

  • Aspects that are central to the problem will contribute directly to class definitions. Many classes are based on nouns and verbs present in the problem domain. These classes form simple hierarchies; polymorphism among data objects works as expected when compared with real-world objects.
  • Some aspects are peripheral to the problem and will lead to mixin class definitions. These are things related to operational aspects of using the software more than solving the essential problem.

A class that involves mixins can be said to be multidimensional. It has more than one independent axis; aspects belong to orthogonal design considerations. When we define separate mixins, we can have separate inheritance hierarchies for the mixins. For our casino game simulations, there are two aspects: the rules of the game and a betting strategy. These are orthogonal considerations. The final player simulation classes must have mixin elements from both class hierarchies.

The type hints for decorators can become complex. In the most generic case, a decorator can be summarized as a function with an argument that's a Callable and a result that's a Callable. If we want to be specific about the arguments and results of the callable, there will be complex-looking type hints, often involving type variables to show how the Callable argument and the Callable result align. This can become very complex if the decorator changes the signature of the decorated function by modifying parameters or results.

As noted previously, object-oriented programming lets us follow a variety of design strategies, as follows:

  • Composition: We introduce functionality through wrapping one class with another class. This may involve the composition of various aspects under a façade. It may involve using mixins classes to add features, or decorators to add features.
  • Extension: This is the ordinary case of inheritance. This is appropriate where there is a clear is-a relationship among the class definitions. It works out best when the superclass is a unsurprising generalization of the subclass details. In this case, ordinary inheritance techniques work out well.

The forthcoming chapters will change direction. We've seen almost all of Python's special method names. The next five chapters are going to focus on object persistence and serialization. We'll start out with serializing and saving objects in various external notations, including JSON, YAML, Pickle, CSV, and XML.

Serialization and persistence introduce yet more object-oriented design considerations for our classes. We'll also have a look at object relationships and how they're represented. We'll also have a look at the cost complexity of serializing and deserializing objects, and at the security issues related to the deserialization of objects from untrustworthy sources.

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

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