Other software patterns

Outside of the patterns in the GoF book[GoF94], there were many more tomes of patterns available. Some were more successful than others. Some did not even claim to be design pattern books. Of those books, I have referenced only a few patterns, but for completeness, I summarise them here.

Ubiquitous language

From Domain Driven Design[DDD04], the pattern of enforcing everyone involved in the development to use the same terms to describe things as the experts in the domain that we’re developing for.

In many developments, you will notice how software developers speak in code. Not literal programming languages, but they will refer to things by how they are implemented, not by what they mean in the domain. This has a disconnecting effect on their interactions with the experts but also doesn’t make connecting layers of the code together very easy either.

When you think of something in the domain, such as a boat for a holiday fishing company, a programmer might think of it as a row or column in a schedule. If they start calling a boat a boat-column, it might confuse the experts, but not quite as much as calling it a row-boat. However, when they start referring to them as schedule items rather than literal machines, some nuance is lost. Maintenance and staffing information is complicated, and when someone points out some boats have more seats, others have wheelchair fittings, and some have different setups which can only be captained by certain crew, the information that a human scheduler needs to do their job would be missing.

The Ubiquitous language pattern requires the same language to be used everywhere, from conversations about the design to the names of classes in the model. When a term has been found to be ambiguous, it’s time to refactor.

The benefit of Ubiquitous language is that it helps find these differences by forcing the developer to create the natural interfaces for all these different views on the world. As a programmer, you likely think about files and classes, but the user doesn’t think about those. An album for one user is their copy on CD, not yet arrived. For another, it’s a title to purchase and keep on a cloud account. For yet another, it’s the sheet music or the art on the cover, or possibly even an income stream for their performance.

Giving things the right name helps find what can and what should not be available to those interacting with the objects.

Bounded context

Also from Domain Driven Design[DDD04], the pattern of being aware of a mapping between the context and model and how there can be more than one of each.

Different experts use different ways to identify the same things at different times. Someone will refer to the patient by their ailment, someone else by their name, and another only knows them by which bed they are in.

In projects where there are strongly separated user groups, you will find they do not always share a common language. If your users from group A use the same word for three different things used by group B, then you have a problem that is not easily resolvable. A Bounded context is one where this information loss gap is recognised and protected against. The pattern requires that the contexts be identified and the border between them be observed. This pattern is a precautionary tale for accidentally assuming you share a data model and making assumptions about the data from the point of view of one context alone.

Specification

The Specification pattern from Domain Driven Design[DDD04] describes an object, a ‘spec’, you can use as a predicate. The query method on the spec takes a candidate and returns a true or false response. A spec can be as simple as constantly returning true or false, but is usually configured in some way. Each instance of a spec is a reusable test. Constructor arguments set up the spec object to deliver a verdict later, such as verifying whether the object was over or under a limit. Its behaviour will generally stay the same once constructed. Think of spec as encapsulating judgement in an object.

In the example in chapter 10, the spec was realised in Python as a lambda. The principle of a spec is not that it is an object, but that it can be used as a decision-maker.

The examples often add the composition of spec objects with other spec objects, such as the unary NOT spec and the binary AND and OR specs that take other specs as arguments during construction and combine them logically during query evaluation.

Collect Low-level Protocol

One of the very earliest patterns in software. It is from Using Pattern Languages for Object-Oriented Programs[UPL87].

The pattern suggests, after initial decomposition, find the low-level code necessary to support the decomposition. Utility methods, protocols, file formats, that kind of thing. A process pattern implying an up front requirements and object modelling before committing to any coding.

Pipes and filters

Published in Pattern Oriented Software Architecture[POSA96], page 53, this architectural pattern provides a structure for processing data. This pattern can be the backbone of the complete application, but these days is a subarchitecture found in many other applications that process data on demand.

The basic premise is that of data streams passing through filters. The filters can do much more than cull; they can adjust, emit more, or completely transform the data. Each filter can take more than one input and can produce more than one output if necessary. The main point is that each filter can be a pure function of the incoming data.

This eminently debuggable processing pattern has been seen repeatedly appearing over the decades of software development. It appears to be a self-forming solution from the context of having a lot of the same format of data, wanting to transform it in the same way, and not necessarily being able to store all the data in working memory at the same time. It’s also emergent from wishing to create complicated purely functional transformation sequences as in-place mutation normally associated with sequences of operations is usually not available in pure functional languages.

Whole-part

Also published in Pattern Oriented Software Architecture, the Whole-part pattern suggests that some objects are more than the sum of their parts. There are three variants of the Whole presented, from a strict assemblage to a loosely connected set of objects. In every case, the Parts held within make up part of the information of the object, but there are capabilities specific to the Whole.

An example I like to provide is based on having a set of points. Each point has a position in space. A point can be moved in space, and any software that handles transformations of these points should be able to adjust their position. However, it is only once you collect the points into a composite or compound object representing more than one of them that the option to scale or rotate them makes any sense. In essence, before the Parts are a collection, orientation and scale cannot exist, and it is from the Whole that this property emerges.

The pattern solution suggests keeping the elements inaccessible to others and provide an interface only to the greater Whole object. In this pattern, Parts are encapsulated in the Whole object.