Architecture & DesignApplying Design Patterns to Solve Design Issues

Applying Design Patterns to Solve Design Issues

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

From the beginning, software systems have been built to address specific points of pain in a given problem domain. As long as the software engineers have a clear understanding of the problem and the requirements, the system can be built to meet the needs and expectations of the users. Once the system has been released and is in the hands of the users, successful systems need to evolve to meet changing requirements. The effort required by the engineers to maintain the system is directly affected by the original design. If the original design does not accommodate change, the effort required to maintain the system increases because the new requirements will inevitably alter the design intent. Eventually, it will become difficult for the engineers to add new code because the responsibility of the individual modules becomes vague and this leads to low cohesion and a high degree of coupling.

Design Patterns exist as a means to capture best practices and to pass along time-tested solutions to commonly recurring problems in software development. Many Design Patterns have been published over the years. They differ in intent and the kinds of problems they are intended to solve. The solution lies in establishing the structure and responsibilities of the individual classes and objects that make up the pattern. The main point, however, is that patterns help to identify and isolate the system-level variability so the software can evolve over time without adversely affecting the underlying design structure.

This fundamental goal leads to three basic strategies for creating good object-oriented designs. They are:

  • Design to an interface
  • Favor composition over inheritance
  • Identify and encapsulate system variability

In fact, these basic strategies are incorporated into most Design Patterns and account for their overall effectiveness.

To illustrate the basic idea of design patterns and to point out how some of these strategies can be useful, this article will use a simple Windows Form application to illustrate how a design pattern can be applied to make an application more robust to the inevitable changes that occur as a system evolves.

Scribble is a simple application provided along with the .NET Framework as a sample to demonstrate how to build a simple window application. The user interface is shown below.

The UML diagram for Scribble is displayed below. Some of the menu items and toolbar buttons have been removed to simplify the UML diagram (by removing some of the attributes and operations within the MainWindow class). This modification does not affect the main point of the article.

The diagram shows the application consists of four classes:

  • MainWindow: Responsible for the user interface
  • ScribbleView: Responsible for the document view
  • ScribbleDoc: Responsible for the document
  • Stroke: Responsible for the stroke settings

The first thing to note is that MainWindow has many members. This is due to the number of menu items and toolbar buttons, all with their corresponding operation. These class members are responsible for the user interface behavior. The application implements event handlers for the menu items and toolbar buttons, respectively, to respond when the user clicks on a control. This design is simple and straightforward. The drawback, however, is that when new menu items and toolbar buttons are added later to support new features, such as undo and redo, the class needs to be modified to provide an implementation. Worse yet, if the ScribbleView, ScribbleDoc, and Stroke objects are enhanced, MainWindow must also be enhanced to take advantage of the new features. A relatively simple design can lead to objects that become complex and difficult to maintain because of the increased coupling between objects.

One way to prevent high coupling between MainWindow and the other objects is to add an “indirection layer” between them. The Command Pattern can be used to refactor MainWindow to minimize coupling. The intent of Command is to encapsulate a request as an object, and the request can be passed as a parameter rather than having the client implement the request directly. In essence, this means we can remove from MainWindow any knowledge of how the server objects are implemented.

The Command pattern, as discussed in Summary of Design Patterns, defines two classes that handle user requests. They are Command and Invoker. These classes are responsible for providing an implementation for the request and a method by which the command can be invoked. Moving these responsibilities out of MainWindow means this object no longer needs to know anything about implementing the request. It will need only to be responsible for responding to the click event. In our example, the user interface requests such as Save(), New(), and so forth, are implemented as Command objects. These objects are called by an Invoker object, which represents the individual MenuItem objects.

Applying Command to our Scribble application leads to the design shown in the figure below.

At first, it appears that the design is more complex than it was previously. This is not really the case if you are familiar with the Command pattern, because the intent of the new classes becomes obvious, and this makes the design easier to understand. The main benefit, however, is now MainWindow does not need to know how to implement the requests behind the user interface controls.

