Chapter 12. Designing Patterns

The patterns we design should already have been vetted and deemed worthy of investment. We need to come up with solid designs to make sure that these patterns deliver on their potential and meet the expectations of the organization. A poor design will flow into later stages, reducing the quality of the pattern and negatively impacting the ROI of the pattern and the overall PBE effort. We can have all the best intentions and a great infrastructure in place to support the use of patterns, but without properly designed patterns, the PBE effort will be unsuccessful.

The traditional skills used in designing software solutions are still relevant and help us in our pattern design efforts. For instance, when designing patterns, we need to ensure that we accurately interpret the underlying best practice. In cases where we are designing a pattern implementation, we are also concerned with usability, reliability, and performance. These aspects of pattern design can easily be mapped to those that we focus on for the design of traditional software solutions. However, there are unique details and areas specific to designing patterns. The patterns and guidelines in this chapter address these unique aspects of pattern design.

Patterns

In the following discussion we look at patterns to help us design patterns, with special consideration of the use of encapsulation and composition to create compound patterns. We also look at how we can leverage exemplars and a meet-in-the-middle approach in our design efforts.

Compound Pattern1

Context

A collection of patterns is available within the organization. The patterns vary in scope and purpose. Some of the patterns apply to related problems; others support different problem sets.

Problem

In situations where we have a number of patterns that work together to solve a related set of problems, how do we simplify the user experience and increase the value and power of the patterns? How do we ensure that related patterns are used together consistently and correctly?

Forces

• Patterns are used in combination without the flexibility of using them individually.

• We need to account for update cycles for all the individual elements that make up the composite solution.

• In supporting the compound pattern, it can be more difficult to determine the source of defects.

Solution

We create a pattern that is a composite of other patterns. We combine patterns into a larger-grained unit of reuse that can easily be consumed by the Pattern User. As a result, the Pattern User needs to know only how to apply the compound pattern rather than the set of patterns encapsulated within. Compound Pattern supports and builds upon two PBE Core Values:

  1. Patterns are best used in combination, rather than in isolation.
  2. Focus on making patterns consumable.

Not only do we want to use patterns in combination, but we also want to simplify the experience in terms of both how the pattern is used and how it is produced.

Looking at the situation from a Pattern User point of view, it is easier to understand a black box that encapsulates a number of patterns than to learn the details of each of the constituent patterns. We are able to focus only on the details that are publicly exposed by the compound. Private details that are encapsulated by the compound reduce the amount of comprehension that is needed.

From a pattern production point of view, rather than looking at producing a large-scope pattern as a large single entity, increasing risk and complexity, we can put together such a pattern by using pattern building blocks, each of which is a pattern. This approach allows us to easily test and work with the smaller components and work our way up to a larger solution. This maps quite directly to the idea of component-based development, with the qualification that our components are patterns.

Original references to this concept labeled compound patterns as composites. However, this led to confusion between this concept and the already existing Composite2 pattern. To avoid confusion, the terminology was adjusted to use the term compound. With that in mind, we can refer to the following for additional guidance on compound patterns:

In a composite pattern, the constituting patterns integrate with each other to achieve a synergy that gives the composite pattern its own identity beyond being just the sum of some patterns. This distinguishes a composite pattern from an arbitrary pattern composition, which may be a suitable solution for a specific design problem, but which does not recur as a pattern of its own.3

Example

The Subsystem Façade pattern described in the case study is a compound pattern that brings together a number of patterns, including the Session Façade, Data Access Object, Role, Money, Embedded Value, and Class Table Inheritance patterns, as shown in Figure 12.1.

Figure 12.1. Overview of the patterns encapsulated within the Subsystem Façade pattern

image

Related Patterns and Guidelines

Limited Points of Variability*, Pattern Implementation Extensibility*

Exemplar Analysis

Context

We have adopted PBE and want to create our own patterns and make them available to others. We also have one or more exemplars.

Problem

How do we go from an exemplar to a pattern?

Forces

• A quality exemplar is imperative to success. We need to invest in high-quality exemplars before performing any analysis or pattern creation. Performing an analysis on a low-quality exemplar is a wasted effort.

• If issues are found with the exemplar, the exemplar (and possibly the associated “best practice”) needs to be corrected before proceeding with the analysis.

• Transitioning an exemplar to a pattern requires the involvement of multiple roles, including a Pattern Author/SME, Pattern Implementation Author, and Pattern Specification Author.

Solution

To design a pattern we need to analyze the associated exemplar. In exemplar analysis we identify the key entities involved in the pattern, specify the points of variability, define the information that the Pattern User provides, recognize the rules we can use to derive any additional information needed by the pattern, and identify the aspects of the pattern that stay the same across applications of the pattern. We work to ensure that we create a pattern that enables us to apply best practices, while supporting many users so that they can create a customized solution in their context.

As we perform exemplar analysis, we use some of the same tools and techniques that we would use in designing any software solution. We have many options available: pen and paper, whiteboards, and modeling tools. The key is that we want to think through the analysis of the exemplar and the resulting design and ensure that we can communicate that design to those creating the pattern.

Let’s take a more detailed look at the tasks and considerations that come into play within exemplar analysis:

Type of pattern. We need to determine the type of pattern that we are going to create. Looking at the exemplar, we can ascertain when and where the pattern applies. Does it apply within a model? Does it result in a model? Or does it result in the creation of a set of text-based artifacts? Is this a pattern that we should automate, or will it only be a specification?

Domain of the pattern. We need to recognize the domain to which the pattern will apply. To design the pattern properly, we need to ensure that the team working on the design has an understanding of the pattern’s domain. Without this understanding it is impossible to truly understand the nuances of the pattern, and we are likely to produce an ineffective solution.

Design roles involved in exemplar analysis. When designing the pattern, a number of roles will be involved: a Pattern Author/SME, a Pattern Specification Author, and a Pattern Implementation Author. It may turn out that one person fills all three of these roles; however, usually multiple people are involved.

Scope of the exemplar. As part of the analysis we need to gain an understanding of the scope of the exemplar. For instance, if we are looking at developing a pattern implementation and using this implementation to create a set of text-based artifacts, we need to understand the set of artifacts. How many files are in the set? What is in each of the files within the set? We can also look at whether the scope of the exemplar is too broad or too narrow. What is the essence of the exemplar? What additional elements have been or could be added? What is the value of having these additional elements? Does it make sense to enhance and broaden the scope at this time, or should that be postponed until a later version of the pattern?

Points of variability. A point of variability is an aspect of the solution that is provided to the user to allow for changes in how the solution is applied. A point of variability enables users of the asset to provide information specific to their situation and have the result of using the asset reflect that input. This is a key aspect of working with a pattern: each time we use the pattern we are able to provide information specific to our content and have the output of the pattern be shaped and influenced by that input. As we identify and think through the points of variability associated with the pattern, we will also see that many parts of the solution will remain static across applications of the pattern. We can think of the pattern as providing us with a solution that brings together user input and static aspects of the pattern to produce a solution that is unique to the situation, while doing so according to best practices.

Roles, relationships, and multiplicity. We’ve already determined the scope of the exemplar; now we need to give thought to the purpose of each the elements within it. Why is each element in the exemplar? Are there relationships between the elements? Is there a hierarchy among the elements in the solution? In recognizing the relationships, are there cases where multiplicity comes into play?

Artifact significance. We need to consider the significance of each element in the exemplar. We need to train ourselves to look at the details and start to recognize higher-order elements and relationships. For instance, if we treat each line of code as a separate and unique aspect of a file, we will get lost in irrelevant details, miss higher-order relationships, and essentially won’t be able to see the forest for the trees. In addition, some of the elements within will be static and other parts will be dynamic. When we look at content that is meant to be dynamic, there will be cases where input is required from the Pattern User. In other cases calculations can be made based on information provided by the Pattern User for some other requirement.

Input model. Once we understand the scope of the exemplar, the points of variability, and the roles, relationships, and multiplicity, we need to give consideration to the information that we require the user of the pattern to provide. We call this information the input model. The input model leverages the roles, relationships, and multiplicity. In the case of a pattern specification, things are relatively simple and straightforward. We look to determine the roles in the pattern that need to be provided by the Pattern User. Things get more involved as we create and support pattern implementations.

Related patterns. As we design the pattern, we also need to keep in mind that it may be related to other patterns. In some cases we may want to encapsulate the pattern in a compound pattern. In other cases we may find that it makes more sense to keep the pattern as an independent unit but ensure that it can easily be used with related patterns. We also need to understand situations where two or more patterns are mutually exclusive.

