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

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube