Decorator – Add new behaviors to the object without modifying the object architecture and keep existing behaviors.
From Design Patterns – Structural Patterns
Structure

Applicability
- When you need to assign different behaviors to objects at runtime without breaking the code which uses these objects, the project design pattern solution should use the decorator pattern.
- It’s awkward or not possible to extend an object’s behavior using inheritance.
Pros and Cons
PROS
- Extending an object’s behavior without creating a new sub-class.
- Add/remove responsibilities from an object at runtime.
- Combine several behaviors by wrapping an object into multiple decorators.
- Single Responsibility Principle – A monolithic class implements many possible variants of behaviors into several smaller classes.
CONS
- It will be hard to remove a specific wrapper from the wrapper’s stack.
- It will be hard to implement a decorator in everyone’s way, which isn’t dependent on the order in the decorators’ stack.
- The initial configuration code of layers may look pretty ugly.
How to Implement
#1 Basic Sample
class Program
{
static void Main(string[] args)
{
var Simple = new ConcreteComponent();
Console.WriteLine("RESULT: "+Simple.WriteData());
ConcreteDecoratorA ObjA = new ConcreteDecoratorA(Simple);
ConcreteDecoratorB ObjB = new ConcreteDecoratorB(ObjA);
Console.WriteLine("ConcreteDecoratorA part:");
Console.WriteLine(ObjA.WriteData());
Console.WriteLine("ConcreteDecoratorB part:");
Console.WriteLine(ObjB.WriteData());
Console.ReadKey();
}
}
abstract class Component
{
public abstract string WriteData();
public abstract string SendData();
}
class ConcreteComponent : Component
{
public override string SendData() => $" ConcreteComponent-SendData";
public override string WriteData() => $" ConcreteComponent-WriteData";
}
class Decorator : Component
{
protected Component _component;
public Decorator(Component Component) => this._component = Component;
public void SetComponent(Component Component) => this._component = Component;
public override string SendData()
{
if (_component != null)
return _component.SendData();
else
return string.Empty;
}
public override string WriteData()
{
if (_component != null)
return _component.WriteData();
else
return string.Empty;
}
}
class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(Component comp) : base(comp)
{
}
public override string SendData()
{
return $" ConcreteDecoratorA - {base.SendData()}";
}
public override string WriteData()
{
return $" ConcreteDecoratorA - {base.WriteData()}";
}
}
class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(Component comp) : base(comp)
{ }
public override string SendData()
{
return $" ConcreteDecoratorB - {base.SendData()}";
}
public override string WriteData()
{
return $" ConcreteDecoratorB - {base.WriteData()}";
}
}
#2 Web Application Project
In the decorator pattern’s article, the .Net Core web application’s solution project adds the service layer concepts, which will add to the Core project. The web project only registers the decorator pattern’s service layer in the Startup.cs file.
Step 1 Create the generic type repository in the project which inherits the generic type repository interface’s GET method, which name is “GetById.”
Step 2 Create four class files in the Core project, such as the Component.cs file, the ConcreteComponent.cs file, the ConcreteDecoratorNotification.cs file, and the Decorator.cs file.
- Component defines the interface objects, which have responsibilities added to them dynamically.
- ConcreteComponent defines an object to which additional responsibility, which can be added.
- Decorator, which maintains a reference to a Component object and defines an interface that conforms to Component’s interface.
- ConcreteDecortatorNotification, which adds responsibilities to the Component.
The Component.cs file code shows below:
namespace Enterprise_Dot_Net_Core_WebApp.Core.DesignPatterns.Decorator
{
public abstract class Component<T> where T : class
{
public abstract string ReadJsonData(int? id);
}
}

The ConcreteComponent.cs file code shows below:
using Enterprise_Dot_Net_Core_WebApp.Core.Interface;
using Newtonsoft.Json;
using System;
namespace Enterprise_Dot_Net_Core_WebApp.Core.DesignPatterns.Decorator
{
public class ConcreteComponent<T> : Component<T>, IDisposable where T : class
{
protected IGenericTypeRepository<T> _igenericTypeRepository;
public ConcreteComponent(IGenericTypeRepository<T> GenericTypeRepository)
{
this._igenericTypeRepository = GenericTypeRepository;
}
public override string ReadJsonData(int? id)
{
try
{
if (_igenericTypeRepository != null && id != null)
return JsonConvert.SerializeObject(_igenericTypeRepository.GetById(id.Value).Result);
else
return string.Empty;
}
catch (Exception ex)
{
return $"Concretecomponent -err: {ex.Message}";
}
finally
{
Dispose();
}
}
public void Dispose() => GC.SuppressFinalize(this);
}
}

The ConcreteDecoratorNotification.cs file code shows below:
using System;
namespace Enterprise_Dot_Net_Core_WebApp.Core.DesignPatterns.Decorator
{
public class ConcreteDecoratorNotification<T> : Decorator<T>, IDisposable where T : class, new()
{
public ConcreteDecoratorNotification(Component<T> comp) : base(comp)
{ }
public override string ReadJsonData(int? id)
{
SendMessageToFB(base.ReadJsonData(id.Value));
SendMessageToLine(base.ReadJsonData(id.Value));
// Add other behavior.
return base.ReadJsonData(id);
}
/// <summary>
/// Send messages to FB.
/// </summary>
/// <param name="Messages">String</param>
/// <returns></returns>
private static bool SendMessageToFB(string Messages)
{
try
{
if (!string.IsNullOrEmpty(Messages))
{
// Execute the Facebook API post
return true;
}
else
return false;
}
catch (Exception ex)
{
return false;
}
}
/// <summary>
/// Send messages to Line.
/// </summary>
/// <param name="Messages">String</param>
/// <returns></returns>
private static bool SendMessageToLine(string Messages)
{
if (!string.IsNullOrEmpty(Messages))
{
// Execute the Line API Post
return true;
}
else
return false;
}
public new void Dispose() => GC.SuppressFinalize(this);
}
}

The Decorator.cs file code shows below:
using System;
namespace Enterprise_Dot_Net_Core_WebApp.Core.DesignPatterns.Decorator
{
public class Decorator<T> : Component<T> where T : class
{
protected Component<T> _component;
public Decorator(Component<T> Component) => this._component = Component;
public void SetComponent(Component<T> Component) => this._component = Component;
public override string ReadJsonData(int? id)
{
try
{
if (_component != null && id != null)
return _component.ReadJsonData(id.Value);
else
return string.Empty;
}
catch (Exception ex)
{
return string.Empty;
}
finally
{
Dispose();
}
}
public void Dispose() => GC.SuppressFinalize(this);
}
}

Step 3 Create the service file in the Core project, which name sets “Decoratorservices.”
using Enterprise_Dot_Net_Core_WebApp.Core.DesignPatterns.Decorator;
using Enterprise_Dot_Net_Core_WebApp.Core.Entities;
using Enterprise_Dot_Net_Core_WebApp.Core.Interface;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Enterprise_Dot_Net_Core_WebApp.Core.Services.DesignPatterns
{
public class DecoratorServices
{
private IGenericTypeRepository<Enterprise_MVC_Core> _repo;
public DecoratorServices(IGenericTypeRepository<Enterprise_MVC_Core> Repo)
{
this._repo = Repo;
}
public async Task<string> Notification(bool bSwitch,int? id, CancellationToken cancellationToken)
{
try
{
var Sample = new ConcreteComponent<Enterprise_MVC_Core>(_repo);
var Messages = Sample.ReadJsonData(id.Value);
ConcreteDecoratorNotification<Enterprise_MVC_Core> _concreteDecoratorNotification = new ConcreteDecoratorNotification<Enterprise_MVC_Core>(Sample);
var MessageFB = _concreteDecoratorNotification.ReadJsonData(id.Value);
return await Task.FromResult(MessageFB);
}
catch (Exception ex)
{
cancellationToken.ThrowIfCancellationRequested();
return await Task.FromResult(ex.Message);
}
finally
{
Dispose();
}
}
public void Dispose() => GC.SuppressFinalize(this);
}
}

Step 4 Create the Controller files and the View part’s index page in the web project. The Controller file name sets “DecoratorController.” The DecoratorController.cs file only calls the Decoratorservices.cs’s method and doesn’t call other class files, including additional service class methods.

Step 5 The web project’s startup.cs file only registers the services class file without other files. It will be easy to write and understand source code in the web project.
// Decorator
services.AddScoped(typeof(DecoratorServices));
Reference
1 Comment