| By Jochen Krebs | Article Rating: |
|
| November 28, 2006 09:00 AM EST | Reads: |
21,115 |
Object-oriented software engineering (OOSE) without design patterns is like cooking without a recipe. Patterns guide us with ingredients and step-by-step instructions for assembling the solution to a recurring problem. In the same way we rely on recipes in cooking, we experience patterns as repeatable, proven solutions, and software engineering becomes more reliable and successful.
As in the culinary arts, where chopping and cutting techniques are prerequisites for mixing and flavoring dishes, there are many design patterns for all sort of challenges - basic, intermediate, and advanced - depending on your needs. However, food recipes often contain references to other recipes that go well with the main dish, thus enhancing the entire meal.
This article will focus on exactly these pattern relationships, combinations, and variations. It's all part of an emerging trend we might call "pattern-driven software engineering." The examples I provide are visualized in UML and would eventually be transformed into code (e.g., Java). Because patterns do not only affect the structure and dynamics of classes and objects, this article will conclude investigating the role of patterns in a service-oriented architecture (SOA).
The Concept of Patterns
Patterns emerge as software engineers begin to notice recurring problems. If you design software and you face a situation in which you ask yourself, "Gee, I can't be the first person facing this problem!" - your search for a pattern has just begun. Once you find and apply a pattern, your solution will not only benefit from the knowledge gained in the past, but this pattern might also open a door to related patterns. An individual pattern works in its described context and offers a variety of related patterns that can improve the quality of your solution even more. Eventually, one design could be a starting point for an entire pattern-driven design process.
Before we discuss the relationships among patterns, let's explore that culinary metaphor a bit and take a look at some individual patterns.
I'll describe a typical TV cooking show to help explain software patterns and their relationships. The goal of the show is to demonstrate the preparation of a specific meal. On most cooking shows, however, we find cups and bowls in front of the chef, with ingredients such as onions already prepared. That's because the expert cook doesn't need to illustrate the chopping of onions in front of the TV audience; it would be boring. Prior to the taping of the show, the chef has probably asked his subordinates for some quantity of "finely chopped onions," the same ingredients used in many recipes. What's important here is that the chef doesn't need to communicate the actual cutting technique; he simply asks for the well-known result, a standard cup of chopped onions.
Software engineers make use of such basic patterns, too. Some of these patterns, such as the General Responsibility Assignment Software Patterns (GRASP) (Applying ULM and Patterns by Craig Larman), are so fundamental that many other patterns make use of them. Basic design patterns organize and control communication or creation, or they establish visibilities among objects. Basically, in an object-oriented system, objects communicate with each other through messages. Therefore all these messages (a.k.a. responsibilities) need to be assigned by the software engineer to build a flexible and maintainable system. Based on that fact, object-oriented software engineers constantly ask themselves the same basic question: "Who should talk to whom?".
The Problem Scenario
For the remainder of this article, I will illustrate various approaches to pattern usage through the scenario of a change request to a timesheet application, where the change has to do with the timesheet approval process. Figure 1 shows a typical situation for an object-oriented designer, where a specific business rule requires identifying whether the timesheet is approved or not. The question ("Are you approved?") and the answer ("yes" or "no") are determined, but the questions remain: who should receive and who should send the message?
Even for very basic design situations like the one described in Figure 1, we can make use of fundamental design patterns; for example, asking the GRASP patterns for help.
In the TV cooking show, the chef is using a fundamental pattern - chopped onions - to assemble a more complex pattern of his own, the meal itself. The level of the pattern has been elevated from a single set of techniques to a dish that comprises other fundamental techniques. The recipe has a name; for example, tomato sauce. It is the chef's responsibility to decide how many onions he uses and how he prepares them. The problem now moves to a higher level, from chopping onions to making a good tomato sauce. The chef begins applying his own pattern, the recipe, which contains other patterns (for sautéing, chopping parsley, etc.). The experienced chef applies a pattern, in a sense, as a way to present food nicely, focusing on color, texture, and style.
Software design patterns are not different. In addition to the fundamental GRASP patterns, engineers make use of more elevated patterns, such as Gang of Four (GoF) (Design Patterns by Gamma, Johnson, Helm and Vlissides) or architectural patterns. Now that most software engineers graduating from universities are grounded in OO principles, the software development industry has begun to raise the level of pattern adoption from the level of problem-solving techniques to problem-prevention techniques. I will use the Design Patterns -Reusable Objects (from the Gang of Four) as a design pattern catalog to demonstrate the pattern relationships and use the IBM Rational Software Architect (RSA) pattern catalog to illustrate the examples.
Let's get back to our initial scenario illustrated in Figure 1, in which we plan to build a timesheet application with a focus on an approval process. The designer needs to identify whether a timesheet is approved or not. In this case, it seems almost enough to simply add an attribute called is Approved to the Timesheet object, which contains one of the boolean values, true or false. The problem with this solution is, however, that the attributes of the object can change, and depending on the content of the attribute we would need to determine the type of message that will be fired. If we want to add another option - for example, Submitted - the boolean attribute, which allows two possible values true or false, does not accommodate this design approach anymore. With the introduction of the Submitted state the original design (built for two values) would break and the entire business logic would require us to reevaluate our initial design.
Later, I will demonstrate how smooth the transition can be from a two-states design to a three-states design, when patterns are applied. As illustrated in Figure 2, our new design approach would violate two fundamental design patterns, Expert and Polymorphism (according to Larman, op. cit.) and would unnecessarily couple one object with the business logic that belongs to another object.
The boolean value approach would not only violate fundamental design patterns, it would also increase the maintenance burden for software engineers because the design for the Timesheet object could easily break and the entire object would need to be retested with every change.
Translating the UML design from the code below would generate a Java structure like this example, violating Expert and Polymorphism.
....
if (b == true)
{
ts.doSomething();
}
if (b == false)
{
ts.doSomethingElse();
}
....
One Solution: The State Pattern
The GoF pattern catalog offers a possible solution for our design challenge. The pattern is called State.
First, we verify that the pattern meets our needs; then, we read the intend, application, and consequences sections of the pattern. Because the pattern says that it "Allows an object to alter its behavior when its internal state changes. The object will appear to change its class [GoF]," we go ahead and apply this pattern to our problem.
One of the benefits of applying the State pattern is that it can resolve the if-statement situation difficulty shown in Figure 3 by isolating the various states. The UML state-machine notation helps us depict and investigate the various states. Initially our timesheet was fairly simple and we isolated two states out of our existing structure, Approved and Not Approved, as shown in Figure 3.
Instead of asking the object which value is nested in an attribute (in our case is Approved) and make a decision based on that (which violate the principle of polymorphism) we instead tell the object what to do and simply send the message to it and let the Timesheet object deal with the event. What we would like to design is some way to send a message, as shown below, where ts is a Timesheet object. This is a new responsibility assignment for timesheet (Java)
....
ts.approve();
....
After we isolate the various states, remove the if-construct from the Timesheet object, and assign the three responsibilities (enter, approve, and reject), we then want to apply the State pattern to our solution. Using the RSA pattern explorer we navigate to the State pattern (See Figure 4), which shows us the participating classes in the pattern.
In order to get an overview of the structure of the State pattern, the pattern explorer provides us the layout shown in Figure 5.
The cookie-cutter solution for the State pattern needs to be adjusted to accommodate our application's specific needs. After dragging the pattern from the pattern explorer directly into our workspace, we can assign the participating classes from our application-specific class model. Figure 6 shows the Timesheet as a context object, the Java interface ITimesheetState for the State and both concrete states from our timesheet application (Approved and Not Approved).
The dynamics of this pattern are shown in the code below, using Java. After the message approve() has been sent to the Timesheet object, it takes the message and delegates it to its state and provides a pointer back to itself (the this-parameter). Below is the message delegation from the context to the State Object.
....
state.approve(this);
....
After the message approve(this) has been sent, the state which at runtime is located in the State object will handle the event (which is truly polymorphic). For example, the Not Approved state would implement the approve(ITimesheetState state) message. Below is the concrete state method implementation - Not Approved.
public void approve(Timesheet ts)
{
ITimesheetState newState = new ApprovedState();
ts.setState(newState);
}|
To support the polymorphic approach, we need to assign the approve responsibility also to the Approved state, as shown below, even though we will not do anything in this particular situation. The code below shows the Concrete state method implementation - Approved.
public void approve(Timesheet ts)
{
// do nothing|
}
Now that the State pattern[GoF] has been applied, let's see what happens in our one-pattern design if a requirements change occurs: for example, stakeholders need to be able to submit their timesheet after the time has been entered, and request approval. The following state machine diagram in Figure 7 shows two new states, Entered and Submitted, which replaced the previous state Not Approved to accommodate this requirement change.
The UML Design Class diagram in Figure 8 depicts the changes caused by the new requirement to the class model. Even though new state classes and messages have been introduced and one state has been removed, the changes are still very manageable. The most important point to be made is that the Timesheet has not been changed at all. It still keeps passing all the messages it receives to its actual state. That is a tremendous improvement to our if-else construct from the Java example,violating Expert and Polymorphism, because the area of concern from a testing perspective has shifted away from the Timesheet object to its states.
Pattern-Driven Development
In the previous section, I illustrated a design problem, applied a common solution (the State pattern) to it, and pointed out the advantages of the design using the pattern (maintainability and flexibility). In a pattern-driven solution, a designer will not only apply a pattern when a problem occurrs, but will drive the entire design by using patterns. This approach is slightly different, because it assumes that the designer works actively with design pattern catalogs and uses the relationships between those patterns. The patterns within a catalog are usually grouped according to a chosen template. The GoF pattern template, for example, has Name and Classification, Intent, Also Known As, Motivation, Applicability, Structure, Participants, Collaborations, Consequences, Implementation, Sample Code, Known Uses and, last but not least, Related Patterns.
Published November 28, 2006 Reads 21,115
Copyright © 2006 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Jochen Krebs
Jochen (Joe) Krebs (http://www.jochenkrebs.com) is a Senior IT Specialist for the Rational Software Brand within the IBM Software Group. He is responsible for successful enablement of Rational products and services for clients in the financial sector. Prior to joining IBM Rational he worked as an Instructor and Senior Consultant with a focus on project management, requirements management, software engineering processes and object-oriented technologies using Smalltalk and Java. He holds his MSc in Computing for Commerce and Industry at the Open University.
![]() |
Costa Gino 12/05/06 06:50:07 PM EST | |||
Object-oriented software engineering (OOSE) without design patterns is like cooking without a recipe. haha...interesting analogy.... well then enterprise software engineering is challenged to solve unique issues... and there couldnt be real recipes in it anyway.... |
||||
- It's the Java vs. C++ Shootout Revisited!
- Patterns for Building High Performance Applications
- Asynchronous Logging Using Spring
- Java for Programmers (2nd Edition)
- Cross-Platform Mobile Website Development – a Tool Comparison
- Three Buzzwords That Every CIO Hears but One They Should Listen To
- Write Once Run Anywhere or Cross Platform Mobile Development Tools
- Immersing into JavaScript Frameworks
- Workday Reportedly Prepping to Go Public
- Cloud Expo New York: The Java EE 7 Platform - Developing for the Cloud
- Book Review: Sams Teach Yourself Java in 24 Hours
- OpenOffice.com Lives
- Book Excerpt: Introducing HTML5
- Adobe Sends Flex to the Apache Foundation
- Five Years Waiting for JRE 7: Is It Justified? (Part 1)
- Book Excerpt: Java Application Profiling Tips and Tricks
- i-Technology in 2012: Five Industry Predictions
- It's the Java vs. C++ Shootout Revisited!
- Patterns for Building High Performance Applications
- OpenXava 4.3: Rapid Java Web Development
- The Next Web Architecture
- Asynchronous Logging Using Spring
- Java for Programmers (2nd Edition)
- Is Write Once Run Anywhere Ever Going to Be a Reality?
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- JavaServer Faces (JSF) vs Struts
- The i-Technology Right Stuff
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- Why Do 'Cool Kids' Choose Ruby or PHP to Build Websites Instead of Java?
- What's New in Eclipse?
- i-Technology Predictions for 2007: Where's It All Headed?
















