SRP example - The decorator pattern

For the practical demonstration of SRP, I will provide you with a solid example using a GoF design pattern. The Decorator pattern is one of the structural design patterns that dynamically adds/overrides behavior in the existing method of an object. It allows functionality to be divided between classes with unique areas of concern, that is, single responsibility. The behavior can be added to an individual object either statically or dynamically without affecting the behavior of other objects from the same class.

Consider the following UML diagram:

UML diagram for the Decorator pattern

The preceding figure depicts the UML diagram for the decorator pattern. I will, instead, provide the example where a decorator pattern is applied, which will help you practically understand this UML as well as the single responsibility principle by looking at the additional changes/behavior added separately instead of modifying the same class again.

In the real world, you sometimes face a scenario where you need to change the database from the SQL server to Oracle or vice versa. Let's say you have a persistence functionality written in a class and you either may change the code in the class or follow the SRP and design in a way that the new persistence functionality is handled by another class without modifying the original class. To achieve this, we follow the decorator pattern in our tiny example persistence layer:

    using System; 

namespace Chapter2.SRP.Decorator
{
public class Student
{
public string Name;
public string Id;
public DateTime DOB;

}
}

Let's say we have a simple Student entity class, which we want to persist in various forms, for example, to XML, JSON, or DB. Here are the basic classes defined to setup the decorator:

    public interface IPersistor<T> 
{
bool Persist(T objToPersist);
}

public class DefaultPersistor<T> : IPersistor<T>
{
public bool Persist(T objToPersist)
{
Trace.WriteLine("DefaultPersistor.Persist gets called");

return true; //Do nothing, eat up.
}
}

In the following code, you see a simple implementation for the XMLPersistorDecorator class, and OraclePersistorDecorator and SQLPersistorDecorator follow the same pattern:

    namespace Chapter2.SRP.Decorator 
{
public class XMLPersistorDecorator<T> : PersistorDecorator<T>
{
public XMLPersistorDecorator(IPersistor<T>
objectToBeDecorated) : base(objectToBeDecorated)
{ }

public override bool Persist(T objToPersist)
{
//stacking up functionality of the decorator pattern -
which basically ensures that main functionality is
achieved and this decorator adds up new functionality.

if (base.Persist(objToPersist))
return DoXMLPersistence();
return false;
}

private bool DoXMLPersistence()
{
//Does XML conversion and persistence operation..

Trace.WriteLine("DoXMLPersistence gets called");

return true;
}
}
}

Now you see the case where the decorator pattern is applied, satisfying the single responsibility principle:

    [Fact] 
public void Test_SRP_Decorator()
{
var student = GetFakeStudent();

IPersistor<Student> studentPersistence = new
OraclePersistorDecorator<Student>(new
XMLPersistorDecorator<Student>(new
DefaultPersistor<Student>()));

Assert.True(studentPersistence.Persist(student));
}

From this code, you can see how changes to the functionality are achieved by various classes instead of by modifying the single class with multiple responsibilities. The preceding sample code is basically persisting to all persistence layers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.138.14