C# Corner

The Decorator Pattern in .NET

The decorator pattern is a good way to add extensibility to an application, as it follows SOLID design principles. Learn how to use it by adding validation logic to a form.

The decorator pattern is a common design pattern often used to add new functionality to an object dynamically. The main benefit of the decorator pattern is that the existing object's class is not directly modified. This follows the open-closed principle in the Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion design principles, or SOLID for short.

The components of the decorator pattern are the component interface, concrete component, decorator interface and the concrete decorator. The component interface defines the operations and properties for the component. The concrete component is the primary class you want to extend that implements the component interface. The decorator interface defines the contract for the functionality that will be added to the concrete component. The concrete decorator class implements the decorator interface and extends the component with the needed functionality.

To demonstrate the decorator pattern, I'll show you the common scenario of adding validation logic to a text box on a form. Using basic inheritance, you could define a TextBox class and then a subclass ValidationTextBox class to add the validation logic. This is fine, but what if you wanted to be able to easily add existing functionality to the class without creating a new derived class or modifying the old code? This can easily be accomplished by using the decorator pattern, as shown in the sample application accompanying this article.
To get started, create a new Blank Windows Store C# Project in Visual Studio 2012. Next, create a new directory named Components that will be used to store the component and component interface classes. Then create a new interface named ITextBoxComponent that will define the basic properties for a TextBox control. Then add a string type property named Text to ITextBoxComponent:

using System.ComponentModel;

namespace VSMDecoratorPattern.Components
{
  public interface ITextBoxComponent : INotifyPropertyChanged
  {
    string Text { get; set; }
  }
}
Now that the component interface has been defined, you'll add the TextBoxComponent concrete component class that implements the ITextBoxComponent interface. First, add the following using statements:
using System.ComponentModel;
using Windows.UI.Xaml.Controls;
Next, add the PropertyChanged event to the class:
public event PropertyChangedEventHandler PropertyChanged;
Then add a TextBox member field that will be used to construct the TextBoxComponent:
protected readonly TextBox _textBox;
In the class constructor, set the _textBox member variable:
public TextBoxComponent(TextBox textBox)
  {
    _textBox = textBox;
  }
The Text property simply gets or sets the Text property on the _textBox instance and calls the OnPropertyChanged method for the setter:
public string Text
{
  get { return _textBox.Text; }
  set
  {
    _textBox.Text = value;
    OnPropertyChanged("Text");
  }
}
The OnPropertyChanged event raised the PropertyChanged event to any subscribers:
protected void OnPropertyChanged(string name)
{
  PropertyChangedEventHandler handler = PropertyChanged;
  if (handler != null)
  {
    handler(this, new PropertyChangedEventArgs(name));
  }
}
For the complete TextBoxComponent class implementation, see Listing 1.

Listing 1. The TextBoxComponent class implementation.

using System.ComponentModel;
using Windows.UI.Xaml.Controls;

namespace VSMDecoratorPattern.Components
{
  public class TextBoxComponent : ITextBoxComponent
  {
    public event PropertyChangedEventHandler PropertyChanged;
    protected readonly TextBox _textBox;

    public TextBoxComponent(TextBox textBox)
    {
      _textBox = textBox;
    }

    public string Text
    {
      get { return _textBox.Text; }
      set
      {
        _textBox.Text = value;
        OnPropertyChanged("Text");
      }
    }

    protected void OnPropertyChanged(string name)
    {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(name));
      }
    }
  }
}

Now it's time to implement the decorator base and concrete classes. Create a Decorators directory within the project, then create a new class file and class named TextBoxDecorator that implements the ITextBoxComponent interface. When that's done, add the following using statements to the class file:

using System.ComponentModel;
using VSMDecoratorPattern.Components;
Next, add an ITextBoxComponent member variable and use it to construct the TextBoxDecorator:
protected readonly ITextBoxComponent _component;

protected TextBoxDecorator(ITextBoxComponent component) 
{
  _component = component;
}
Now implement the Text property that gets and sets the Text property on the _component object:
public virtual string Text
  {
    get { return _component.Text; }
    set
    {
      _component.Text = value;
    }
  }
Next, implement the OnPropertyChanged method that's the same as in the TextBoxComponent:
protected void OnPropertyChanged(string name)
{
  PropertyChangedEventHandler handler = PropertyChanged;
  if (handler != null)
  {
    handler(this, new PropertyChangedEventArgs(name));
  }
}

public event PropertyChangedEventHandler PropertyChanged;
See Listing 2 for the complete TextBoxDecorator implementation.

Listing 2. The TextBoxDecorator implementation.

using System.ComponentModel;
using VSMDecoratorPattern.Components;

namespace VSMDecoratorPattern.Decorators
{
  public abstract class TextBoxDecorator : ITextBoxComponent
  {
    protected readonly ITextBoxComponent _component;

    protected TextBoxDecorator(ITextBoxComponent component) 
    {
      _component = component;
    }

    public virtual string Text
    {
      get { return _component.Text; }
      set
      {
        _component.Text = value;
      }
    }

    protected void OnPropertyChanged(string name)
    {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(name));
      }
    }

    public event PropertyChangedEventHandler PropertyChanged;
  }
}

[Click on image for larger view.] Figure 1. An invalid e-mail that's been validated.

Now it's time to implement the ValidateTextBoxDecorator concrete decorator class. The ValidateTextBoxDecorator class enhances a TextBoxComponent by adding a Validate method and an ErrorMessage property. Create a new class file for the ValidateTextBoxDecorator class and add the following using statements to it:

using System.ComponentModel;
using VSMDecoratorPattern.Components;
using System.Text.RegularExpressions;
Next, add private member variables for the error message and regular expression validation pattern:
private string _errorMessage;
private readonly string _regExPattern;
After that, in the constructor, pass in a component and a regular expression pattern to use and subscribe to the PropertyChanged event of the component:
public ValidateTextBoxDecorator(TextBoxComponent component, string pattern)
  : base(component)
{
  _regExPattern = pattern;
  component.PropertyChanged += ComponentOnPropertyChanged;
}
In the ComponentOnPropertyChanged method, call the Validate method to perform the validation on the input text when it's updated:
private void ComponentOnPropertyChanged(
  object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
  Validate();
}
In the Validate method, a Regex object is created to perform the validation. If the validation check fails, the ErrorMessage property is set to "Validation Failed" and false is returned. If the validation check passes, the ErrorMessage property is cleared and true is returned:
public bool Validate()
{
  if (!Regex.IsMatch(Text, _regExPattern))
  {
    ErrorMessage = "Validation failed.";
    return false;
  }

  ErrorMessage = string.Empty;
  return true;
}
The ErrorMessage property sets the _errorMessage property and calls the OnPropertyChanged method in its setter:
public string ErrorMessage
{
  get { return _errorMessage; }
  private set
  {
    _errorMessage = value;
    OnPropertyChanged("ErrorMessage");
  }
}
See Listing 3 for the complete ValidateTextBoxDecorator class implementation.

Listing 3. The ValidateTextBoxDecorator class implementation.

using System.ComponentModel;
using VSMDecoratorPattern.Components;
using System.Text.RegularExpressions;

namespace VSMDecoratorPattern.Decorators
{
  public class ValidateTextBoxDecorator : TextBoxDecorator
  {
    private string _errorMessage;
    private readonly string _regExPattern;

    public ValidateTextBoxDecorator(
      TextBoxComponent component, string pattern)
      : base(component)
    {
      _regExPattern = pattern;
      component.PropertyChanged += ComponentOnPropertyChanged;
    }

    private void ComponentOnPropertyChanged(
      object sender, PropertyChangedEventArgs propertyChangedEventArgs)
    {
      Validate();
    }

    public bool Validate()
    {
      if (!Regex.IsMatch(Text, _regExPattern))
      {
        ErrorMessage = "Validation failed.";
        return false;
      }

      ErrorMessage = string.Empty;
      return true;
    }

    public string ErrorMessage
    {
      get { return _errorMessage; }
      private set
      {
        _errorMessage = value;
        OnPropertyChanged("ErrorMessage");
      }
    }
  }
}

Now it's time to implement the UI for the application. The user is able to copy the following XAML markup into the root <Grid> element in the MainPage.xaml:

<StackPanel HorizontalAlignment="Center" Name="InputPanel">
  <TextBlock>Input Field</TextBlock>
  <TextBox Name="InputField" Text="{Binding Text, Mode=TwoWay}" MinWidth="200"></TextBox>
  <TextBlock Name="ValidationMessage" Text="{Binding ErrorMessage, Mode=TwoWay}" 
    Foreground="Red"></TextBlock>
</StackPanel>
Next, open up the MainPage class file. In the OnNavigatedTo method, construct a TextBoxComponent and then upgrade it to a validation text box that performs e-mail address validation through use of the ValidateTextBoxDecorator class:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
  var textBoxComponent = new TextBoxComponent(InputField);
  var validationTextBox = 
    new Decorators.ValidateTextBoxDecorator(textBoxComponent,
    @"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*
    [a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$");
  InputPanel.DataContext = validationTextBox;
}

You should now be able to run the completed application and check for an invalid e-mail, as seen in Figure 1, or a valid e-mail address, as seen in Figure 2.

[Click on image for larger view.] Figure 2. An valid email, showing validation.

Expanding on this concept, you see that you can easily extend the TextBox class by adding additional decorator classes for needed functionality. Better yet, the pattern leads you toward class composition, which is in most cases easier to maintain than a class hierarchy.

The decorator pattern is a useful design pattern to have in your toolset when tackling application extensibility. The pattern is well-suited for adding new behavior to a component without changing the existing class, and is a good way to follow the open-closed principle.

About the Author

Eric Vogel is a Senior Software Developer for Red Cedar Solutions Group in Okemos, Michigan. He is the president of the Greater Lansing User Group for .NET. Eric enjoys learning about software architecture and craftsmanship, and is always looking for ways to create more robust and testable applications. Contact him at [email protected].

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube