Chapter 15

Designing with Patterns

So far, in this third part of the book, we have been largely concerned with design approaches that involve following procedures (methods) to create applications. In this chapter we consider a quite different way of producing a design (or part of a design), by reusing abstract ideas about design structures that have been found to work well by others. In a way, it makes use of forms that could be considered as being more related to ‘design as a noun’ in order to support the activities of ‘design as a verb’!

Reuse of experience comes in various forms, and the idea of designing an application by reusing some form of pattern has attracted a lot of attention since the 1990s. Patterns offer another way of codifying experience about design, and of transferring it to others—although there are those who consider that they provide a knowledge transfer mechanism that is more appropriate to experienced designers than beginners.

The pattern concept can be employed at different levels of abstraction, and with different architectural styles, and although it is usually associated with object-oriented development, it is by no means restricted to being used with such architectures. However, as with all mechanisms for transferring design knowledge, patterns provide no automatic guarantee of success, and do require care in use.

15.1Patterns as a mechanism for knowledge transfer

The concept of the design pattern is one that originated in another discipline, and is rooted in the work of the architect Christopher Alexander (Alexander et al. 1977). He has described it in the following words.

As a simple example, we could have a pattern for building a school. The problem that it addresses is a generic one (to support the process of educating children and young adults), and the format of teachers and classes is more or less universal. However support for this might be realised in many different, but related, ways, with the differences between them reflecting such issues as educational structures, age group, climate etc.

While the ‘school pattern’ describes how something might be structured, there are other familiar patterns that describe how things behave. Bus services provide an example of a behavioural pattern with easily-recognised components (bus stations, stops, routes, timetables etc.). Catching a bus in an unfamiliar place might involve local variations, such as where to board, and when and how to pay, but the idea is a familiar one that usually requires little explanation.

Used in the context of design, a pattern can provide a generic solution to some problem which recurs in various forms, and which itself may be sub-part of a larger problem. A pattern can help a designer gain understanding of the characteristics of a particular problem as well as providing a strategy for addressing it. If it is well documented, it should also provide some ideas about the context where it might arise, as well as of any possible consequences (usually in the form of technical debt) that might be incurred when using that pattern.

Build-a-pyramid pattern

In some ways, the idea of the pattern comes much closer to embodying the traditional master/apprentice model for transfer of knowledge and expertise than forms such as plan-driven design methods can hope to achieve. Although design methods also address ways of finding solutions, patterns educate about problems too, and the recognition of these is an important part of learning about design, and one that is not always easy to achieve without some form of guidance. Patterns also provide scope for providing peer-to-peer exchange of design knowledge in manageable ‘chunks’, in a form that matches the observed practices of designers. One of the characteristics of expert designer behaviour that was observed in the pioneering study by Adelson & Soloway (1985) was the employment of ‘labels for plans’, whereby a designer would recognise and ‘label’ a sub-problem that they knew how to address, leaving them free to concentrate on the less familiar aspects of a problem. Implicitly, they intended to reuse a ‘pattern’ that they had employed previously. In the context of design patterns, this labelling also has a role, by providing a useful vocabulary that allows designers to share ideas.

The design pattern concept is not restricted in its use to ‘detailed design’ activities. At the programming level it is generally referred to as an idiom (Coplien 1997) and at an architectural level, the concept of the architectural pattern is well established (Buschmann et al. 1996, Bass et al. 2013). While we will not examine idioms in any detail in this chapter, we will examine the forms and uses of both ‘design’ and ‘architectural’ patterns. Figure 15.1 illustrates their roles.

Figure 15.1

Figure 15.1: Patterns at different levels of abstraction

It is important to appreciate that a pattern is not a reusable piece of software. (That would be termed a framework (Fayad & Schmidt 1997).) Rather, a pattern is a form of conceptual knowledge schema (Détienne 2002), forming a piece of generalised design knowledge that has been extrapolated from experience. In particular, it describes how all or part of a design solution (model) is organised. In practice, patterns are:

  • more concerned with forms of coupling between elements that are based on using composition rather than inheritance;

  • represent a way of sharing the collective experiences of skilled designers (knowledge transfer);

  • are categorised in terms of some abstract (and recurring) design issue, providing a description (pattern) for how this might be addressed.

So, in terms of the way that we address an ISP, how does the use of patterns influence the overall design process? Basically, the use of patterns can reduce the opportunistic nature of the interactions that occur during design. Patterns impose an element of structure upon the design model which might relate to behavioural or constructional constraints, as illustrated in Figure 15.2. (We may also recognise patterns in the problem space which will influence how we approach the design process, but this aspect is omitted from the diagram.)

Figure 15.2

Figure 15.2: How patterns influence ‘solving’ an ISP

To end this introductory section, we look at a simple example from the CCC, not with the aim of providing specific solutions at this point, but rather with the aim of recognising that building an application that manages its processes may well draw upon experience with other applications.

15.2Architectural patterns

Looking at the examples from the CCC at the end of the preceding section, one of the patterns that we could recognise in this is what could be termed the ‘renting a resource’ pattern. While this is concerned with the function of the application (and patterns occur in requirements specifications too of course), it describes a high level pattern that is concerned with the overall behaviour of the application.

When we turn to the role of design in meeting such a need, the most abstract form of pattern that we encounter is the architectural pattern that provides the outline form of the overall system architecture (Buschmann et al. 1996, Bass et al. 2013). In Chapter 6 we briefly examined these from the perspective of architecture, while here we consider their role as patterns. The main concern of such a pattern is to describe the overall organisation of a software application—in particular, how the sub-systems should be structured and what the relationships between these should be. ‘Organisation’ in this context may relate both to static issues of configuration, as well as to run-time behaviour for the interactions between the elements.

As with other patterns, the basic description of an architectural pattern needs to address the following three factors.

  • The context within which some recurring situation occurs. Examples of this might be the need to decouple elements of an application, or to incorporate the ability to dynamically update information sources while a system is operating.

  • The problem, which is related to the issues that the context may present for software development.

  • A solution that addresses the needs of the problem in a suitably general way. This may be described in terms of the type of element making up the application, the forms of interaction between elements, the topology of the elements, and the likely constraints and trade-offs involved.

Architectural patterns have some synergy with ideas about architectural style although they are not necessarily restricted to being realised using a particular style. In principle at least, any architectural style can be employed with a pattern providing that the form of the elements can provide the necessary structures and that their interactions can be organised to fit the pattern.

Perhaps because of the large-scale perspective involved, the set of architectural patterns is much smaller than that of design patterns discussed in the next section. However, as with all patterns (and styles) they do provide a useful vocabulary for the designer who is wishing to exchange ideas or explore options. In the rest of this section we briefly examine three examples of architectural patterns. (A much more detailed and analytical explanation of a useful catalogue of architectural patterns is provided in (Bass et al. 2013).)

15.2.1Model-view-controller (MVC)

The model-view-controller pattern describes a widely-used form of organisation that is employed for many applications that involve some degree of interaction with end-users. Here, the context is one of needing to decouple some form of information store (the ‘model’) from the way that it is shown to different users and viewed on different devices (the ‘views’). The problem is how to achieve this decoupling while also keeping the application responsive to user actions. To achieve this, MVC divides such an application into three types of element.

  • The model incorporates the core functionality and associated data for the application.

  • A view provides information about the model to the user, presented in a particular way. There may well be multiple views, corresponding to the needs of different types of user, or to the need to present information in different ways. It also interprets and communicates user actions to the controller.

  • Controllers handle user input. Each view has an associated controller that responds to any choices made through the use of relevant forms of input (keyboard, mouse, touchscreen,…).

Figure 15.3 shows this schematically (note that there are many forms of visual description for MVC). Note that both views and controllers send requests to the model, but that the model does not send out any requests, and it only provides responses through the view. Requests sent by a controller do not create a direct response, instead they cause the model to be modified, and the corresponding view to be notified that the state of the model has been modified. The application may also directly change the model (with no user interaction) of course, and again, when this occurs the views are notified. The view can then request details of the revised model (or part-model) and display it to the user in response to their input (the mechanism for doing this is provided by the Observer design pattern described in the next section.

Figure 15.3

Figure 15.3: The model-view-controller pattern

The important characteristic of MVC is the decoupling of the internal model from the associated interactions with the user (separation of concerns). We can recognise this pattern as describing many familiar but different, interactive applications. Word processors, spreadsheets, web browsers etc. all fit this model. Decoupling the ‘knowledge store’ from the way that it is presented, as well as from the different forms of interaction, makes it possible to implement the application so that it can be used on different operating systems or with a range of devices.

Figure 15.4 shows a sketch of how MVC might be used with the CCC application. This is really a rather ‘behavioural’ sketch, where the designer is thinking out how the MVC model will handle the situation where a customer makes a booking. The booking results in a modification to the record set for the cars (the chosen car is no longer available to other customers), and it has some consequent effects upon the different views (such as the symbol for that car on the city map changing colour).

Figure 15.4

Figure 15.4: An MVC interpretation of the CCC application

15.2.2Layers

The Layers pattern addresses a context where different elements of the software need to be developed and to evolve separately, requiring that interaction between them should be kept to a minimum. This means that the resulting software is likely to be portable and can readily be modified to meet new needs. Essentially, Layers represents a very stratified approach to addressing the issue of ‘separation of concerns’. When using Layers, the solution adopted is to group those elements that provide a particular service into a ‘layer’, with the layers being organised in a hierarchy, so that the layers providing higher levels of abstraction depend on lower ones, but not vice-versa.

While Layers may not be used as universally as MVC, it does provide the structuring for some very important software applications, with networking and operating systems being key examples of where this pattern is particularly useful. Figure 15.5 illustrates the use of Layers to implement the OSI model used in computer networking.

Figure 15.5

Figure 15.5: A layers example: open systems interconnection model

The OSI model is probably quite an extreme example in terms of the number of layers employed. At a minimum, there need only be two layers. For Layers to be used appropriately, it is necessary that:

  • any dependencies are unidirectional (each layer depends only upon the layer ‘below’);

  • a layer will normally only use the layer immediately below it (exceptions are possible but are better avoided);

  • a layer should embody some particular role or functionality, and ideally making modifications or adaptations to the application should only affect one layer.

Implicitly too, the use of Layers may impose a run-time overhead, since there may be many method (sub-program) calls involved in communicating between the upper layers and the bottom-most layer, which is where the required action is eventually performed.

15.2.3Broker

In everyday life, the role of a broker is to act as a ‘trusted intermediary’ who can provide some form of expertise to help with choosing a service that we need. The idea of ‘insurance broker’ and ‘mortgage broker’ remain relevant, even in an era of internet shopping (and many web sites claim to offer the services of a broker). We turn to a broker so that we can draw upon their expert domain knowledge to determine which provider is likely to be best suited to our needs.

The same can occur in a software context, particularly in the context of service-oriented architectures. In this context we have clients (which themselves may be providing services to others) and servers and these need to be matched so that clients obtain the most appropriate service for any request (Bennett, Layzell, Budgen, Brereton, Macaulay & Munro 2000).

When using a broker architecture for software, the client sends requests to the broker, the broker seeks out and uses the most appropriate service, and then returns the outcomes to the client. This involves a dynamic binding for each request (or set of requests), which is managed by the broker so that the client may well not be aware of which server provided the necessary response. Figure 15.6 shows this schematically. The broker architecture is commonly associated with platforms for distributed service provision such as. NET and Enterprise Java Beans (EJB). This is because the broker pattern is particularly useful in a dynamic environment, where the set of service providers (which may include ‘information services’) and their availability may be subject to regular change, as may well occur with distributed systems.

Figure 15.6

Figure 15.6: A simple broker architecture

Use of a broker does add a run-time overhead in exchange for this decoupling, and incorporating security needs can add further to this (Turner, Brereton & Budgen 2006). The broker itself needs to be secure (and hence trustworthy) and also any requests and responses must be transmitted securely. Equally, a broker does provide a robust run-time environment since the broker is able to make substitutions for a server that has failed, or that may have become available.

15.3Design patterns

Design patterns address smaller and more detailed pieces of an overall design than architectural patterns and have been adopted particularly widely in the object-oriented context. As we noted when looking at plan-driven approaches to design, this form of ‘design method’ tends to become overly-complex when used with objects, and design patterns, rightly or wrongly, have been seen as one way of overcoming the problem by reusing ‘proven’ solutions to specific aspects of an ISP.

Objects offer a useful form of design element that can form one of the components of a pattern. As we noted in Chapter 10, an object:

  • possesses a state, which is encapsulated within the object and can be inspected and changed through its methods;

  • exhibits behaviour through its responses to external events;

  • possesses an identity, since more than one object may be created from a class.

Objects are also capable of using static and dynamic binding (valuable for patterns) and can be coupled to other objects using a number of different mechanisms. One thing that we might note though, as already mentioned, is that design patterns tend to make use of aggregations of objects rather than employing inheritance. Employing inheritance remains a design challenge, much as it did for plan-driven design methods, and opportunities to employ inheritance may still need to be identified through techniques such as refactoring.

For our purposes, the important features of an object are that it provides an abstraction of some aspect of the design model, that it incorporates encapsulation of state information, and that it provides externally accessible methods.

While design patterns provide some ‘part-solutions’, a really major reason for using these, and one that is quite different from the purpose of architectural patterns, is that they are intended to be used to address those aspects of a design that are most likely to undergo change. Much of the rationale behind different patterns is to isolate such elements in the design model with the goal of reducing the impact of future evolution. This is something that we will particularly examine when we look at some examples of patterns. It also means that patterns often seek to employ loose coupling to provide the necessary flexibility.

A text that helped popularise the idea of the design pattern is the pioneering book from the ‘Gang of Four’ (often abbreviated to GoF in the patterns literature): Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides (1995). Their book provides a catalogue of 23 design patterns and helped establish ideas about pattern classification and description. Valuable though this is, it is not really a tutorial about patterns, and a text such as Head First Design Patterns by Bates, Sierra, Freeman & Robson (2009) provides a much easier (if sometimes rather eccentric) way of learning about the design pattern concept and about some major patterns.

Design patterns have attracted a strong and involved user community, and a major resource providing various forms of knowledge about design patterns is provided by the web site at www.hillside.net. This on-line resource provides details of many patterns, as well as links to other books and tutorials.

The adoption of a pattern within the design of an application does represent an important long-term decision (which of course, implies an element of technical debt). Where an application evolves through time, one of the risks is that the changes to the objects mean that a pattern may decay, or that its presence might even impede unforeseen forms of change. There isn't much that can be done about the latter (or they wouldn't be unforeseen), but the issue of decay is one that designers need to consider when making changes.

Two useful concepts related to pattern decay are those of grime and rot, that are used to describe the corruption of a pattern that can occur when changes are made to the objects involved in the pattern (Izurieta & Bieman 2007, Feitosa, Avgeriou, Ampatzoglou & Nakagawa 2017). Pattern grime is the “degradation of design pattern instance due to buildup of unrelated artifacts in pattern instances” and in essence involves material being added to the classes and objects of a pattern that are not directly related to the purpose of the pattern. Pattern rot, which is less common, is the “deterioration of the structural or functional integrity” of the elements making up a pattern. The presence of grime and rot may mean that a pattern cannot support changes to the application in the way that was originally envisaged when the decision was made to adopt that pattern.

Patterns can take a variety of forms, and the GoF classified patterns along two ‘axes’, as illustrated in Figure 15.7, which were as follows.

Figure 15.7

Figure 15.7: Pattern classification scheme used by the ‘Gang of Four’

  1. The purpose of the pattern. Its purpose describes what a pattern is used for, and is usually described as being one of the following three types.

    • creational patterns are concerned with occasions where objects need to be created in order to achieve the purpose of the pattern;

    • structural patterns address the way in which the constituent classes or objects are composed;

    • behavioural patterns describe the ways that classes or objects interact, and how responsibilities for different aspects of the pattern's purpose are allocated between them.

  2. The scope of the pattern. This describes whether the pattern is primarily one that addresses the use of classes or the use of objects (where we can regard a class as being a form of ‘template’ from which one or more instantiating objects are realised). Most patterns deal with objects, and so we concentrate on looking at examples of these.

In terms of the viewpoints model, creational and structural patterns are essentially ones for which the organisation of the pattern is associated with the constructional viewpoint. And obviously, the organisation of behavioural patterns is associated with the behavioural viewpoint.

While we use the framework shown in Figure 15.7 here, it is worth noting that others have employed other ways of categorising patterns. Although in Buschmann et al. (1996) the main focus is on architectural patterns, there is also some discussion of a number of design patterns. They categorise these using a set of role-based headings (structural decomposition, organisation of work, access control, management and communication).

The remaining question to address, before looking at some examples of patterns, is how to describe a pattern. The books by Gamma et al. (1995) and Buschmann et al. (1996) use slightly different templates for this. The following template is something of a merging of these (and others) to try and capture all of the issues that need to be documented, and one that we use in the following subsections.

  • Name. This is used to identify a pattern and (ideally) to indicate the essence of what it does.

  • Also known as. Rather pragmatically most catalogues of patterns do recognise that they may be categorised using different identifiers in other contexts.

  • Problem. This is the design ‘problem’ that the pattern is intended to address.

  • Solution. This outlines the way in which the pattern addresses the problem, and explains any design principles that underpin the solution.

  • Example. Provides a real-world example of the problem and of the pattern-based solution.

  • Applicability. Describes the situation in which the problem might arise and where it may be appropriate to employ the pattern, possibly including hints for recognising such situations.

  • Structure. Provides a detailed description of the organisation of the solution usually employing diagrammatic forms to model both the behavioural and the constructional viewpoints.

  • Implementation. A set of guidelines for implementing the patterns, noting any possible pitfalls that might arise.

  • Known uses. A set of examples from existing systems.

  • Related Patterns. (Also known as See Also.) Patterns that address similar problems or that complement this one in some way.

  • Consequences. Design trade-offs that might need to be made when employing the pattern, as well as any constraints its use might impose and possible forms of technical debt that it might incur.

The choice of patterns described in the following subsections is based upon those GoF patterns that were identified as being most useful in the survey of pattern experts described by Zhang & Budgen (2013). (This survey is discussed more fully in the section on empirical evidence.) And as a final comment, when discussing GoF patterns it is common practice to identify the page number where the description of the pattern begins, and this practice has been followed here.

15.3.1Proxy (207)

The role of a proxy can be undertaken in various ways as part of everyday life, when someone acts ‘on behalf of’ another person for some purpose. A common example is in voting, where someone unable to go and take part in a ballot will appoint another person to act as their proxy and cast their vote for them. This pattern employs the same concept, and involves one object acting on behalf of another object.

The GoF classify this pattern as being Object Structural whereas Buschmann et al. describe it as being Access Control. Using the template outlined above this is described as follows.

  • Name. Proxy

  • Also known as. Surrogate

  • Problem. Proxy addresses the problem where direct provision of access to an actual object requires to be moderated in some way. This may be because access represents a significant overhead of some form (time, space) or because different types of client may require or be entitled to different levels of access.

  • Solution. This is to provide a representative of the object and let the client object communicate with this proxy rather than with the actual object. The proxy provides the same interface as the actual object and ensures correct access to it, while possibly performing additional tasks such as enforcing access protection rules.

  • Example. The GoF provides the example of an object representing a complex image when used in a word processor. Rather than incurring the overhead of loading a (potentially) large graphical object it may be possible to employ a much simpler proxy that ensures that key properties (such as position and boundaries) are correctly represented in the screen image. With more widespread access to electronic data sources, a more common example today is probably the protection proxy form, in which the proxy is used to control the level of access that users may be permitted to have to an object or to information. For example, in the case of access to Electronic Health Records (EHR), a doctor is likely to have fuller access than (say) a nurse, and an administrator might have quite limited access that is restricted solely to demographic information.

  • Applicability. Proxy can also be employed to control access to relatively complex objects, which may be in a different space (remote proxy); with minimising overheads, as in the case of the word processor and image (virtual proxy); and controlling different access rights (protection proxy).

  • Structure. Figure 15.8 illustrates the idea of proxy using a class diagram. Normally, requests from the client will be serviced by the methods of the proxy, relaying these on to the original object only when necessary. Figure 15.9 uses a sequence diagram to illustrate the operation of virtual proxy (or alternatively this could be viewed as an example of protection proxy, with the first request being refused for lack of access permission).

  • Implementation. Since this is not relevant to the theme of the book, we omit this element.

  • Known uses. Both the GoF and Buschmann et al. provide examples, with the latter being more extensive and varied and also relating more strongly to internet-based forms of implementation, where this pattern is quite widely employed.

  • Related patterns. The Adapter (139) and Decorator (175) patterns are also concerned with issues related to interfaces.

  • Consequences. Obviously, the use of proxy creates an overhead by adding a level of indirection when accessing potentially quite important objects.

Figure 15.8

Figure 15.8: Class diagram for the proxy design pattern

Figure 15.9

Figure 15.9: Sequence diagram for the proxy design pattern

As an initial example of a design pattern, proxy illustrates a number of the points made above about patterns in general. It is compositional in form, addressing what is very much a sub-problem for any design, relatively simple in its structure, and provides quite loose coupling that may help with future changes. It is also a pattern where ideas from ‘conventional’ coding and the internet in its various forms coincide, showing how the concept behind a pattern can apply across quite a wide range of roles and forms.

15.3.2Observer (293)

Observer is a well-known and widely-used pattern and is categorised as being object behavioural by the GoF. It embodies the concept of a publish-subscribe relationship between the design elements. The pattern as described by the GoF is fairly basic, omitting various ‘housekeeping’ issues, and the description provided here likewise concentrates on the core model, while indicating where additional aspects need to be considered for implementation.

  • Name. Observer

  • Also known as. Dependents, Publish-Subscribe

  • Problem. A common side-effect of organising a software system as a set of co-operating objects is the need to maintain consistency between related objects, while avoiding overly-tight coupling that may constrain performance as well as reduce the scope for reuse.

  • Solution. This involves creating a ‘publish-subscribe’ relationship between the subject and the observers. Whenever the subject undergoes a change of state it notifies the observers that this has occurred, and they can then query the subject in order to synchronise with it and obtain any updated values.

  • Example. A simple example is that of a spreadsheet. When the data in a cell or group of cells is changed, any graphs, pie-charts or the like that are using that data will also need to be redrawn. Here the spreadsheet cell is the subject, and the objects that are responsible for drawing the charts and graphs are the observers.

  • Applicability. This form of relationship occurs widely wherever objects are working together to perform some task, and where an object needs to be able to notify other objects about changes without any knowledge about those objects.

  • Structure. Figure 15.10 illustrates the idea of observer using a class diagram. An interface is used to implement the subject-observer relationship and this is then implemented by the concrete subject and observers. Observers register with the subject to receive calls to update() from the subject. On receiving an update() message it is then the task of the observer to use some form of getState() to check if the change affects it (since observers may be interested in different aspects of the state, they may use different versions of getState()). Figure 15.11 uses a sequence diagram to illustrate the operation of observer.

  • Implementation. Again, this element is omitted here.

  • Known uses. Observer is a very widely used pattern. The example above of the spreadsheet is just one of many. Another well-known one is within the MVC architectural pattern, where the model acts as a subject, and the views act as observers, being notified when changes are made to the model.

  • Related patterns. Publish-Subscribe, Mediator.

  • Consequences. While Observer provides loose coupling that makes it possible to vary subjects and observers independently, and to add observers without needing to modify the subject or other observers, there are some ‘housekeeping’ issues that need to be considered. These include:

    • a subject does need to keep track of the observers;

    • if an observer is observing more than one subject then it is necessary to extend the update() method so that the observer is able to know which subject has provided the notification;

    • if a subject is deleted then it is necessary to notify all of its observers to avoid dangling references.

Figure 15.10

Figure 15.10: Class diagram for the observer design pattern

Figure 15.11

Figure 15.11: Sequence diagram for the observer design pattern

The model shown in Figure 15.10 describes the very basic operation (largely ignoring the housekeeping issues above). Even so, this is a relatively simple as well as effective pattern (it was the pattern that was most highly valued in the survey by Zhang & Budgen (2013)).

15.3.3Abstract Factory(87)

The Abstract Factory provides an example of an object creational pattern that addresses a problem that probably is a bit more limited in scope than those of the previous two examples, although the problem itself is an important one. The aim of this pattern is to make a ‘client’ class independent of the specific set of objects that may need to be created for it to perform its task (decoupling it from them), where there is a choice of classes that can be instantiated. The decision about which class to instantiate is deferred to runtime, when a specific concrete factory class is used to create the objects.

  • Name. Abstract Factory

  • Also known as. Kit

  • Problem. This pattern addresses the need to achieve portability of an application across environments. A common role is to enable an application (the ‘client’) to be used on a range of platforms that provide different forms of ‘look and feel’ user interaction.

  • Solution. The abstract factory defines an interface for creating each basic form of widget used in interaction (such as scroll bars, radio buttons etc.). Clients perform operations to obtain a new instance of a widget, but are not aware which concrete class is being used.

  • Example. The widgets will usually form a user interaction toolkit that supports multiple standards for such widgets as scroll bars, buttons etc.

  • Applicability. The roles for this pattern address situations where either:

    • a system needs to be independent of the way in which its products are created, composed and represented; or

    • a system may be configured locally, using just one form from a family of products.

  • Structure. Figure 15.12 illustrates the idea of abstract factory using a class diagram. The client uses the abstract factory when it is compiled and then at run-time it is linked to an actual (concrete) factory. So if ConcreteFactory1 is selected, that class will provide ProductA and ProductB for use by the client. (Of course, the interfaces that these must conform to are specified by the abstract factory.)

  • Implementation. Again, we omit this element.

  • Known uses. Abstract factory isolates clients from the responsibility and process of creating product objects, the client manipulates the instances through their abstract interfaces and hence remains unaware of the particular implementation.

  • Related patterns. Prototype

  • Consequences. While the abstract factory can be easily extended to include new members of a product family (for example, a new form of user interface), adding new products to the set provided by the factory is not easy, because the pattern defines a fixed set of products. So adding (say) a variant form of radio buttons would require modifying the abstract factory and recreating all of the concrete factories. In that sense, using abstract factory does create a form of technical debt through the constraints this creates.

Figure 15.12

Figure 15.12: Class diagram for the abstract factory design pattern

Abstract factory meets a quite specific need and is generally considered to do it well. However, apart from the example of different user interfaces it is not as likely to be so widely used as the previous two examples.

15.4Other uses of patterns

This section addresses two rather different pattern-related issues. The first is to briefly examine an example of how design patterns can be used with another architectural style (SOA), while the second involves looking at how patterns can be employed to describe experiences of what doesn't work.

15.4.1Software service patterns

Software service concepts were described in Chapter 11. While components (which can be considered as the precursors of services) do not seem to have attracted much interest from the patterns community, software services have presented a topic of greater interest, and indeed, patterns may well be a particularly effective way of adapting the service concept to user needs.

At the architectural pattern level, Bass et al. (2013) consider that the service-oriented architectural pattern describes the use by service consumers of distributed services offered by service providers. Essentially this pattern describes the use of services in the form described in Chapter 11. It identifies the need for a service to publish details of what is provided, and also to address quality issues through the use of service-level agreements (SLAs), which reflects the strong business element often associated with the use of the service model.

Topologically, this architectural pattern can be expected to possess a ‘star’ configuration. A simple example is provided in Figure 15.13. In this example the external service providers are essentially ‘fixed’ and some form of ‘service broker’ or ‘orchestration server’ could well be included to allow greater flexibility of the choice of providers and configuration.

Figure 15.13

Figure 15.13: A very simple SOA pattern

At the more detailed level (akin to the GoF patterns), a number of books have documented a wide range of patterns. A major source is the text by Erl (2009). While some patterns are derived from the object-oriented pattern set, most appear to be unique to the service context. Service patterns are complicated by a number of factors, including the existence of different service frameworks, the dynamic (self-adapting) nature of many service applications, and the lack of standard notations.

15.4.2Design anti-patterns and code smells

Ideas about software reuse, which is a key motivation for employing design patterns, has understandably focused upon finding ways to reuse good experiences. But of course, a designer's knowledge schema may well include experiences that were unsuccessful too, and these also need to be remembered so that they can be avoided in the future. So, not surprisingly, although less well-codified than the concept of the design pattern, this has led to the concept of the design anti-pattern.

In a very readable, if rather tongue-in-cheek, paper, John Long (2001) provides some examples of “obvious, but wrong, solutions to recurring problems”, putting particular emphasis upon the processes involved in choosing to use them. Indeed, the anti-patterns literature does tend to put more emphasis upon the reasons why wrong solutions are adopted, rather than on the specific form of the wrong solutions themselves (Brown, Malveau, McCormick & Mowbray 1998).

This emphasis is quite understandable, since technical solutions are rarely completely wrong in themselves, although a particular solution may well be the wrong one to adopt in a particular context. So, while the literature on design patterns emphasises solution structures, although recognising the influence of context and motivation, the anti-patterns literature focuses mainly on motivation.

For our purposes, the main message here is that reuse of a design model (or in the case of patterns, of part of a design model) is not automatically “a good thing”. What has proved successful in one situation may not always work in another.

However, while anti-patterns are in some ways something of an evolutionary dead end, they can be considered as a form of antecedent for the much more widely adopted idea of code smells. The term ‘code smell’ is generally attributed to Kent Beck, and in the absence of a formal definition (the concept does have a subjective element arising from context), the description provided in Fowler (1999) is quite widely used. In this, a code smell is considered as being a structural weakness that indicates a deeper flaw, and that needs to be removed from the source code through the use of refactoring in order to improve the maintainability of the software. (While the term does provide an unquestionably evocative description of the sort of issue involved, it is probably better not used when explaining refactoring plans to project managers, customers etc.)

As the term indicates, code smells are likely to be recognised during implementation, although some may well be detectable in advance of that. Here we briefly look at some examples.

  • God Class. A God Class “refers to those classes that tend to centralise the intelligence of the system. An instance of a god class performs most of the work, delegating only minor details to a set of trivial classes and using the data from other classes” (Lanza & Marinescu 2006). A God Class may well make frequent access to the data of foreign classes and its role violates the object-oriented design principle that each class should only have one responsibility. God Classes also tend to be very large, affecting ease of comprehension. Because of its role, it is anticipated that such a class will be changed frequently during maintenance, and hence more likely to include errors.

    However, the study by Olbrich, Cruzes & Sjøberg (2010), looking at the evolution of three OSS applications does demonstrate that it may well be that the use of a God Class is a quite reasonable way to organise some forms of application, provided that its size is not extreme. This emphasises the point made above that the identification of relevant code smells may well be subjective and contextual.

  • Data Class. Such a class has data fields, together with methods for changing and inspecting these, but plays no role in the operational purposes of the application. Its lack of a behavioural role weakens the object-oriented structures and at the worst it may simply be a mechanism for including global data. As observed in (Fowler 1999) this may be an acceptable form for a class to have during initial development, but at some point it should take on the role of a ‘grownup’.

  • Code Duplication. Duplication of code can arise in more or less any architectural style (for example, it can easily arise in the lower levels of a call-and-return structure). Duplication can lead to problems with maintenance, particularly where only one instance is changed, creating inconsistencies. Fowler (1999) suggests some techniques to employ for refactoring when this is detected.

Looking at these examples, although they may (partly at least) arise from design decisions, their characteristics relate strongly to code. Hence it may be more difficult to identify these in a design model (the God Class perhaps excepted).

Where code smells do have a useful role is when identifying the need for possible refactoring. As discussed in Section 14.7, refactoring involves reorganisation of the code from a constructional perspective, while retaining its function and behaviour, in order to help with the future evolution of an application. Code smells can help with prioritising the need for such change in the design. They may also provide a motivation for refactoring that is related to the design model itself, although they still have a strong link to the actual code structures.

15.5Designing with patterns

Having reviewed various examples of the pattern concept, the next obvious question to ask is “how do we use design patterns to solve design problems?”. In this section we discuss some issues related to pattern use.

Many of the books that describe design patterns, such as those by Gamma et al. (1995) and Buschmann et al. (1996) are very much structured as catalogues of patterns. And later texts such as Bates et al. (2009) focus largely on the issues of how to implement patterns. Important as both of these are, particularly for documenting patterns, they still leave our initial question unanswered. Unfortunately, rather as a gardening catalogue, full of glorious colour pictures of healthy, thriving plants, provides little real aid with the task of planning a new garden—although it may tell us which plants like shade and how tall they will grow—so it is apt to be with design patterns. Possession of a catalogue provides a source of ideas; it provides information that can help with planning and anticipating possible consequences, but the task of working out how to use its contents is still a creative activity.

(Actually, the analogy with planning a garden is quite a good one, at least, if you like gardens, since gardens do evolve and exhibit behaviour, even if over much longer periods of time than we expect with software. Trees grow and shade different parts of the garden, some plants take time to become established but then take over adjacent sections unless controlled. So, like the software designer, the gardener's planning requires an ability to envisage some future state, while having inadequate control over both the conditions and the quality of materials that they need to use to achieve that state.)

How then do we use catalogues of design patterns, whether in books or on-line? Well, the GoF advice is very much along the lines that patterns need to be learned, and that by studying patterns the designer will acquire both insight into how and why the pattern works as well as enough familiarity with it to be able to recognise those situations where it can be used to effect. Designers are also advised to follow the following two principles.

  • Program to an interface, not an implementation. What this means is that a client object should not be aware of the identity of the actual object that is used to service a particular request, so long as its form and behaviour conform to the interface specification.

  • Favour object composition over class inheritance. This is not to deny the value of inheritance as a mechanism for reuse, but rather that it should not be over-used.

This implies that the minimum conditions for the successful use of patterns require that a designer should:

  • acquire a ‘vocabulary’ of design patterns;

  • be able to recognise where a particular pattern could provide a useful solution;

  • have an understanding of how to realise the pattern within that context.

Given that there are now hundreds of documented patterns (the book by Gamma et al. (1995) documented only 23 of them), these are quite challenging requirements, especially given that the question of which patterns are the ones that are most valuable to learn (first) is not easily answered.

The strategy advocated by Buschmann et al. (1996) is rather different, although the basic conditions for when it is appropriate to employ a pattern are the same. They advise classifying a given problem in the same way that the patterns themselves are classified, as a step towards being able to identify potentially useful patterns. Their basic process is as follows.

  1. Specify the problem, and if possible, any sub-problems involved.

  2. Determine the category of pattern that is appropriate to the design activity being performed (that is, architectural or design patterns).

  3. Determine the problem category appropriate to the problem.

  4. Compare the problem description with the available set of patterns in the catalogue that fit that problem description.

  5. Consider the benefits and liabilities (assess the design trade-offs and possible technical debt that will be incurred by using the pattern). Of course, this may also need to include the possibility that there is no existing tried and tested pattern for the given problem.

  6. Select the pattern variant that best fits the problem and offers most benefits.

Since their strategy includes architectural styles as well as design patterns and idioms, the above process can be considered as fairly comprehensive, and in principle at least, avoids the need to learn a growing catalogue of patterns. We might also note that it has a distinctively top-down decompositional aspect, which is perhaps unexpected, given that patterns are essentially a compositional concept. Perhaps the main practical limitation is that pattern documentation has tended to follow the structure adopted by the GoF.

If we go back to the model of pattern use represented by Figure 15.2, an obvious question is how to recognise the opportunity to use a pattern. We should not assume that there is always going to be a suitable pattern when using a process such as the one above. Indeed, trying to force all problems into a ‘pattern framework’ is likely to produce exactly the opposite of what it intended. So step 5 in the above process is an important one, and the designer may well need to be able to recognise when there is no ‘ready-made’ pattern that fits a given problem. A key thing here is recognising that a particular design problem is likely to have been encountered in enough situations to be likely to have resulted in someone creating a pattern for it. Again, this is where the more experienced designer is likely to have an advantage.

The process recommended by the GoF carries within it a self-limiting aspect that is clearly difficult to overcome. This arises from the way that, as more patterns are identified and added to the corpus of pattern knowledge, so the ability of the individual designer to learn all of these becomes an ever-increasing challenge. What is needed is some agreed way of indexing patterns, or at least agreement about what constitutes a core set of patterns that everyone might be expected to learn. Neither of these has really emerged, and in their absence, the process recommended by Buschmann et al. would seem to cope better with the nature of ISPs.

15.6Empirical knowledge about designing with patterns

While the design patterns community has contributed some very valuable insight into how we can reuse design ideas, this has not always been quite as balanced as might be desired. Enthusiasts continue to write patterns and encourage others to do the same; there are conferences on patterns (usually labelled as xxxPLoP, where PLoP stands for ‘Pattern Languages of Programs’); and a journal (TPLoP of course). What has been less evident is any enthusiasm for winnowing out the less useful patterns and evaluating the concept in general. Patterns are unquestionably a useful concept, but as with all concepts employed for design, it is important to obtain an understanding of their limitations too.

This section almost entirely describes studies related to object-oriented design patterns. There is very little empirical research into architectural patterns (perhaps not entirely surprising) or into the use of design patterns with other architectural styles.

Zhang & Budgen (2012) conducted a systematic review into what was known about design patterns. They found a small number of empirical studies (mainly small-scale experiments) and a number of ‘experience papers’, with that by Wendorff (2001) being particularly insightful. All of the patterns studied were either directly taken from the GoF or were close derivatives. They concluded that: “we could not identify firm support for any of the claims made for patterns in general, although there was some support for the usefulness of patterns in providing a framework for maintenance, and some qualitative indication that they do not help novices learn about design”. However, the strength of evidence for the overall findings from the study was limited by the small number of primary studies and the way that these used a spread of different patterns, with Composite, Observer and Visitor being the only patterns that had been addressed by more than two or three primary studies.

This study was followed up with two surveys. The first was a quantitative survey of the usefulness of the 23 GoF patterns, using the authors of pattern papers as the sampling frame (Zhang & Budgen 2013), and then was augmented by a more qualitative survey (using the same sampling frame), seeking explanations for some of the results (Zhang, Budgen & Drummond 2012). As with almost any survey undertaken in software engineering, it is extremely difficult to determine to what degree the sampling frame used could be considered as being representative of pattern users as a whole.

From the first survey, the most highly rated patterns were Observer, Composite and Abstract Factory, and there was also a substantial group that were not considered to be useful (most notably, Memento). There was also considerable ambivalence about Singleton, which was investigated further in the second survey. The Singleton pattern is a creational pattern that “ensures a class only has one instance”, with a single global access point. (A good example of a situation where the use of this might be appropriate is for a spooler class responsible for managing output to a printer.) The qualitative survey revealed that a significant number of users were concerned that Singleton was easily mis-used to provide global variables, and that its use also increased coupling. So, while it could be useful for limited roles, the potential disadvantages in terms of introducing undesirable features into a design model were such that they felt it better to avoid its use1.

While there have been quite comprehensive studies of research trends related to the use of design patterns, such as Mayvan, Rasoolzadegan & Yazdi (2017), (which found that pattern development, pattern mining, and pattern usage were the most active research topics in the field of design patterns), there appear to have been relatively few studies related to any form of evaluation of the patterns themselves. Given how much patterns are valued by quite a wide community (and lack of research does not imply lack of value), this lack of critical analysis is disappointing.

This situation is even more marked for the use of software service patterns. Although there is no lack of tutorial material, there seem to have been few studies that assessed the usefulness of this class of patterns.

If we turn briefly to the anti-pattern context, then we find that, despite the issues of context and difficulty of definition, there have been many studies on the use of code smells. An example of such a study is that by Palomba, Bavota, Penta, Fasano, Oliveto & Lucia (2018), and it is worth noting that this, like many others in this area, largely draws upon data from open source projects. However, these studies do not appear to offer any very conclusive views on the usefulness of code smells, and rarely provide any systematic form of evaluation. Some of the cause of this may be the influence of context as a confounding factor, as noted by Olbrich et al. (2010).

Key take-home points about designing with patterns

Design patterns provide a useful and valued means of codifying and exchanging information about design structures (large and small) that have been found to be effective by software designers.

  • A pattern describes both a ‘design problem’ and a generic solution to that problem that can be reused over and over again in many different ways. Labelling of patterns aids with knowledge transfer and reuse.

  • Patterns are more concerned with coupling through composition than with inheritance. They generally aim to provide loose coupling between design elements so as to aid the ‘evolution’ of a design model.

  • Architectural patterns describe ways of structuring applications to fit particular characteristics of their role.

  • Design patterns provide ‘part-solutions’ for elements of a design model, simplifying the process of ‘solving’ an ISP by providing a way of organising specific aspects of the design model.

  • Patterns are used for different aspects of the design model. Creational and structural patterns are organised around the constructional viewpoint, while behavioural patterns structure the design within the behavioural viewpoint.

  • Patterns can be used with any architectural style (in principle) but have largely evolved for the object-oriented architectural style where there is a large community of pattern enthusiasts.

  • The design process when making use of design patterns is an informal one with only quite limited guidance about the recognition and use of patterns being available to help the designer.

  • Empirical knowledge about the usefulness of design patterns and architectural patterns is limited. However, it is clear that some patterns (most notably Observer, Composite and Abstract Factory) are valued by experienced designers. There is some evidence that the use of patterns is unlikely to provide help for less experienced designers.