Additional composite Command design patterns

We can identify a number of composite Command design patterns. In the previous example, we designed a composite object that implemented a sequence of operations. For inspiration, we can look at the bash shell composite operators: ;, &, |, as well as () for grouping. Beyond these, we have if, for, and while loops within the shell.

We looked at the semantic equivalent of the shell sequence operator, ;, in the Command_Sequence class definition. This concept of a sequence is so ubiquitous that many programming languages (such as the shell and Python) don't require an explicit operator; the shell's syntax simply uses end-of-line as an implied sequence operator.

The shell's & operator creates two commands that run concurrently instead of sequentially. We can create a Command_Concurrent class definition with a run() method that uses multiprocessing to create two subprocesses and waits for both to finish.

The | operator in the shell creates a pipeline: one command's output buffer is another command's input buffer—the commands run concurrently. In Python, we'd need to create a queue as well as two processes to read and write that queue. This is a more complex situation: it involves populating the queue objects into the configurations of each of the various children. Chapter 13, Transmitting and Sharing Objects, has some examples of using multiprocessing with queues to pass objects among concurrent processes.

The if command in the shell has a number of use cases; however, there's no compelling reason to provide anything more than a native Python implementation via a method in a subclass of Command. Creating a complex Command class to mimic Python's if-elif-else processing isn't helpful; we can—and should—use Python directly.

The while and for commands in the shell, similarly, aren't the sorts of things we need to define in a higher-level Command subclass. We can simply write this in a method in Python.

Here's an example of a for-all class definition that applies an existing command to all the values in a collection:

class ForAllBets_Simulate(Command):

def run(self) -> None:
for bet_class in "Flat", "Martingale", "OneThreeTwoSix":
self.config["betting_rule"] = bet_class
self.config["outputfile"] = Path("data")/f"ch18_simulation7_{bet_class}.dat"
sim = Simulate_Command()
sim.configure(argparse.Namespace(**self.config))
sim.run()

We enumerated the three classes of betting in our simulation. For each of these classes, we tweaked the configuration, created a simulation, and executed that simulation.

Note that this for-all class won't work with the Analyze_Command class defined previously. We can't simply create composites that reflect different scopes of work. The Analyze_Command class runs a single simulation, but the ForAllBets_Simulate class runs a collection of simulations. We have two choices to create compatible scopes of work: we could create an Analyze_All command or a ForAllBets_Sim_and_Analyze command. The design decision depends on the needs of the users.

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

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