An exemplar is required input for designing a pattern. Patterns capture real-world, proven solutions. We do not want to get into situations where we are inventing the aspects of the pattern. We will run into issues with our patterns if they are not based on exemplars, including these:

Difficult to complete the pattern. If we are not working toward a target, how do we know when we are done?

End user issues in applying the pattern. If the solution is not based on a real-world, proven solution, it’s likely that the pattern will have issues when it is applied. Such issues will negatively impact the motivation of the participants in a reuse program and lead the users to question the value of patterns.

Reduced pattern testability. The exemplar becomes the first (and primary) test case for the pattern. We should be able to take a set of inputs that map to the exemplar and re-create the exemplar.

Decreased pattern adoption. Without an exemplar it is difficult to convince others to use the pattern because there will be uncertainty regarding its origins.

Example

The Oslec team uses Exemplar Analysis as they design the Subsystem Façade pattern in Chapter 5. In this effort they identify an exemplar—a best-practice representation of the solution that should exist for subsystem façades within Oslec. They confirm with their SME that the exemplar is truly representative of best practices, is of sufficient scope, and contains sufficient variability. They then proceed to analyze the elements within the exemplar. In this analysis they determine the roles that each file plays and then dig further into the solution to identify the areas of variability within the files. In performing this analysis, they detail the elements, their roles, and how the variability points will map to information provided by the user of the pattern.

Related Patterns and Guidelines

Limited Points of Variability*, Pattern Creation Lifecycle*

Meet-in-the-Middle Pattern Design

Context

We have identified a candidate pattern and determined that it has appropriate potential and ROI.

Problem

How do we design a pattern that accurately codifies best practices while also being consumable?

Forces

• We need to recognize that multiple perspectives are needed as we design a pattern.

• We need to ensure that there is a balance among the perspectives.

Solution

Three approaches to design that are often discussed include top-down, bottom-up, and meet-in-the-middle. The approaches are common and the names self-explanatory, so we’ll review them quite quickly. In top-down design, we start with high-level artifacts and work our way down to more detailed artifacts. In bottom-up, we start with detailed artifacts and work our way up to high-level artifacts. In the meet-in-the-middle approach, we compromise and incorporate both top-down and bottom-up approaches, meeting in the middle.

If we work in only a top-down fashion, we risk delivering a pattern that ends up not being true to the underlying best practice. If we focus on the design of the pattern only from a top-down perspective, we will not sufficiently leverage the exemplar and risk diverging from it. If we work only from a bottom-up perspective, we stay true to the exemplar, but we risk producing a pattern that is difficult to understand and work with. We risk losing sight of the bigger picture and can end up with a pattern that is not as simple, abstract, or consumable as it should be.

A meet-in-the-middle approach forces us to focus on the details of the solution as well as the higher-level issues, such as how the pattern should be written up and how it is consumed. When we look at things from a higher-level perspective, we can start to think about the rules, constraints, and assumptions that can assist us with consumability. How can they be used to simplify how the pattern will be consumed? How can we use them to increase the scope and impact of the pattern? We can also start to think about how the user should interact with the pattern. Should the pattern be just a specification? Would a pattern implementation also make sense? What tooling would be impacted by the use of the pattern? How will the pattern fit in with the development process? How will the introduction of the pattern impact the development process?

We then connect these ideas and aspects of the design with the details that we find and understand as we work with the exemplar. As we apply exemplar analysis, we see the different roles, artifacts, and the associated input information that are needed for the pattern. However, we recognize, based on our top-down efforts, that the input model to the pattern is not necessarily the model that the Pattern User will see. We can simplify the user model and how that model is represented to the user and connect that user model to the input model that the pattern requires.

We balance the two perspectives and need to approach the effort iteratively and incrementally. Alternating between the two perspectives will help us align the design of our pattern.

Example

In the case study the Oslec team uses this pattern when they build the Subsystem Façade pattern implementation. The Subsystem Façade is based on an exemplar coming from an existing solution, providing a bottom-up perspective on the pattern implementation. From a top-down perspective, they have a domain model that is to be used as a representation of the input model. The team then works to bring these two perspectives together to create a pattern that is true to the solution but is also consumable by the Pattern Users.

Related Patterns and Guidelines

Exemplar Analysis*, Pattern Creation Lifecycle*

Pattern Implementation

Context

The organization has adopted PBE and wants to move beyond just documenting best practices and following them manually.

Problem

Having pattern specifications is useful, but they can be tedious to apply (many steps, human error, etc.). And if we need to make changes to how the pattern is applied, we find that the reapplication of the pattern is tedious. We may also find that we still require a significant amount of expertise to apply a pattern specification. Last but not least, it is difficult to enforce governance of the pattern when the team is just using pattern specifications. How do we speed up the use of a pattern in a solution while ensuring that it is applied correctly and consistently?

Forces

• We require tooling that supports the creation and use of pattern implementations. We also need to ensure that the team is equipped to succeed with these tools.

• Quality and testing efforts typically associated with software deliverables need to be updated to support pattern implementations.

Solution

Automate the pattern by creating a pattern implementation. In contrast to a pattern specification, which captures a pattern as formal written documentation, an implementation codifies a pattern in tooling, automating the use of the pattern.

So the question we have to ask is, how is a pattern implementation surfaced in tooling? There’s actually not a single simple answer to the question. We can automate a pattern in tooling a number of different ways. Some of the possibilities include the use of a wizard, plug-in, model-to-model pattern implementation, UML pattern implementation, or model-to-text pattern implementation. The key is to understand each of these approaches and choose the right one for the situation. Some of the things that we want to consider as we decide how we wish to create an implementation include these:

• Where and when should the pattern be used?

• What does it take as input?

• What does it generate as output?

• Are we working with models?

• Are we generating text-based artifacts?

• How should the Pattern User work with the pattern implementation?

Based on the answers to these questions, we select one or more types of pattern implementations. We say “one or more,” as it is possible and common to use several types of pattern implementations in combination. For instance, we can use a UML pattern implementation to help us create an input model that is then consumed by a model-to-model pattern implementation.

We also need to consider the trade-off between intelligence in the pattern implementation and the level of detail required in the input model. We may find that we want to keep the input model as simple as possible. In that case the pattern implementation needs to be more intelligent, using rules and assumptions to help interpret the model. If we go to the extreme and have all details in the model, we would likely use a code generator that is able to do a simple and direct mapping from the input model to the output model.

As patterns are used in combination and do not exist in isolation, we should also watch for other pattern implementations that we can leverage in building our pattern implementation. Often a pattern implementation is a compound pattern, so we will want to incorporate other patterns into our efforts. In addition, as we build our pattern implementation, we will need to look at proper componentization to enable others to reuse portions of our pattern implementation as they build their own pattern implementations.

Example

A selection of pattern implementation examples is covered in Chapter 2. Additional model-to-model and model-to-text pattern implementations are identified, designed, built, and used in the case study.

Related Patterns and Guidelines

Compound Pattern*, Exemplar Analysis*, Model-to-Model Pattern Implementation (Chapter 13), Model-to-Text Pattern Implementation (Chapter 13), Pattern Creation Lifecycle*, Pattern Implementation Extensibility (Chapter 13), Pattern Specification (Chapter 13), UML Pattern Implementation (Chapter 13)

Guidelines

The following guidelines provide further advice on how to design patterns. In particular, we look at the overall lifecycle that brings together the solutions, exemplars, and patterns. We also consider how we can use patterns in team settings; after all, development is rarely an individual effort. The discussion also looks at variability points and extension points as two mechanisms to help with pattern customization.

Limited Points of Variability

Summary

When creating a pattern, how do we ensure that Pattern Users are able to customize the application of the pattern to suit the specifics of their situation? While doing so, how do we ensure that Pattern Users are not overwhelmed and confused by the configuration and customization options for the pattern? We need to limit the number and types of points of variability to ensure that the pattern provides the most important ones and then uses calculations and assumptions to leverage the input information provided by the Pattern User.

Introduction

A key aspect of a pattern is that it provides points of variability. These enable Pattern Users to customize the pattern and ensure that the pattern can be applied in the context in which they are operating. As the designers of the pattern, we face a balancing act. We want to provide Pattern Users with flexibility and configurability so that the pattern can be used in their situation. As we want to support a range of Pattern Users in a number of situations, the usual outcome is that as the number of Pattern Users grows, the requirements for configurability increase. However, the more flexible and configurable the pattern becomes, the more difficult it is to use. How do we ensure that the user of the pattern is not overwhelmed and confused by the configuration and customization options for the pattern? How do we ensure that the pattern is flexible and configurable and can be widely used? As we try to answer these questions, there are a number of considerations to keep in mind:

• We need to recognize and consider the pattern as well as related artifacts such as other pattern implementations, model templates, and DSLs.

• We need to pay attention to user feedback and adjust the points of variability as necessary to support usability.

• Simpler does not mean simplistic. We need to identify as few variability points as possible, but it is most important that we have all the necessary points of variability.

Explanation

Patterns by definition are reusable assets that are meant to be customizable. Recall from Chapter 1 where we highlighted that a pattern

. . . describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.4

To allow a pattern to be used “a million times over, without ever doing it the same way twice,” we need variability points. However, the situation is not so simple as to just use variability points; there is a balancing act between consumability and customizability. On the one hand, if we create a pattern with too many points of variability, the pattern becomes too complex to use, requiring that the Pattern User spend a great deal of time in not only learning the pattern, but also figuring out how it can be configured for their situation. On the other hand, a pattern with too few or no points of variability is no longer a pattern. In the case of too few points of variability, Pattern Users are unable to make the pattern specific to the needs of their situation; context no longer really comes into play when applying the pattern.

As we design the pattern and consider the points of variability, we have a number of options:

Use assumptions and derivations. In designing a pattern we need to ensure that the pattern provides the most important points of variability and then uses derivations and assumptions to leverage the user-provided information. In the case of a derivation, we are able to deduce some of the pattern parameters based on other information provided by the Pattern User. Much like the effort that goes into database normalization, why ask the Pattern User to provide information that can be calculated? Adding assumptions to the pattern can help us reduce the number of points of variability.

Split the pattern into multiple patterns. Does the existence of many points of variability indicate that we are trying to capture more than one pattern? Have we gone overboard in trying to apply the Compound Pattern pattern?

Create a larger pattern. Is there a larger-order pattern that we can use to customize the inner pattern according to best practices, while limiting the amount of input required from the user of the pattern?

Use related patterns. Are there existing patterns that could be used in conjunction with the pattern? Do they help eliminate the amount of variability needed for this pattern?

Validate the pattern with actual users. Test the pattern with end users. Do they find it overly complex? What flexibility do they say they need in the pattern? Which variability points do they use as they apply the pattern in real-world situations?

As we determine the points of variability for a pattern, it is important to make sure that we are capturing this information as part of the specification and related documentation. In the case of the pattern implementation, we will also use the associated pattern documentation to further detail the points of variability.

In Chapter 5 of the case study, while describing exemplar analysis for the Subsystem Façade pattern, we see how the Oslec team follows this guideline to analyze the exemplar to identify the points of variability and how they try to maximize the use of derivation to minimize the input information needed. More specifically, they first identify those aspects that do indeed vary, as opposed to those that remain static. As they analyze the elements that vary, they recognize that there are cases where a number of elements are related. Based on the relationship, they could reduce the amount of information provided by the Pattern User and then use calculations to populate the set of related elements. The Oslec team also creates a compound pattern and shares information among the set of patterns, again limiting the amount of detail required in the input model. In Chapter 6 they wrap up development of the pattern and move forward, working with a Pattern User to test the pattern and ensure that it both works and provides the correct level of variability.

Related Patterns and Guidelines

Exemplar Analysis*, Pattern Implementation Extensibility*

Pattern Creation Lifecycle

Summary

There is a flow in which we use existing patterns to help us build solutions that will serve as exemplars, which in turn lead to the creation of new patterns. With an exemplar in hand, we can use abstraction and analysis to create a set of one or more new patterns. We then use those patterns in the creation of future models that will serve as the basis for future patterns.

Introduction

As we begin to create patterns, we may find that it seems daunting. Where do our exemplars come from? How do we progress from exemplars to new patterns? What role do patterns play in the creation of exemplars? How can we then leverage patterns in the creation of additional patterns? How do these patterns fit into our development efforts? To find answers to these questions, there are a number of considerations to keep in mind:

• Mass-market tooling generates generic aspects of a solution. Most aspects of the solution that are unique and specific to an organization are created manually.

• The team needs to have abstraction and simplification skills while also being able to see the bigger picture of how elements relate and can come together.

• Exemplars need to be proven, best-practice solutions. They will serve as the basis for a pattern. As such, they are incredibly important.

Explanation

To design the pattern we need to recognize, understand, and follow the pattern creation lifecycle. Figure 12.2 illustrates this lifecycle, which shows how we use existing patterns to help build solutions that will serve as exemplars. Once an exemplar has been created, we use abstraction and analysis to create a set of one or more patterns. The lifecycle then continues as we use those patterns in the creation of future models that will serve as the basis for future exemplars and in turn patterns, and so on.

Figure 12.2. An overview of the flow from requirements through a solution and into the creation of a pattern

image

When we consider the models that we create in crafting an exemplar, our focus is usually on depth rather than breadth. We want to create the best-practice approach to some slice of the solution that can then be reused many times in building the rest of the solution. We are looking for situations where it is difficult or impractical for others to re-create instantiations of the exemplar for their specific situation. We often find that expertise is limited and therefore a valuable resource, so we want to leverage that expertise as widely across the organization as possible. To that end, we want to create the best-practice solution, and then find a way to make that solution available for others to use in building out the breadth of the solution. We replicate the use of the exemplar, but with the use of variability points we make each instantiation unique to the context of the situation. In other words, we provide a pattern to the organization to help us build out the complete solution that is needed.

This guideline is an important mind shift in how we use modern tooling. Rather than using tools as is to create a solution, we need instead to look at these tools as mechanisms that we can use to help us build our own tools—more specifically, our exemplars and patterns. A mainstream product will always have gaps and shortcomings with respect to problems that are specific to an organization. If the tools were tailored to a particular environment, the vendor would have much too small a market and would not make any money on its investment.5 So the vendor creates tools that solve a wide range of problems for a wide range of customers.

When we create our exemplar, we leverage the features of the tooling we have purchased or already created. Generic wizards and automations in tools can be used to form the foundation of the exemplars. We then use this output as the starting point for crafting the solution that is unique to our situation. We use automation provided by the tool as strategically as possible. We leverage its strengths in using it to build out the generic aspects of the exemplar. The work that we put into customizing and enhancing the output is work that we want others to be able to avoid. Of course, we are also aided in this effort by the use of existing patterns.

In the case of an MDD approach, we usually think through the design as we work through multiple models, with each model being used at a specific level of abstraction. As seen in Figure 12.2, as we create our exemplar, we can follow a similar approach. A benefit of building the exemplar and then in turn the pattern is that once the pattern has been built, we don’t require all those who wish to replicate the solution to use numerous models. We can avoid having to keep all of the models in sync as well as avoid the tasks associated with such efforts (reverse engineering, model reconciliation, etc.).

As we look at Figure 12.2, we can see that we are operating on two axes. The first axis is related to where we stand in relation to the problem versus the solution. We are driven by the problem as we work toward creating a solution that becomes an exemplar. This is straightforward, as we use the details of the problem to guide us in creating our exemplar, which includes selecting the appropriate existing patterns to use. The other axis is related to how concrete or abstract the element is. As we build out the exemplar, we extend its scope to account for variability and variations that might not exist in any one instance of the solution but would show up in an analysis of a range of instantiations of solutions based on the best-practice approaches that are embodied by the exemplar. In this way we start to move from a very concrete and specific representation of the solution to something that is a little more abstract. As we continue to increase the level of abstraction and work toward the problem space, we start to approach the definition of the pattern. The pattern allows us to handle variability by providing points of variability that allow Pattern Users to influence the pattern output.

To summarize, we use one or more models to work from a problem and move into the solution space. We then move from this concrete representation of the solution to one that is more abstract, which we call an exemplar. Once we have an exemplar in hand, we increase the amount of abstraction and start to think about how this relates to the problem space, which ends up being represented as a pattern. With completed patterns available, we can then continue the cycle.

The case study provides an example of the full pattern creation lifecycle, from the use of patterns in defining a solution, transitioning the solution to an exemplar, using that exemplar to create a pattern, and then using that pattern in creating the solution.

Related Patterns and Guidelines

Design Solutions with Patterns (Chapter 16), Piecemeal Pattern Creation (Chapter 10), Simple Solution Space (Chapter 10)

Pattern Implementation Extensibility

Summary

How can we facilitate the use of a pattern implementation in situations for which it was not originally intended? As part of the design, consider how and where we want to enable others to extend the pattern implementation.

Introduction

As we design our patterns, we cannot account for all possible future uses. If we do so, we will either overengineer the pattern or find ourselves in a situation where we are never able to finish the pattern. However, we’d still like to make our patterns flexible and adaptable to unforeseen situations. How can we do so without overengineering the solution? How can we do so while still delivering a completed pattern within reasonable and expected timelines? Consider:

• There is a balancing act between increasing reuse potential and increasing complexity.

• Documentation and packaging associated with the pattern must detail and support extensibility. The documentation needs to be thorough enough to detail the extension points as well as how to use them to extend the pattern.

• The pattern implementation platform we are using must provide a mechanism for extensibility.

Explanation

As discussed in relation to the Limited Points of Variability guideline, we want to design a solution that provides a balance between usability and configurability. In that case much of our focus was on how we can consume a pattern as is. However, we also need to look at the design from the point of view of others who may want to build a new pattern. In such a situation those building the new pattern may find that our pattern provides them with a significant portion of the solution needed for their pattern. One approach to adding capabilities to such a pattern is to use Compound Pattern. Another approach, which we discuss in this guideline, is to provide extension points, which allow for extending the pattern.

An extension point is a formally defined construct associated with the pattern implementation that allows others to add to or alter the pattern. For example, in using a model-to-text pattern implementation we may use an extension point to change the way code is generated for certain situations.

When looking at how to design a pattern so that it is extensible, we need to keep the following in mind:

Balance continues to be important. We want to ensure that we have the right balance between producing a pattern for our current needs and providing support for future unknown needs.

Targeted extensibility. As we define the extension point, we identify where it makes sense for the pattern to be extended. It is a way for the pattern author to ensure that the pattern essence is preserved even when extended. What are the most important and critical aspects and elements of the pattern that we are designing? What elements of the pattern would provide the most value to someone looking to reuse these elements via extensibility? We want to focus the limited time and effort we have for extensibility on those areas we see as providing the most value to future pattern implementations.

Platform capabilities. When designing a pattern implementation, we need to ensure that the tooling that we are using for the implementation provides support for extensibility. Also, different platforms have different mechanisms in place for supporting extensibility. As part of the design, we need to consider how and where we want to enable others to extend the solution.

The Eclipse platform provides an example of this approach through its mechanism for Extension Points.6 Pattern implementations built on the Eclipse platform are typically developed and then delivered as Eclipse plug-ins, so we are able to use the standard Eclipse Extension Point mechanism for our patterns. With the extension points in place, we can modify the behavior of the pattern, the output of the pattern, or calculations that are performed upon elements from the input model. The Pattern Implementation Author needs to be aware of this capability and use it judiciously, as we want to avoid overinvesting in flexibility and meeting future unknown needs.

Related Patterns and Guidelines

Plug-in pattern,7 Limited Points of Variability*

Team Pattern Implementation Use

Summary

Software development is a team sport, requiring the efforts of many to succeed. Thus, the patterns that are built need to support a team environment. How do we design patterns to support team development scenarios?

When we design patterns for a team environment, we need to consider the reapplication strategy, management of user-provided content, model structuring and merging support, and the languages that will be used.

Introduction

We want to use a number of pattern implementations across our development team, including multiple levels of abstraction. How do we design our patterns to support team development scenarios? For instance, when the team is using our patterns in building their solution, how do they share models? How do they add code to the solution? How do they modify code that was generated by the pattern? How are round-trip scenarios supported? As we attempt to answer these questions, there are a number of considerations to keep in mind:

• Testing efforts associated with the resulting pattern need to account for team and reapplication scenarios.

• We need to ensure that the artifacts that are generated by the pattern stay true once they are generated. If Pattern Users need to change the output from the pattern, perhaps they need to revisit whether they are using the correct pattern.

• We need to plan how we want to support the customization of output from the pattern.

Explanation

Designing patterns for use in a team environment requires that we consider the reapplication strategy, management of user-provided content, model structuring and merging support, and the languages that will be used.

Let’s start by considering ownership of the artifacts that are generated by a pattern. This particularly applies when looking at model-to-text pattern implementations; that is, we are at the point where we are moving from some higher level of abstraction to where we are working with code, configuration files, and other text-based artifacts. Usually we find that the pattern we are using will not lead to 100% content generation, so we have a situation where some percentage of the solution is generated by the pattern and the rest needs to be created by the user. However, what happens when we need to update the model? What happens to user-created code when requirements change? Designs are updated? Defects are identified?

When looking at the ownership of the output, we can see that there are four possibilities:

  1. Pattern-implementation-owned. The pattern implementation owns the output and expects that the user will not touch or update this output. No user modification is allowed.
  2. User-modifiable. In this situation the pattern implementation will write default values to the output unless the user specifies custom output.
  3. Seeded. A pattern implementation will write this element only once. After the content has been seeded, users are free to do as they desire.
  4. Other. The pattern implementation may encounter output that exists in the target output that is none of these other options. It could be content that is user-generated or possibly generated by another pattern implementation.

We need to make sure that the user of the transformation is aware of the owner of the content and how it can be updated. Therefore, we need to identify the owner of the output in some way, either by annotation or by putting the output in a specific location. A best practice is to keep output that is transformation-owned separate from other elements.

As we design our pattern, we need to keep each of the four ownership scenarios in mind and set up our pattern accordingly. Recognizing the content owner and clearly identifying how code is owned become important when it comes time to reapply a pattern. When the pattern is reapplied, we need to be able to determine its behavior. Should it overwrite the content that is already in the target? Should it leave it as is? Are there situations where the files are shared between the user and the pattern? Should it try to merge the new and the old? As we design our pattern, we need to keep these questions in mind and determine a reapply contract that will work for our pattern and the expected Pattern Users. Once we define this contract, we need to ensure that it is documented and published.

As we work with code that is user-modifiable, we need to ensure that our design identifies how we want to handle this scenario. We can use special tags or instructions in our pattern output to indicate the behavior that is supported when the pattern is reapplied. For instance, we can use a tag to denote where a user can add content to the output without concern that the pattern will overwrite the content when the pattern is reapplied. As we design, we want to be strategic in terms of where and how we provide user-modifiable regions.

We want user-modifiable regions only in places where we truly need the user to provide content. We do not want to use them just to take shortcuts in figuring out what the pattern should be creating for us. Overuse of user-modifiable regions leads to situations where we undercut the best practices in the pattern, as the Pattern User can essentially change the pattern solution, reducing our adherence to best practices and consistency. We also need to make sure that we are not underusing such annotations, because this may lead to user frustration with the pattern. There will be times and places that users need to customize; they should not have to change the pattern to get the output that is needed.

As we grasp how the pattern works with individual users and an iterative process, we need to expand the scope and complexity and start to think about what happens when there are multiple people working in this situation. For example, the user model may be created, populated, and maintained by multiple users; how are we supporting these users? Ideally, the modeling language and tooling that we select will support model fragments and model merging. In this way we can isolate the changes that are made, and in cases where multiple people are working in the same fragment, we can merge the changes.

At a design level, we’re not overly concerned with all of the details of the tag, or the code in the templates, but we do need to understand when and where such functionality will need to be used. In addition, we need to understand the capabilities and support provided by the tooling and languages that we have selected. For instance, many UML tools provide out-of-the-box support for model merge and working with model fragments. So, if we build a DSL on top of UML using a profile, we will benefit from being able to reuse these features.

A simple example can be seen in the way compilers work. They often support the use of src and bin directories. The compiler will use the src directory as input and will overwrite any and all contents in the bin directory, as it is compiler-owned.

When working with JET in creating templates to support such a scenario, we can direct those building the pattern to use the <c:userRegion> tag. In the case of working with Java, we can leverage the <java:merge> tag within JET. This tag will cause the generated contents to be merged with the content already found in an output file. We provide such an example while developing the Subsystem Façade pattern in the case study.

Related Patterns and Guidelines

Integrated Patterns and DSLs (Chapter 15), Simple Solution Space (Chapter 10)

Summary

There are a number of skills and considerations that come into play as we design patterns. For instance, we need to understand the pattern creation lifecycle and the relationship among solutions, exemplars, and patterns. We also need to give thought to how patterns will be consumed, supporting ease of use as well as team scenarios. The design also needs to account for how the pattern will be customizable. The pattern output needs to be customizable to the specific context of the Pattern User. However, there is a balancing act between customization and ease of use. Overall, much as in designing any solution, we need to consider our requirements and options and select the trade-offs that make the most sense for the situation.

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

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