To further illustrate the point, consider the MenuItemHandler method as it was originally implemented.

  //Handle the Menu Item clicks
  private void MenuItemHandler(object sender, System.EventArgs e)
  {
    if(sender==menuItemNew)
    {
      New();
    }
    else if(sender==menuItemOpen)
    {
      Open();
    }
    else if(sender==menuItemSave)
    {
      Save();
    }
    else if(sender == menuItemAbout)
    {
      AboutHelp();
    }
    else if(sender == menuItemExit)
    {
      Exit();
    }
    else if(sender == menuItemClose)
    {
      CloseView();
    }
    else if(sender == menuItemSaveAs)
    {
      Save();
    }
    else if(sender == menuItemNewWindow)
    {
      NewWindow();
    }
    else if(sender == menuItemPenWidths)
    {
      PenWidthsDlg();
    }
    else if(sender == menuItemThickLine)
    {
      ThickPen();
    }
    else if(sender == menuItemToolbar)
    {
      toolBar1.Visible  = menuItemToolbar.Checked
                        = !toolBar1.Visible ;
    }
  }

This method is called when a user clicks on a menu item. The handler needs to determine which menu item was clicked and then invoke the corresponding method implemented within the same class.

Now, let’s examine the same method in the modified design, which is shown below.

//Handle the Menu Item clicks
  private void MenuItemHandlern(object sender, System.EventArgs e)
  {
    MenuItemScribble mi = (MenuItemScribble) sender;
    mi.DoCommand();
  }

Applying the Command Pattern resulted in a much simpler event handler. Now the MenuItemScribble object is responsible for invoking the command object’s execute method. MainWindow knows which MenuItemScribble object was clicked and the handler simply calls that object’s DoCommand() method. Therefore, MainWindow does not need to know how to respond to the user request. It simply responds to any user request by invoking the DoCommand() method, and it becomes the responsibility of MenuItemScribble to invoke the Command object’s Execute() method. Now, if we need to enhance Scribble, we can do so by adding new features to the server objects and then defining additional concrete command classes to implement these new features on behalf of MainWindow.

This illustrates how a design pattern can be used to simplify the responsibilities of an object and to identify and isolate the changes that may occur over time.

To illustrate the benefits of designing to an interface, let’s examine how MenuItemScribble handles the request. The class definition is shown below.

public class MenuItemScribble : MenuItem
  {

    public MenuItemScribble(string text, EventHandler ea,
                            ICommand cmd, MainWindow win) :
                            base(text, ea)
    {
      mMenuItem = new MenuItem(text, ea);
      mCmd = cmd;
      mHwnd = win;
    }

    public void DoCommand()
    {
      mCmd.Execute(mHwnd);
    }

    // Attributes
    private MenuItem mMenuItem;
    private ICommand mCmd;
    private MainWindow mHwnd;
  }

The class declares an instance of the ICommand interface, and a method to invoke a command object’s Execute() method. The command object is passed into MenuItemScribble’s constructor. By employing the ICommand interface instead of a concrete class, the MenuItemScribble class can invoke the Execute method of any command object, regardless of which concrete object is passed into the constructor. This means that the MenuItemScribble object can invoke any command object, even the ones that do not as yet exist. This has the added benefit of requiring only one MenuItemScribble class definition, and the command object that is passed in when the MenuItemScribble object is instantiated can be determined at runtime. This is a very flexible design construct that we can use to easily build in the capability for Scribble to alter the behavior when a user clicks on a menu item in order to accommodate the state of the client.

In conclusion, Design Patterns are powerful techniques to be used to simplify a software design so it becomes much easier to maintain and extend the application in the future, often at lower cost to the user.

About the Author

James Maioriello, Ph.D., is a Senior Design Engineer for Formation Systems Inc., a corporation located in Massachusetts dedicated to the development of software systems for product development in the processing industries. His position allows him to use his expertise and skills to design solutions for chemists and other scientists so they can focus their talents on innovation. Dr. Maioriello obtained degrees in Chemistry from Lehigh University and in Software Engineering from Polytechnic University in New York.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories