GoF design patterns 1

Omid Eidivandi (XaaXaaX)
4 min readOct 3, 2021

The design patterns are the solutions to any problematic in the computer science , in this article i’m going to explore all of 23 GOF and their cases in your real time scenarios, to give the base cases and experiences which can cover your needs and problematic.

By theoretical the GOF patterns are placed in 3 categories

Structural , Behavioral and Creational

What stands every category for?

The Behavioral patterns are highly used for the scenarios which bring the problematic based on the object’s state and behavior and can help to have the simplified algorithms with a high level context’s result.

The Creational patterns are the solutions based on the objects and categories of objects n which are used to construct a complex centralized solution, it aims to decouple the objects creation and composition from the whole system , in that way the system doesn’t care about the objects and their compositions, if you face a problem which is based on the categorized objects or creating objects with some conditional or limit constraints consider looking for the solution in this category of patterns.

The Structural patterns are focused on the way objects are get in composition to form a larger structural design, in the way creating or modifying the objects doesn’t affect the system. use this category when the need is to extend, modify or create the objects without affecting the core implementation of the system.

The implementation is based on a educational scenario , i kept it easy to understand and implementable. The example is a C full based implementation. The Project is created as a simple Api Controller.

Patterns Implementation :

public class TaskController : Controller
{
public IActionResult Index()
{
return Ok();
}
}

Firstly , we have to Implement the base part of task , the TaskItem.

public class TaskItem
{
public uint TaskId { get; set; }
public TaskDataEntity TaskData { get; set; } public DateTime CreationDate{ get; set; } public TastStatus Status { get; set; } public bool IsTaskActive()
{
return
this.Status != TastStatus.Abandoned &&
this.Status != TastStatus.Rejected &&
this.Status != TastStatus.Unknown;
}
}

The Repository implementation is in the example source code, the controller index pattern simply treat a task as bellow:

public class TaskController : ControllerBase
{
private readonly ITaskService service;
public TaskController(ITaskService service)
{
this.service = service;
}
public Task<IEnumerable<TaskItem>> GetTaskItems()
{
return
(await service.GetTasks())
.Select(m => (TaskModel)m);
}

public async Task<bool> IsTaskRecent([FromRoute]uint id, uint timestamp)
{
var task = await this.service.GetTask(id);
return (task as RecentTaskItem).IsTaskRecent(timestamp);
}

By implementing this Bridge pattern we made the resiliency, so any kind or ITaskService implementation can be used based on our needs.

Let’s Consider the business considers to have a new result on the recent TaskItem , what we need is just to add a condition on the task creation date for the RecentTaskItem object in comparison with TaskItem, so we add a new object as bellow :

public class RecentTaskItem: TaskItem,ITaskItem
{
public bool IsTaskActiveAndRecent(uint timestamp)
{
return
this.IsTaskActive() &&
this.CreationDate.Ticks >= DateTime.Now.AddTicks(timestamp * -1).Ticks;
}
}

Bu implementing the Adapter Pattern we could achieve the OCP of SOLID. the adapter pattern is useful when facing the objects which are in a framework or the inaccessible libraries, but in this example we could keep the TaskItem Principal responsibilities and extend it to respond the business needs.

The System needs to have some signals based on the MainTaskContainer, here we notify the Task object to be modified as MainTaskContainer state change.

The MainTaskContainer acts as a subject to our design by following implementation:

public class MainTaskContainer
{
public delegate void CallBack(FlowStatus status);
public event CallBack UpdateTasks;

protected FlowStatus Status { get; set; }
public void UpdateStatus(FlowStatus status)
{
this.Status = status;
UpdateTasks(status);
}
}

Now we will change the underlying TastItem Implementation to accept its container at the time of creation.

private readonly MainTaskContainer container;
public TaskItem() { }
public TaskItem(MainTaskContainer container)
{
this.container = container;
this.container.UpdateTasks += Update;
}

The Observer Pattern can lead us to minimize the complexity of related state changes , INotifyPropertyChanged interface in C# and PropertyChangeListener in Java act as an observer pattern.

Now Imagine the need for cloning the TaskMetaData object that will simplify creating a new task with already configured data, we do this by clone method it’s a MemberwiseClone , as it says , the members are cloned without considering the sub objects (Member Instances). Look at the following snippet of code

public class TaskMetaData
{
public Priority Priority { get; set; }
public string Owner { get; set; }
public TeamData Team { get; set; }
public ApplicationData Appliction { get; set; }
}

The TaskMetaData is a member instance of TaskItem class , but the problem raises when we need to clone a task it will not be cloned side by the member properties, to overcome this issue using the Prototype pattern is the best solution which fills our scenario.

[Serializable]
public class Prototype<T>
{
public T Clone()
{
return (T)this.MemberwiseClone();
}
public T FullClone()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Seek(0, SeekOrigin.Begin);
T copy = (T)formatter.Deserialize(stream);
stream.Close();
return copy;
}
}

Now make the TaskItem derive from Prototype class as bellow

public class TaskItem : Prototype<TaskItem>, ITaskItemBase
{

...

ITaskItemBase ITaskItemBase.Clone()
{
return this.FullClone();
}
}

Another Issue in our example is some sort of Componenization problematic, the MainTaskContainer Can be treated as a TaskItem and also treated as a Container, To achieve this goal the Composite pattern introduces a nice fully componentized solution , lets consider the TaskItem and MainTaskContainer should act as a single unit , that says they act in the same manner but the container has the same functionalities as Task but based on a collection of internal Tasks.

public class MainTaskContainer : ITaskItemBase
{
private readonly IDictionary<uint, ITaskItemBase> TaskList;
public MainTaskContainer() : this(new Dictionary<uint, ITaskItemBase>()) { }
public MainTaskContainer(IDictionary<uint, ITaskItemBase> TaskList) => this.TaskList = TaskList;
...

public void Update(FlowStatus status)
{
this.Status = status;
TaskList.AsParallel().ForAll(t => t.Value.Update(status));
}

}

--

--

Omid Eidivandi (XaaXaaX)

i'm a technial lead , solution/softwatre architect with more than 20 years of experience in IT industry,, i m a fan of cloud and serverless in practice