Describing Patterns with Collaborations






Describing Patterns with Collaborations

You don’t want to reinvent the proverbial wheel. In the old days, craftsmen built a physical template for a wagon wheel into the floor of a barn. They’d reuse the template or pattern to create a new wheel by bending wood to fit the framework etched in the floor. Builders, craftsmen, and engineers use the same basic approach to solving hard problems—they develop a pattern, using the following steps:

  1. Build ad-hoc solutions to a development problem.

    The answer to a complex problem requires you to make a choice among competing alternatives. You build different solutions when you’re not sure what works best or which solution offers the best results.

    For example, suppose you must write software that constructs a complex assembly for a CAD/CAM (Computer-Aided Design/Computer-Aided Manufacturing) application. You must program two behaviors: the process of constructing the whole assembly (an air-filter unit) and the creation of each part in the assembly. The first thing you do is build several different software solutions to assemble air-filter units for the CAD/CAM application. These two bits of behavior can be programmed in many ways, and you need to try several solutions to see which one works best.

  2. Find the solution that works best in different situations.

    In our running CAD/CAM example, the most successful solution to putting together a complex assembly is found by separating the two behaviors into different classes. You need a class that represents the assembly of an air-filter unit and a class that knows the process of constructing an air-filter unit.

  3. Abstract commonality out of your best solutions.

    Find and extract the important common features (classes, attributes, operations, associations) of your solutions and make the solution as general as possible. Base your decisions on practice, not on theory. Look at balancing such competing factors as cost to build, time to build, and performance of the resulting solution.

    In the CAD/CAM software example, the common features of successful solutions are as follows:

    • Provide a class that directs the construction of the whole assembly. A Director class knows how to direct the assembly of air-filter units. The Director does not actually build each part, just knows which part to build in what order to build the parts making up the assembly.

    • Define a common interface for building each part. The interface must capture the operation signature (see Chapter 3) for building an assembly so we’ll call the interface Builder.

    • Supply a specific class that knows how to build individual parts for the whole assembly. Since this class actually knows how to build individual parts for the assembly it gets the name ConcreteBuilder.

  4. Create a pattern that describes the abstractions you developed in Step 3.

    Providing other developers with a pattern description helps you communicate clearly what works.

  5. Reuse the pattern in the appropriate situations to boost productivity and build high-quality solutions.

Defining and classifying patterns

A pattern is basically a template solution to a problem. Patterns can be vital to the development process—but they’re only effective if they’re presented clearly and consistently.

 Remember   When you describe a pattern, provide your fellow developers with the following information:

  • Pattern name: Give your pattern a memorable name that matches its purpose.

  • Problem description: You tell others just what problem this pattern solves. Provide your readers with information about the context of the problem, when to consider using your pattern, and how to recognize whether the problem they have is one to which your pattern provides an answer.

  • Solution description: Here you describe the classes, how they collaborate, their associations, their constraints, and the job of each class.

  • Consequences of the solution: Every one of your patterns has positive and negative aspects. Don’t forget to tell other developers about any issues they must face as a result of choosing to use your pattern.

Patterns occur at many different levels of complexity. The three most important levels for complexity are given the following names:

  • Pattern: A pattern is a solution to a small software problem that developers face over and over again in the construction of an application. A well-built application utilizes many patterns to solve modeling problems during analysis and construction issues at design time.

  • Framework: A pattern for an entire application is known as a framework. Frameworks are “almost complete” applications; decisions about the structure of the application, specific classes, their behavior, and flow of control through the application are already made and in place. Just plug in a few of your own classes to employ the framework for your application’s requirements.

  • Architectural framework: A pattern for an entire system composed of many applications is known as an architectural framework. On a grand scale, you use architectural frameworks to bind many applications into a whole system of subsystems. An architectural framework could be a group of application frameworks, but it does not have to be. The important thing about architecture frameworks is that they describe how the individual subsystems work together. At this level, the architectural framework provides guidelines that specify responsibilities and interactions for each of your subsystems/applications.

 Tip   For lots more information on patterns, check out the Hillside Group at http://hillside.net/.

