Tag Archives: patterns

Review: Applying Domain-Driven Design and Patterns (Nilsson)

Applying Domain-Driven Design and Patterns Applying Domain-Driven Design and Patterns: With Examples in C# and .NET, by Jimmy Nilsson. Addison-Wesley, 2006.

Stir together Eric Evans' Domain-Driven Design with Martin Fowler's Patterns of Enterprise Application Architecture, mix in C# code, and you've got the flavor of this book.

It's not as classic as its antecedents. But it does provide useful examples, relating it from the perspective of someone familiar with database issues, but who now comes at things from the domain-driven perspective.

Some of the tools used have changed, but the basics still hold.

3A – Arrange, Act, Assert

Some unit tests are focused, other are like a run-on sentence. How can we create tests that are focused and communicate well?

What's a good structure for a unit test?

3A: Arrange, Act, Assert

We want to test the behavior of objects. One good approach is to put an object into each "interesting" configuration it has, and try various actions on it. 

Consider the various types of behaviors an object has:

  • Constructors
  • Mutators, also known as modifiers or commands
  • Accessors, also known as queries
  • Iterators

I learned this separation a long time ago but I don't know the source (though my guess would be some Abstract Data Type research). It's embodied in Bertrand Meyer's "command-query separation" principle, and others have independently invented it.

With those distinctions in mind, we can create tests:

Arrange: Set up the object to be tested. We may need to surround the object with collaborators. For testing purposes, those collaborators might be test objects (mocks, fakes, etc.) or the real thing.

Act: Act on the object (through some mutator). You may need to give it parameters (again, possibly test objects).

Assert: Make claims about the object, its collaborators, its parameters, and possibly (rarely!!) global state. 

Where to Begin?

You might think that the Arrange is the natural thing to write first, since it comes first.

When I'm systematically working through an object's behaviors, I may write the Act line first. 

But a useful technique I learned from Jim Newkirk is that writing the Assert first is a great place to start. When you have a new behavior you know you want to test, Assert First lets you start by asking "Suppose it worked; how would I be able to tell?" With the Assert in place, you can do what Industrial Logic calls "Frame First" and lean on the IDE to "fill in the blanks." 

FAQ

Aren't some things easier to test with a sequence of actions and assertions?

Occasionally a sequence is needed, but the 3A pattern is partly a reaction to large tests that look like this:

  • Arrange
  • Act
  • Assert
  • Act
  • Assert
  • Arrange more
  • Act
  • Assert

To understand a test like that, you have to track state over a series of activities. It's hard to see what object is the focus of the test, and it's hard to see that you've covered each interesting case. Such multi-step unit tests are usually better off being split into several tests.

But I won't say "never do it"; there could be some case where the goal is to track a cumulative state and it's just easier to understand in one series of calls. 

Sometimes we want to make sure of our setup. Is it OK to have an extra assert?

Such a test looks like this:

  • Arrange
  • Assert that the setup is OK
  • Act
  • Assert that the behavior is right

First, consider whether this should be two separate tests, or whether setup is too complicated (if we can't trust objects to be in the initial state we want). Still, if it seems necessary to do this checking, it's worth bending the guideline.

What about the notion of having "one assert per test"?

I don't follow that guideline too closely. I consider it for two things: 

  1. A series of assertions may indicate the object is missing functionality which should be added (and tested). The classical case is equals()It's better to define an equals() method than (possibly create and) repeat a bunch of assertions about held data.
  2. A series of similar assertions might benefit from a helper (assertion) method.

(If an object has many accessors, it may indicate the object is doing too much.)

When a test modifies an object, I typically find it easiest to consider most accessors together. 

For example, consider a list that tracks the number of objects and the maximum entry. One test might look like this:

    List list = new List();
    list.add(3);
    assertEquals(1, list.size());
    assertEquals(3, list.max());

That is, it considers the case "what all happens when one item is inserted into an empty list?" Then the various assertions each explore a different "dimension" of the object.

What about setup?

Most xUnit frameworks let you define a method that is called before each test. This lets you pull out some common code for the tests, and it is part of the initial Arrange. (Thus you have to look in two places to understand the full Arrange-ment.)

What about teardown?

Most xUnit frameworks let you define a method that is called after each test. For example, if a test opens a file connection, the teardown could close that connection.

If you need teardown, use it, of course. But I'm not adding a fourth A to the pattern: most unit tests don't need teardown. Unit tests (for the bulk of the system) don't talk to external systems, databases, files, etc., and Arrange-Act-Assert is a pattern for unit tests. 

History

I (Bill Wake) observed and named the pattern in 2001. "Arrange-Act-Assert" has been the full name the whole time, but it's been variously abbreviated as AAA or 3A. Kent Beck mentions this pattern in his book Test-Driven Development: By Example (p. 97). This article was written in 2011. Added a description of Assert First and Frame First due to Brian Marick's comment. [4/26/11] 

Review – Design Patterns in Java

Design Patterns in Java. Steve Metsker and William C. Wake. Addison-Wesley, 2006. I won’t review my own book, but I will summarize:

This is a workbook-style book, updating Steve’s earlier Design Patterns Java Workbook and Design Patterns in C#. It covers the same 23 patterns as Design Patterns, but adds some different perspective and a number of challenges to help you make sure you understand the patterns. It’s targeted to intermediate programmers, though more advanced programmers who want to brush up on patterns might consider it also. (May, ’06)

Review – Refactoring to Patterns

Refactoring to Patterns, Joshua Kerievsky. Addison-Wesley, 2005.
Design patterns and refactoring have been related for a long time. (Consider that Ralph Johnson, one of the co-authors of Design Patterns, was a sponsor of the work that created the original refactoring browser.) Josh has cataloged a number of refactorings that can lead your code to (or toward) any of the best-known design patterns. The code examples are excellent: realistic, interesting, and showing what was added or deleted. I recommend it for anybody who understands the basic concepts of refactoring, and is ready to further develop their design and refactoring skills. (Reviewed Sept., ’05)

Pattern Patter: Anonymous Subclass with Instance Initializer

In JUnit Recipes, JB Rainsberger points out this idiom:

static final Set s = new HashSet() {{
  add("this");
  add("that");
  add("another");
}};

(JB points to an article by Paul Holser, which cites Dave Astels’ Test-Driven Development as the source.)

What’s it do? The new HashSet(){}; part creates an anonymous subclass (a new type of HashSet without any name). The inner braces are an instance initializer, run before the constructor (implicit and empty) of our new class. So this code creates a new Set, and fills its contents.

Review – Domain-Driven Design

Domain-Driven Design, by Eric Evans. Addison Wesley, 2004.
Evans has developed a pattern language that focuses on how thinking about our domain needs to be of primary importance in developing software. This understanding should show up consistently in how customers and programmers talk about the domain, as well as in the code. His language includes discussion of key architectural concerns such as an effective persistence mechanism and layering.

People have struggled with "what's a metaphor" in XP. His pattern language provides the concrete notions of "ubiquitous language" and domain modeling to make that area more concrete. He has a wonderful example of how a shift to talking about "share pie" and "share arithmetic" solved a lot of persistent rounding problems in financial software. (I would call this a metaphor; I'm not sure he would.)

Like Design Patterns, this is a book that deserves to be studied and expanded on. This will still be a significant book years from now
(Reviewed December, 2003.)

Patterns for Iteration Retrospectives

These patterns (or perhaps proto-patterns) discuss how to use iteration retrospectives as a way of helping a team reflect on and learn from the project while it’s still going on.

Some goals of retrospectives:

  • Build a safe environment
  • Build trust and participation
  • Appreciate successes
  • Provide a framework for improvement
  • Catharsis
  • Face issues
  • Set team "rules": create and evolve the team’s process

The organization of these patterns:

  • Overall
    • Iteration Retrospectives (1)
  • Safety
    • Safe Space (2)
    • Bag Check (3)
    • Anonymous Responses (4)
    • Open Format (5)
  • Language
    • Safety Blanket (6)
    • Reframing (7)
  • Structure
    • Backward and Forward Look (8)
    • Deeper Dig (9)
    • Fish in Water (10)
    • Facilitator’s Toolbox (11)
    • Change in Pace (12)
  • Outcomes
    • Tentative Rules (13)
    • SMART Goals (14)
    • Smaller Bites (15)

 

I. Overall

Iteration Retrospectives (1)

Retrospectives are effective tools for helping teams learn from experience, but after a project, it’s too late (for that project). Agile teams deliver frequently, but often can’t afford to do a full project retrospective after each delivery.

Therefore,

Hold short retrospectives at the end of each iteration.

 They typically take 15-60 minutes; when this is first done it will tend to take on the longer side. Invite the whole development team. [But – there may be power issues related to management, so be careful about these.]

Note that iteration retrospectives don’t preclude project retrospectives.

Related Patterns: Section II. Safety discusses ways to make people comfortable sharing their concerns. Section III. Language considers a couple speech patterns you may want to be aware of. Section IV. Structure looks at different ways to organize the retrospective session. Finally, section V. Outcomes considers how the team takes the results of a retrospective and applies it back into the project.

Iteration retrospectives 

* * *

II. Safety

It’s risky to say certain truths aloud. A retrospective needs to take that into account. These patterns talk about the need for safety, and how to take advantage of trust as it builds up over time.

Safe Space (2)

Most people have thoughts and concerns they want the whole team to know, but people won’t share their inmost thoughts unless they believe it’s safe to do so. Iteration retrospectives don’t have enough time to slowly build rapport and safety.

Therefore,

Let repetition and familiarity build safety over time. Use safer (more anonymous) mechanisms at first, and move to more open ones as people become comfortable.

Related Patterns: Bag Check(3) provides a means of getting over uncertainty about the benefits. Anonymous Responses(4) provides a way to let people say hard truths; Safety Blanket(6) provides another way. You can move to a more Open Format(5) as people trust each other more.

Shield

 * * *

Bag Check (3)

In extremely sensitive situations, people may have concerns that they’re not willing to ignore. But if they don’t set them aside and cooperate, the team can’t do its job at all. People can’t wait a week or two to resolve this.

Therefore,

Each day, let people have a brief, structured chance to express their concerns, and temporarily set them aside.

Example: Some teams use a "bag check" or "parking lot" protocol, where they explicitly identify concerns at the start of the day, but then set them aside to work. At the end of the day, there’s another meeting where people can "reclaim their bag" (if it’s still a concern) or "abandon their bag" (if the concern has dissipated). This lets the team acknowledge their concerns, but still work.

Example: Scrum teams have a daily meeting where people tell what they did yesterday, what they intend to do today, and what’s in their way. (XP teams often use a similar "stand-up" meeting.)

Example: The Core Protocols (Software for Your Head) have explicit protocols for Check In and Check Out, so people can tell the team when they are engaged or not.

Luggage

* * *

Anonymous Responses (4)

Saying nothing feels safest, but deprives the team of a chance to learn and improve.  It’s hard to be the first to speak. It’s hard to understand other people’s perspectives.

Therefore,

Use anonymous or semi-anonymous techniques to make it safe to communicate things it might not feel safe to say out loud.

Example: A technique for anonymous responses would be to have each person write a topic on a card, then have the facilitator shuffle the cards, read them, write the topic on a chart, and destroy the cards.

Example: A technique for semi-anonymous responses would be to have each person write a topic on a sticky note, then walk to the chart and place the note where they think it belongs. Someone determined to break the anonymity could watch for handwriting, or see who put what where, but it mostly lets people act "as if" they don’t know who wrote what.

Related Patterns: Expect to be able to move to a more Open Format(5) as people build trust over time.

Mask

* * *

Open Format (5)

Over time, the group becomes more comfortable and people feel they can safely say what they want and what they see, but the meeting structure has excessive concern for safety. The team wants to review more quickly, but the safeguards slow them down.

Therefore,

Evolve to a more open format. Retain a way for someone to request a safer format when they think the team needs it.

Example: Instead of placing sticky notes, let people call out topics to a facilitator. 

Example: Let others on the team facilitate the review.

Facilitator at easel

* * *

III. Language

The way people discuss things can reveal things about a situation.

Safety Blanket (6)

If an issue is sensitive enough, saying, "I’m concerned about this issue" (even anonymously) can feel risky.

Therefore,

Wrap the concern in a "safety blanket": instead of "What concerns do you have?," ask "What concerns do you think people on your team have?" or even, "What concerns do you think people on a similar project might have?"

 Note: As people learn to trust each other, this pattern can fade away.

Related Patterns: Safe Space (2).

Baby on blanket

 * * *

 

Reframing (7)

People omit subjects and objects in their sentences, making hidden assumptions. People ignore their own power and wait for others to do things. People mistake wishes for needs.

Therefore,

Recognize when this is a problem, deconstruct what is said, and re-frame it into a statement that is active and under control of the speaker.

Example: "Management ought to provide snacks." Is this because the team wants snacks, or wants a demonstration that management cares? If it’s the former, people could start bringing snacks in and try to create a trend.

Example: "Somebody ought to make sure it works before QA gets it." This is much more powerful, when turned into an explicit request, "Developers, for each story would you add an explicit task to double-check that it works before marking the story done?"

Gilded picture frame

* * *

IV. Structure

These patterns consider the structure of the retrospective, and ways to explore what the team thinks.

 

Backward and Forward Look (8)

Some things have gone well, others poorly. Some problems are temporary, others last longer. Talk alone doesn’t change things.

 Therefore,

Use a framework that looks both backward (to what happened) and forward (to what we intend to do in the future).

Example: The SAMOLE framework asks people to suggest things that the team should keep doing the SAme, things the team should do MOre of, and things the team should do LEss of.

Example: The PMI framework (deBono) asks people to consider what is Plus, Minus, or Interesting.

Example: The WW/NI framework asks people to consider what Works Well and what Needs Improvement. (This may be augmented with explicit "Resolutions" for how to act in the future.)

Example: The WW/DD framework asks people what Worked Well and what they would like to Do Differently. (This is sometimes known as the Plus-Delta framework.)

Example: Appreciative Inquiry approaches focus on peak experiences and how to recreate them, rather than on what’s gone poorly.

Traffic sign

* * *

Deeper Dig (9)

Just because an idea is on the table doesn’t mean everybody agrees with it. People may have other things to say. Other ideas or problems may be more important.

Therefore,

Mine the data for areas of agreement and areas of conflict. Compare this retrospective to previous ones.

Example: You’d like to know how important a concern is. In anonymous forms, you may see the same topic appearing multiple times (perhaps with similar words). In an open form, you can vote or multi-vote on what’s important.

Example: Sometimes the same topic may appear in multiple categories, representing a conflict. One person may think the team is refactoring too much, another that it’s refactoring too little. You may take this information and try to surface where the rest of the group is.

Example: Sometimes the same topic may appear in multiple categories, without being contradictory. Someone may write that refactoring worked well this week, and the same person suggest that refactoring needs improvement.

Example: By looking at the previous retrospective, you may see that an  issue recurs. This may help a team learn that its interventions aren’t working in this area.

Shovel

* * *

Fish in Water (10)

Some problems are like water to a fish: so much part of the environment that they’re hard to notice. Other problems are noticed, but not named.

Therefore,

Actively seek to find what the team is not seeing or not saying. You may need to create extra safety to make it safe to discuss.

Example: People sometimes talk about the "elephant in the room": a problem so big it can’t be ignored, but yet it’s never mentioned. For example, hurtful behavior by a manager could be very hard to discuss.

Example: People may get used to something and forget about the possibility it could be changed. For example, the room is always the wrong temperature, or this tool always crashes.

Aquarium

* * *

Facilitation Toolbox (11)

An unexpectedly sensitive issue arises, or something comes up that doesn’t fit the team’s usual framework.

Therefore,

Maintain a set of facilitation techniques, and use them when needed.

Example: Break into small discussion groups

Example: Use a Structured Sharing technique (Thiagi – www.thiagi.com).

Example: Use multi-voting, prioritization, polls, etc.

Related Patterns: If trust has shifted, you may need to focus again on creating a Safe Space(2).

Toolkit

* * *

Change in Pace (12)

Using a different format each week adds novelty, but makes it hard to develop a rhythm. A standard format can make it easier to do retrospectives week after week, but it can get boring after a while.

Therefore,

Periodically (e.g., every two to three months) try a different retrospective style. This could be a one-time event, or a change for retrospectives going forward. 

Example: Change the standard format from SAMOLE to WW/DD.

Example: Play a retrospective game (such as one based on a classification card game).

Example: Try one of the exercises in Norm Kerth’s book (Project Retrospectives).

Shoes

* * *

V. Outcomes

Tentative Rules (13)

People need agreement on how to work together, but people resent being told what to do.

Therefore,

Let people explicitly set the rules under which they’ll work together, and provide feedback mechanisms so they can adjust them when necessary.

Keep rules fluid. The stance is "trying on a shirt" to see how the shirt fits and feels, without committing to buying it first.

Example: Many agreements require consensus. People must agree to live with the rule, perhaps for a fixed time. (Even someone who completely disagrees with a strategy may be willing to give it a fair chance, so others can come to disagree with it for themselves.)

Shirt

* * *

SMART Goals (14)

People suggest protocols that are too fuzzy to assess, or not well enough understood to act as guides. People state ideals as if they were rules.

Therefore,

Use the SMART acronym to guide the creation of effective rules:

                S – Specific
                M – Measurable
                A – Achievable
                R – Relevant
                T – Time-Boxed

Example: "Refactor better." It’s hard to argue with, but it doesn’t actually tell anybody what to do.  The team can break this up into concrete actions, such as, "Pick a smell each week, and spend an hour each Wednesday trying to find examples of it," or "When you check off a task, write Yes or No beside it to indicate if you looked for refactoring opportunities before checking in."

Related Patterns: If your goal isn’t met, you may try a Smaller Bite(15).

Brain

* * *

Smaller Bites (15)

The team wanted to try something new, but they didn’t get around to it or it was too hard. This is especially a concern if an item has been on the team’s list for two or more iterations without success.

Therefore,

Try something similar, but easier.

Example: "Automate at least one customer-specified test for each story" was the goal, but the team didn’t do that. They might agree to "Automate a customer-specified test for the next story we start."

This pattern helps a team converge "What we say we’ll do" with "What we really do." Once smaller bites have been mastered, the team can move back up to the bigger goals.

Apples with bites out

* * *

Resources

Cockburn, Alistair. Agile Software Development. Addison-Wesley, 2001. "Reflection workshops" are a top-level practice in Crystal Clear.

Kerievsky, Joshua. "How to Run an Iteration Retrospective." 2002. <www.industriallogic.com/papers/HowToRunAnIterationRetrospective.pdf>

Kerth, Norm. Project Retrospectives: A Handbook for Team Reviews. Dorset House, 2001.

McCarthy, Jim and Michele McCarthy. Software for Your Head: Core Protocols for Creating and Maintaining Shared Vision. Addison-Wesley, 2001.

Kaner, Sam, et al. Facilitator’s Guide to Participatory Decision-Making. New Society Publishers, 1996.

Thiagi. www.thiagi.com – Training and games.

[Originally developed August, 2003 for an OOPSLA workshop on retrospectives. Edited and re-organized, September, 2005. Added references to Cockburn and Kerievsky, Sept., 2005.]
 

Command, Interpreter, Visitor

Design patterns are interesting in themselves, but it's useful to be able to move between patterns while refactoring a system. We'll look at how you can move between command, interpreter, and visitor.

Many interactive programs allow a user to perform one of a number of actions. The code for this tends to begins its life scattered around the system.

This usually shows up in one of two forms. In the first form, the code is in a big switch or if statement:

if (control.equals("Save")) {
      // save the file
} else if (control.equals("Cut")) {
      // cut the selection
} else {
      // etc. - on and on
}

This is bad form because the handler routine becomes a bottleneck for changes. Any new operation means changing code rather than adding code; thus changes in one feature might break completely unrelated ones.

The second form has the code attached to widgets. For example:

cutMenuItem.addMenuListener (new CutListener());
    :
public class CutListener {
    public void handle(MenuEvent e) { /* cut code */}
}

This code is better in that it keeps separate functions apart, but it's still flawed because GUI code tends to be intermingled with Model code.
 

Command

There are several forces that might make you wish for a more general solution:

  • A need to trigger the same action from multiple places (e.g., from the menu or from a button). The "CutListener" code above might work, except that it's customized for menus rather than some other widget type.
  • A need to support macros. This is a similar problem, in that there may be two things trying to perform an action: the original button click or the macro subsystem. The macro code can be even more complicated if it's dealing with a sequence of commands.
  • A need for "undo" support. Again, there are two places that might want to perform an operation: the original button or menu item, and the undo/redo subsystem.

The bits of "action" code tend to represent functions, but we may find it easier to "objectify" them. The Command pattern exists to do this.

Suppose we have this code:
        :
        Function A
        :
        Function B
        :
(Make sure to test after each step.)

  1. Create a parent Command class:
            Command {
            abstract void execute();
        }
  2. For each bit of code (e.g., Function A), introduce a new class that's a subclass of Command (e.g., ACommand):
            ACommand extends Command
    Do this one command at a time.
    Compile and test the new class.
  3. Copy the function code to the subclass' execute() method:
            ACommand {
            execute() { /* Function A body */ }
        }
  4. Replace the original site for function A:
            :
      new ACommand().execute();
        :
      Function B
        :
    Compile and test.
  5. Repeat for each function.
  6. Determine whether those call sites each need to create a new instance of the command (subclass) each time. You may find you can create an instance of the command and pass it around. (For example, a menu and button might be able to share the same instance of a command object.)

At this point, you're ready to introduce macros, redo, etc. as in the standard Command pattern.
 

Command and Composite

For macros, or just convenience, it can be convenient to build commands out of other commands. For example, some systems let the user record a sequence of commands as a saved action.

In this situation, make Command a Composite:
            public class ACommand extends Command {...}
            public class CompositeCommand extends Command {
                Vector commands; // etc. ...
                public void execute() {
                    // for each element c of commands, do c.execute()
                }
            }
The CompositeCommand represents a command that can hold and execute a sequence of other commands.
 

Interpreter

At this point, we've almost become an interpreter. There are three key differences:

  • Interpreter generally references an external context
  • The composite types are usually broadened to more than simple sequences (e.g., separate composites for "if" versus "repeat").
  • Interpreters typically don't support "undo", as the context becomes so big it's not feasible to hold all the state in memory.

How do you get a language into the form that Interpreter expects to work with? There are typically two approaches:

  • Programmatic: The caller is responsible for building the object structure it wants to interpret.
  • Parsed: There is a separate routine that takes a string (the code to be interpreted), and it is responsible for understanding the string so it can create the objects.

If you're setting out on a plan to invent a new language, you should first consider whether an existing interpreter could handle what you need (perhaps with a new library or some minor tweaking).
 

To Visitor

You have this:
        public class Command {
            abstract void execute();
        }
        public class ACommand extends Command ...
        public class BCommand extends Command ...
But you find there's another process that wants to look at the same nodes:
          public class Command {
            abstract void execute();
            abstract void display();
        }
Then another function comes along: it doesn't need to change the command structure, but needs to perform a new function. You get tired of updating all the command nodes again. You may decide to move to the Visitor pattern. (See Design Patterns by Gamma et al. for the gory details.)

  1. Create a Visitor class. For each command class that exists, give it an accept method:
        public void visit(ACommand) {}
      public void visit(BCommand) {}
    Compile and test.
  2. Add an accept() method to each Command node. Each one should have an implementation like this:
            public void accept (Visitor v) {
                v.visit(this);
            }
    Compile and test.
  3. For each function F on the Command nodes, create a subclass of Visitor, FVisitor. Copy its body from the corresponding command to the corresponding method. Compile and test.
  4. Create a new test of the new Visitor. It should create an instance of the visitor, and call the command's accept() method. Compile and test. Create more tests as needed. This should function exactly as the original. Compile and test.
  5. Working one at a time, turn all calls to the function into calls on the visitor. Compile and test.
  6. There should be nothing left calling the function. Delete it from each command class. Compile and test.
  7. Repeat for the other functions.

What you're left with is the new Visitor form.
 

Warning

Here's my warning: Use Visitor with care.

I've migrated to Visitor twice in my life. I've ended up ripping it back out both times.

Why? Because Visitor is not happy when the command structure changes. Even when I think I've got that nailed down, it turns out that the command structure is what changed, but I wasn't adding more functions.

Is Visitor a bad or useless pattern? No – it's chasing something important. I have it clustered with the idea of "double dispatch" (depending on two types to know what to do) and mixins (assembling code from small bits of functionality). I think of "futuristic" approaches like aspect-oriented programming [http://www.parc.xerox.com/csl/projects/aop/] or subject-oriented programming [http://www.research.ibm.com/sop/] as embedding visitor in a way that the system can automatically take care of changing the commands or the visitors. Visitor in Java feels about as clumsy as a simulated object in C: it can be done manually, but only in a disciplined way.
 

Returning

It's not hard to go back from Visitor to Interpreter:

  1. Work one Visitor at a time. Add a function to the command class corresponding to the Visitor. Compile and test.
  2. Copy the corresponding visitor method code to the method on the various Command subclass objects. Compile and test.
  3. Add new tests for the new command objects. Compile and test.
  4. Working one at a time, replace each Visitor call site with a call to the corresponding function. Compile and test.
  5. Once all uses of a Visitor are gone, delete it. Compile and test.
  6. Continue until all Visitors have disappeared.

Conclusion

We've demonstrated how code can be changed to use a Command pattern, then an Interpreter, and a Visitor, and back.

Resources

[Written 9-6-2000; minor edits 12-7-2000.]

From 0 to Composite (and Back Again)

In refactoring, you make small steps in changing code, to preserve semantics but improve other properties. This paper looks at how you might evolve software to use the Composite pattern, starting from nothing. (See Design Patterns by Gamma et al. for a description of the Composite pattern.)

0

In the simplest case, an object has no knowledge of another object. No references? No problem. The syntax for this situation is both simple and well-known.

1

There comes a day when an object learns about another object. In Java, this is done via a reference:

public class ClassA {
        ClassB member;
    }

Even a simple reference involves a number of decisions:

  • Protection. The general rule is to use the least public protection required (from private, package, protected, and public).
  • Static or member. If there's a single reference shared by all members of that class, it should be declared static. If each instance of the class needs its own reference, it's not static.
  • Initialization. You can initialize a value in the declaration, in the constructor, or via a member function. You want to maintain the validity of the reference. By setting it in the declaration or constructor, you can ensure that it moves from valid reference to valid reference (e.g., no chance of NullPointerException). You may pay a price in time or memory, though. If you don't set the value until some member function is called, you risk that a sequence of calls (in an unexpected order) can retrieve a null value.
  • Update. Some references are intended to be set once and treated as read-only after that. (The object referred to may change, but the reference itself will not.) These are usually best dealt with using declaration or constructor initialization. Other references are meant to change, and you'll provide some methods that have that effect. Note that it is harder to think about values that change than those that don't.

2 or a Few

If you have a reference to a different class, you normally create a separate member variable. If the second reference is of the same type as the first, you have to decide whether or not to make it separate. A second reference may presage a third, fourth, etc. – see the next section ("Many").

If you can only have 2 or 3 of something, just declare the new one(s) as a new field. For example, if you're dealing with a transfer between two accounts, there's one account being credited and one being debited. We see no reason this will evolve to n-way transfers, so two fields will suffice. You might make the same decision for a 2-d coordinate (x and y).

Many

Once you grow to two or more of something, you should consider allowing for an arbitrary number of them. Java has three alternatives:

  • Array. You can create an array of the type being referenced:
    ObjectX[] array = new ObjectX [count];

    (Recall that in Java the array and its contents are initialized separately, so this statement created an array of null references.) The nice part about using an array is that it contains objects of a known type. The downside is the requirement that you know the size of the array to allocate it, and it's difficult to change its size.

  • Collection. "Collection" is meant to cover all Java's generic collection objects: Vector, List, etc. In Java, containers are based on storing and fetching an Object. The user of the collection must know to which type to cast the result. (If Java had some sort of template or generic type facility, this cast would be unnecessary.) The benefit of using collections is that they provide flexible, variable-size containers, with a variety of performance characteristics. The downside is their requirement for casts.
  • Specialized collection object. Some collections need to maintain specialized information. In these cases, you might introduce a special type for the collection. For example, suppose you're creating a checkbook program that needs to track a number of transactions. You could maintain an array or Vector of these, but a set of transactions really represent a whole account. Since Accounts have other significant information attached to them (account number, total, etc.), we might have a new class Account with a list of Transactions.
    Account <>------* Transaction

    Now, other objects can maintain a reference to Account instead of to a bunch of Transactions. Account can use whatever collection method is most appropriate. (If collections are used, Account can hide the necessary casts – an example of Fowler's refactoring "Encapsulate Downcast".)

    The good side of a specialized object is that it creates a meaningful new object where we can attach behavior. It's more work than creating a generic collection, though. (See also M. Fowler's refactoring "Encapsulate Collection".)

Composite

A list of items may suffice for a long time, but you may eventually need a more complex structure.

Suppose you have a set of products, and someone chooses at most one of each, maintained in a list of current orders. One day, the marketing department gets the idea of having "bundles", where a bundle is a list of existing products. You can try to maintain the existing list structure, adding the products in the bundle into the "current order" list. The problem comes when a user wants to remove a bundle: you find you've lost track of what was in the bundle.

The crucial step to solving this is to say, "What if a bundle were another product?" Then we have products containing products. This sort of recursion screams out "Composite".

You might think you don't need the full complexity of composite – it allows bundles that contain bundles, and who would need that? That may or may not be true. It's certainly possible to conceive of a system that allows only products or bundles containing products (but not bundles containing bundles).

This situation is much like the 0-1-infinity rule that discourages using 2 or 3 explicit members: if you have two levels of products, odds are good that you'll have more. (You'll probably find it less complex to allow the recursion).

How to Move from Custom Collection to Composite

  1. Move your design to a custom collection type:
    List <>-----* Item

    Make sure the List has methods for add(Item), remove(Item), and getChild(int).  (Test.)

  2. Create a new parent class "Parent", with a protected constructor, that has the add(Item), remove(Item), and getChild(int) methods.  (Test.)  [The names of your classes should be appropriate for your domain; in the example, we might use "Product", "Bundle", and "Item".]
  3. Make Parent the superclass of Item. (Test.)
  4. Modify List's and Parent's methods to take and store instances of Parent (instead of Item). You may need to declare and stub out any methods from Item to Parent. The compiler will tell you which ones they are. (Test.)
  5. Make Parent the superclass of List. (Test.)
  6. Move the signature of any (remaining) operation common to both List and Item up into the parent. Provide a default implementation if possible. (Test.)
  7. For each method, ensure that it's implementation is reasonable. You may be able to make some of them abstract in the Parent. (Test.)
  8. Locate each reference to Item. See if it can make sense to operate on Parent instead, and generalize the type if possible. (Test.)
  9. Locate each reference to List. See if it makes sense to operate on Parent instead, and generalize the type if possible. (Test.)

And Back…

When do you move back from Composite to a collection?

  • When your objects are no longer in a meaningful hierarchy. (You have items and lists of items, but no lists of lists.)
  • When there are no shared operations (left) between lists and items.

If you have the data hierarchy of a Composite, but no shared operations, you're in a situation similar to a "data bag" or "struct class": you may want to observe what clients do with the class – perhaps there's functionality that belongs in the Composite rather than its client.

How to Move from Composite to Collection

  1. Modify the add(), remove(), and getChild() methods of Parent and List to operate on Item rather than Parent. The compiler will let you know if anybody is still trying to use "bundles of bundles". (Test.)
  2. Make sure List has all the methods of Parent. (Move an implementation down if necessary.) (Test.)
  3. Make List's superclass be the superclass of Parent. (Test.)
  4. Make sure Item has any methods it needs from Parent. (It shouldn't need the list methods.) (Test.)
  5. Make Item's superclass be the superclass of Parent. (Test.)
  6. Nobody should be referencing the Parent class any more. Remove it. (Test.)
  7. Review the operations shared by Item and List to see if any should be removed. (Test.)

…To 0

The remaining changes are straightforward:

  • Change a constant-size collection to use an array.
  • Move from a small array to explicit members.
  • Delete members until there's only one left.
  • Delete the last reference, and you're back to "0".

Summary

We've shown how a series of fairly small steps can move you through the path from 0 references to a Composite. This suggests that we are never "painted into a corner" if we start simpler, even if our data will grow to the complexity of the trees used in Composite.

We've also shown how to refactor the other way: from Composite down to nothing. This assures us that we can simplify our application if the requirements become simpler.

Resources

[Written 2-8-2000.]

Interface Seams: Patterns for Interactive Applications

These patterns describe some of the forces and decisions to be made while implementing an interactive application. [This is a submission to PLoP’98.]

Interactive Applications

Many applications that we think of as "killer applications" on personal computers, such as word processors or spreadsheets, developed before graphical user interfaces were common. While graphical interfaces have brought several benefits to these programs (better usability, scrolling, fancy fonts, and crisper graphics), the core models of these programs haven’t changed: word processors still deal with text, and spreadsheets with matrices of formulas.

Patterns are a literary form for capturing design knowledge. Developed by Christopher Alexander to describe architecture (the physical kind) [1], they have been adopted and adapted to describe software architecture [2] [3]. A pattern guides a decision or class of decisions that must be made, by describing a problem and its solution in context. A group of patterns is sometimes organized into a pattern language, which provides guidance through a series of decisions.

The pattern language described below attempts to generalize from applications such as Emacs [4, 5], WordStar [6], dBASE III [7], and VisiCalc [8], and to capture their essence. These patterns apply fairly early in the design, after task analysis and object-oriented analysis have identified key tasks, objects, and relationships.

In the Portland Pattern Repository [9], Kent Beck has articulated two related patterns: in Story, he notes that while stories are presented in time, the interface must be designed in space. In One Task Per Window, he provides guidance on how to associate windows with tasks. The patterns below are intended to weave into that same design space, and assume Beck’s patterns as a given.

This pattern language addresses three aspects of application design: appearance, behavior, and extensibility.

Design of Screens

Make Position Reflect Relationships

Problem: The user must deal with hundreds, thousands, or more related objects.

Context: Analysis has identified critical objects and relationships.

Forces: The user’s task requires focusing on a small number of abstract, key relationships.

Solution: Give each object a visual representation based on its attributes. Let the object’s placement on the screen reflect the key relationships.

It may be helpful to have a structuring metaphor, a simple guiding principle that lets the user build a mental model of how the system works.

Examples:

  • Emacs [4, 5]: the objects are characters, the key relationship is their sequence.
  • Spreadsheets [8]: the objects are formulas, arranged in a table.
  • SortTables [10]: the objects are records, arranged in sequence.

Related Patterns: Arrange Zones By Similar Persistence, Make Space Proportional to Importance, and Put Less Important Information on the Periphery discuss how to structure the screen. Beck’s Story and One Window Per Task [9] provide guidance on preserving key relationships between objects and the user’s task.

Related Ideas: Horton [11] discusses ways to arrange online documents to reflect a logical pattern.

Arrange Zones By Similar Persistence

Problem: There are many objects to show, but it is not clear how to share screen space among them.

Context: Screen-based interface.

Forces: Users need a consistent screen structure so they know what to expect.

Solution: Partition the window into zones. Items in a given zone should change at about the same rate. If possible, use dividing lines or other indicators to clearly delineate the zones.

Zones should have inviolate borders – they should not interfere with each other. (Popup menus and dialog boxes may be acceptable exceptions, provided they don’t cover information critical to their use.)

Zones should be arranged in a way that accords with Position Reflects Relationship.

Example: Spreadsheets typically have a strong zone structure: the cells, their labels, and a data entry box.

Counter-Example: Some versions of the Unix editor vi have a status/command line, but it gets covered by the user’s text when a command line is not needed. Text in that line is thus ambiguous.

Related Patterns: One Window Per Task [9]. Beck has alluded to a pattern Split Into a Small Number of Zones that is probably related.

Related Ideas: Publishing uses the notion of a grid in layout design, providing a stable visual format throughout a publication [12].

Make Space Proportional to Importance

Problem: How big should the various zones be?

Context: A window divided into zones.

Forces: Many objects compete for screen space.

Solution: Make the space for a zone proportional to the importance of the objects and relations in that zone.

Example: A spreadsheet devotes most of the screen space to the user’s data.

Counter-Example: At a local copy shop, the word processor is configured to show all indicators: a menu bar, a tool bar, font/size indicators, and other buttons. The screen is small, and the various indicators take up almost half the screen space, leaving little room for editing – the main task.

Related Ideas: Tufte has a rule for graphics: Maximize the data-ink ratio, within reason [13], meaning that ink should be spent providing useful information.

Put Less Important Information on the Periphery

Problem: What goes where on the screen?

Forces: People will tend to focus on the middle.

Solution: Put the biggest and most important zone in the middle. Put less important information surrounding it, on the edges. This information is usually about less important objects, or meta-information about the central objects.

Be aware of reading habits, however. Readers of English expect to look left to right and top to bottom.

Related Patterns: Make Position Reflect Relationships takes precedence over this pattern.

Related Ideas: Horton proposes a similar approach for online documents [11].

Design of Behavior

Computer Adds Value

Problem: The user is working too hard.

Context: A computerized solution is being designed. Forces:

  • The user would like the computer to do the tedious parts of the task.
  • Programming all parts of the task might be expensive or impossible.

Solution: Ensure that the computer adds value to the performance of the user’s task. Look for places where the computer can go beyond merely being a storage device, and can provide summary information, computation, automatic placement, analysis, and arranging.

Examples:

  • SortTables [10]: a self-sorting box of file cards.
  • Spreadsheet [8]: automatically updating formulas.

Let the User Control Navigation

Problem: The user gets confused when information scrolls past too quickly to read.

Forces: Users like to feel in charge of what they are doing and the pace of their work.

Solution: Let the user control navigation. Users should be able to move the focus of attention at their own pace. This navigation might be controlled by scrollbars and/or navigation keys (cursor keys, top/bottom, page-up/down).

Example: Most programs with graphical interfaces use scrollbars to control navigation.

Counter-Example: Some terminal emulators let text scroll past, without trying to save it.

Engender a Sense of Progress

Problem: The user needs a sense of how much work is done and how much is left to do.

Forces: It’s frustrating for a user not to know where they are.

Solution: Structure the visual layout to provide some indication of pr ogress. This may take the form of progress bars, counters, scroll bars, etc.

Example: The thumb of a scrollbar provides an indication of position as well as being a navigational widget.

Related Ideas: Thimbleby [14] briefly mentions " sense of progress" as a consequence of his equal opportunity design principle.

Support Lapses in Attention

Problem: Users forget what they’re doing, in the middle of doing it.

Forces: Real users are in an environment the computer is unaware of: phones ring, people drop by, and so on.

Solution: Attack the problem on several fronts:

  • Arrange Zones By Similar Persistence lets the user focus on what has changed since they last looked at the screen.
  • Let the User Control Navigation restricts what will happen without user involvement.
  • Modelessness prevents context-dependent situations.
  • If there must be modes, provide visual indicators of the current mode

Counter-Example: The vi editor has a command mode and a text entry mode, but no indication of which is active.

Related Ideas: Thimbleby [14] calls this the "gone for a cup of tea problem," and discusses how it interacts with modelessness.

Design for Extensibility

Two-Layer Architecture

Problem: We need several interfaces to a system (e.g., graphical, audio, application programming interface, etc.)

Forces:

  • Real systems must operate in many environments.
  • We can’t afford to develop completely independent versions of a system.
  • Users may require different interfaces at different times.

Solution: Use a two-layer architecture: define a core set of commands that do the work, and define bindings that map keystrokes or mouse actions to those commands.

Example: Consider an electronic mail system: we might like a dial-in audio version for use on the road, and a graphical version for use at the desk.

Use an Existing Extension Language

Problem: How to provide everything people want from the command set?

Forces:

  • Creeping featurism in the command set.
  • Language design shouldn’t be the focus of the application development.

Solution: When a command language (as in Two-Layer Architecture) reaches the point where control structures become necessary, there will be inexorable pressure to grow it into a full programming language. Thus, define the core commands as primitives, and embed them in an existing language such as TCL [15] or Lisp [16].

Example: Emacs [4, 5] has Lisp as an implementation and extension language.

Counter-Example: An early digital video developers’ toolkit provided some simple commands. They tried to grow these into a language, but it had strange restrictions (control structures could be nested only two layers deep, limited number of variables, etc.).

Conclusions

This series of patterns has been designed as a pattern language to help in developing screen-based applications, by identifying tradeoffs and pressures in screen design, behavior, and extensibility. Most of these patterns have strong precedents in the human-computer interaction and documentation fields. With the pattern form, we hope to capture more than just a slogan. We hope that the context and forces expose some design tradeoffs, and that the patterns can be used in a generative way, for a particular class of applications.

References

  1. Alexander, Christopher, et al. A Pattern Language: Towns, buildings, construction. Oxford University Press (New York), 1977.
  2. Gamma, E., Helm, R., Johnson, R., and Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley (Reading, MA), 1995.
  3. Coplien, J. O. and Schmidt, D. C., editors. Pattern Languages of Program Design. Addison-Wesley (Reading, MA), 1995.
  4. Stallman, R. EMACS: The Extensible, Customizable Self-Documenting Display Editor. In Proc. SIGPLAN/SIGOA Symposium on Text Manipulation, Portland, OR, ACM, 1981.
  5. Finseth, C. A. The Craft of Text Editing: Emacs for the Modern World. Springer-Verlag (New York), 1991.
  6. Ettlin, W. A. WordStar Made Easy. Osborne/McGraw-Hill, 1981.
  7. Weber Systems Inc., Staff. dBASE III Users’ Handbook. Ballantine Books (New York), 1985.
  8. Beil, D. H. The VisiCalc Book (Atari Edition). Reston Publishing Co. (Reston, VA), 1982.
  9. Beck, K. "User Interface" in the Portland Pattern Repository.
  10. Wake, W. C. and Fox, E. A. SortTables: A Browser for a Digital Library. Conference on Information and Knowledge Management, CIKM-95, ACM, 1995.
  11. Horton, W. Visual Rhetoric for Online Documents. IEEE Transactions on Professional Communication, 33(3), Sept., 1990, pp. 108-114.
  12. Swann, A. How to Understand and Use Design and Layout. North Light Books (Cincinnati, OH), 1987.
  13. Tufte, E. R. The Visual Display of Quantitative Information. Graphics Press (Cheshire, CT), 1983.
  14. Thimbleby, H. User Interface Design. ACM Press (New York), 1990.
  15. Ousterhout, J. K. Tcl and the Tk Toolkit. Addison-Wesley (Reading, MA), 1994.
  16. McCarthy, J. et al. LISP 1.5 Programmers Manual. MIT Press (Cambridge, MA), 1965.

Acknowledgements

Thanks to Dr. Edward A. Fox of Virginia Tech, for his encouragement and support in exploring this topic.


Copyright 1998, William C. Wake
William.Wake@acm.org
PLoP ’98 submission, 5-12-98

A Telephone Pattern Language

This document is the start of a pattern-language for phone-based systems.

  • GOMS – did they analyze phone systems?
  • Same key for same function everywhere (e.g., 9 to exit, # for enter).
  • Option for reaching a human – put it last.
  • Shortcuts? Speed-dial keys.
  • Added value. Rolm: tells who called. Multiple prompt levels. Timestamp on messages.
  • Describe an option, then give the number to dial. (This reduces short-term memory needs.)
  • Take advantage of processing to remove ambiguity. Example: phone that lets you type person’s name. Bentley has an example of this in a real phone directory.
  • Allow people to interrupt the machine.

Forces

  • Sequential nature of interface, makes more demands on STM than visual systems.
  • Limited keyboard
  • Low ‘balk’ level of callers

Examples/References

  • Grunt (Schmandt)
  • Elizabeth Mynar (sp?)
  • CHI from last few years
  • TOIS from last few years

Add Value

Context. Trying to automate a system with a phone-based interface.

Forces.

  • It is easiest to automate as little as possible.
  • If automation provides no value, people won’t use the system.

Resolution. Make the system add value as much as possible.

Discussion. An automated system (replacing a human operator) tips the balance very far towards reducing the value of the interaction. Automation should try to make up for this by trying to add value where it can.

From the caller’s side, automation is a tradeoff: it’s nice to be able to leave a message, but it’s frustrating not to reach the desired person. There are several common ways of adding value for the caller: the ability to leave a message, the chance of receiving "targeted" or "personalized" messages, notification when the callee is on the phone, directory assistance, and others.

For the callee (who is often the purchaser or at least the heavier user of the system), the system might provide timestamps, tell who called (by name or number), provide archiving of messages, or any number of other features.

[Idea: tie online systems & phone based systems.]

Designed Vocabulary

Context. Describing options in a PBI.

Forces.

  • Slow interaction speed
  • Range of usage frequency (some users are heavy, many users are occasional users)

Resolution. Use a limited vocabulary, chosen to match the set of tasks.

Discussion. Variety in expression may be a valid goal in some forms of writing, but it is not helpful in appliances. Pick particular words to refer to concepts, and use them consistently. (For example, if you "listen to message", you should also "record a message", not "make a recording".)

Minimize Expected Path Length

Context. Trying to arrange options and descriptions in a PBI.

Forces.

  • Many options are available.
  • Sequential interaction

Resolution. Minimize "path length times expected frequency" for the operations.

Discussion. Consider what operations are most common, and describe them first. This prevents people from having to listen to rarely chosen options before hearing the one they’re most likely to want. If an option’s frequency justifies it, you might provide special shortcuts for it.

This principle is well-established in performance engineering (see [Smith]) (as well as UI [Shneiderman??]).

Describe Then Prescribe

Context. Trying to word options in a PBI.

Forces.

  • People have limited short-term memory.

Resolution. Describe the option, then tell how to invoke it.

Discussion. The user is trying to do two things: choose a correct option to match their goal, and figure out how to invoke that option. If the phrasing is "Press 1 to leave a message", the user has to keep "Press 1" (the system’s goal) in mind while they decode and assess the option "leave a message" (their goal). By instead using a phrasing such as "To leave a message, press 1", they can match their task to the chosen option, without the distraction of preparing for the mechanics of the operation.

This approach can be carried further, listing potential options before describing their mechanics. "You can listen to this message, delete this message, or exit. To listen to this message, press 1. To delete this message, press 2. To exit, press 9 or hang up." This approach trades off a longer time of listening against two opportunities to choose a task.

Human of Last Resort

Context. Trying to automate a system with a phone-based interface.

Forces.

  • It is often easiest for a caller to just speak to a human.
  • If the automation is to reduce the operator’s load, users must find it preferable to mostly deal with the machine.

Resolution. Offer to connect to a human operator (if possible), but make this offer only after all other options have been listed. Use key ‘0’ to invoke this option.

Discussion. People hate "phone mail purgatory," where they just want to talk to someone (anyone!) who can help. Make sure they have a way to escape.

Consistent Keystrokes

Context. Trying to automate a system with a phone-based interface.

Forces.

  • A variety of options are available.
  • Users have limited memory and patience.

Resolution. Have at least some keystrokes that are context-independent, having the same meaning throughout.

Discussion. This lets occasional users quickly rebuild a model of what works. For example, you might consistently use ‘#’ for ‘Enter’, ‘9’ for ‘Exit’, and ‘0’ for ‘Operator’ in all contexts.