Using composite structure diagrams

You use a special composite structure diagram to describe a pattern. The composite structure diagram shows a “collaboration” and the parts that play different roles in the pattern. A collaboration is a group of objects interacting together to accomplish some functionality. (For more on composite structure diagrams, see Chapter 5.)

You show a pattern with a collaboration symbol and an internal structure:

  • Collaboration symbol: A collaboration symbol is a large dashed oval shape. (Be prepared to make it large.) At the top of and inside the dashed oval, you place the name of the collaboration (that is, your pattern’s name). Use a dashed line to separate the pattern’s name from its internal structure.

  • Internal structure: Place each element of your pattern inside the dashed oval. Be prepared to make the elements small if you didn’t make the oval large enough. When illustrating a pattern, be sure to attend to the following issues:

    • Draw a composite structure diagram: When you show the elements of your pattern, you use a composite structure diagram. These diagrams consist of parts and connections. Parts are simply classes shown inside another class. Connections are special kinds of associations shown inside a class. See Chapter 5 for more details.

    • Show each major class of your pattern as a part: You draw each part as a box with the name of the part inside. I name the parts after the role each part plays in the pattern.

    • Show connections between the parts: If you have an association between the classes in your pattern, show them as connections between the parts. To show a connection, draw a line between the two parts that must communicate. Add any multiplicity constraints on the connection; put them between the parts. Don’t forget to name the connection, just as you name associations. This helps others to understand what’s going on.

  • Decide whether to use an interface: If your pattern calls for inheritance and abstract operations in a superclass, then use an interface. Instead of a superclass, you attach a provider interface to the subclass; the provider-interface symbol is a lollipop or a small circle attached to a line. The line joins up with whichever internal part provides the attributes and operations defined by the interface. For the part that actually invokes those operations, you show a required-interface symbol—half a circle attached to a line—and use the line to connect the required-interface symbol to the part that does the invoking. When that’s done, you simply connect the required-interface symbol to the provider-interface symbol. When you’re done, it looks like a ball-and-socket joint. (See Figure for an example.)


    Figure: Collaboration showing the Builder design pattern.

 Warning   Composite structure diagrams don’t allow generalizations, so you can’t use them to show an inheritance hierarchy. Use an interface symbol instead. (See Chapter 5 for more on composite structure diagrams and Chapter 6 for more on generalization and inheritance.)

 UML2   UML 2 has a notation for expressing patterns. It’s called a composite structure diagram with collaboration. If you’re familiar with UML 1.x, don’t confuse this diagram type with the old collaboration diagram. UML 2 doesn’t actually have a collaboration diagram. Instead, UML 2 renames the old UML 1.x collaboration diagram and calls it a communication diagram. (See Chapter 14 for more information on communication diagrams.) Collaboration means the structure depicted in a static diagram that shows the relationship among classes that serve as parts working together to accomplish some collective behavior, but the diagram doesn’t specify how they collaborate.

Looking at a common design pattern

Figure illustrates the design pattern known as Builder that developers frequently use. You see a large dashed oval with the name of the pattern at the top. The Builder design pattern consists of three primary classes and two interfaces—a provider interface and a required interface. The Director knows what to build and when to build it. The ConcreteBuilder knows how to construct a particular Product. The pattern also includes the Builder provider interface and the Builder required interface. An instance of the Director invokes operations on instances of a ConcreteBuilder defined in the Builder interface. The provider interface is shown with the Builder name above as a small closed circle attached to the ConcreteBuilder. The required interface is shown with the same Builder name above an open circle (socket) attached to the Director. An instance of ConcreteBuilder then invokes known operations on the Product class to construct instances of the parts that eventually make up the Product.

 Remember   If you document design patterns and you have to deal with inheritance (generalization), use an interface to capture the abstract superclass.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows