Introduction to Design Patterns - Creational Design Patterns
In our day-to-day life, we encounter a lot of problems. For some, we get a temporary fix. However, for the others, we consult an expert to find a permanent solution. That’s how most of our problem-solving processes work.
But whatever the solution be, we always try to properly implement it without any errors, so that the issue doesn’t crop up again.
A real-world example would be a construction site. Now before we build the entire structure, we ask the engineers to evaluate the soil strength and other construction-related factors. Based on the results, we ask them to propose the type of materials to be used, recommended design to follow for construction, and so on.
Similarly, while developing software, there will be issues like bottleneck situations, code redundancy, inefficient codes, and so on. To address most of these problems, while developing software in the object-oriented world, design patterns are created.
In this post, we will understand everything about design patterns.
Table of contents
- Introduction to Design Patterns
- Types of Design patterns
- Creational Design Pattern
- Conclusion
Introduction to Design Patterns
The word pattern indicates the way certain things occur. And in this context, it is considered a recurring problem in application development.
Design patterns are reusable solutions to the common software design problems that happen in the object-oriented world. In other words, they provide templates or generic solutions that can fit around the real-time problems faced during software development.
It was first introduced in the year 1994. 23 design patterns address various problems and are broadly classified into three sections. Let us understand them in the next section.
Types of Design patterns
Design patterns are broadly classified into three sections,
- Creational
- Structural
- Behavioral
Creational design patterns deal with how the objects can be created, in order words, how to instantiate an instance of a class. Creational has 5 types of design patterns
Structural design patterns deal with how classes and objects are implemented to form a large structure. They mainly focus on how classes inherit from other classes and how they are composed of other classes. Structural has 7 types of design patterns.
Behavioral design pattern mainly deals with the interaction and responsibilities of the objects such as how objects of different classes communicate and how they can talk easily and form a loosely coupled code. Behavioral has 11 types of design patterns.
In the next section, let us see about the design patterns in the creational category and continue with the rest of them in the subsequent blogs.
Creational Design Patterns
Abstract factory
Abstract factory deals with the creation of objects for a set of related or dependent objects rather than creating objects using the concrete classes directly. It acts as a super layer that creates other sub-layers at the run time depending on the concrete class selection.
Below is the template of the code.
namespace PARTECH_AbstractFactory;
public abstract class AbstractProductA { }
public class ProductA1 : AbstractProductA { }
public class ProductA2 : AbstractProductA { }
public abstract class AbstractProductB { }
public class ProductB1 : AbstractProductB { }
public class ProductB2 : AbstractProductB { }
public abstract class AbstractFactory
{
public abstract AbstractProductA GetProductA();
public abstract AbstractProductB GetProductB();
}
public class ConcreteFactoryA : AbstractFactory
{
public override AbstractProductA GetProductA()
{
return new ProductA1();
}
public override AbstractProductB GetProductB()
{
return new ProductB1();
}
}
public class ConcreteFactoryB : AbstractFactory
{
public override AbstractProductA GetProductA()
{
return new ProductA2();
}
public override AbstractProductB GetProductB()
{
return new ProductB2();
}
}
public class Consumer
{
private AbstractProductA _productA;
private AbstractProductB _productB;
public Consumer(AbstractFactory factory)
{
_productA = factory.GetProductA();
_productB = factory.GetProductB();
}
}
public class Main
{
Consumer obj = new Consumer(new ConcreteFactoryA());
}
In this code, there are two abstract classes - AbstractProductA and AbstractProductB. They act as the base class for products of type A (ProductA1, ProductA2) and B (ProductB1, ProducB2) respectively.
There is an abstract factory class that has two methods that correspondingly return AbstractProductA and AbstractProductB. The ConcreteFactoryA and ConcreteFactoryB are the classes where the actual initialization of classes happens which inherits the AbstractFactory class.
To consume the AbstractFactory, a consumer class is created which has the objects for AbstractProductA and AbstractProductB and takes up AbstractFactroy as input. This means, no concrete classes are being used in the entire class where it is being consumed. Rather, they are instantiated in the concrete class.
Here is a real-world example of Abstract Factory
Consider a banking application. There are different ways for different people to open an account here. Now think of a situation where an account needs to be opened for a major as well as for a minor. The form to open an account can be either shipped to the customer or collected from the bank. Also, the procedure/workflow changes for each of them.
Let's now understand how this can be achieved using AbstractFactory.
public abstract class AccountOpening { }
public class Major : AccountOpening { }
public class Minor : AccountOpening { }
public abstract class Shipping { }
public class InPersonCollection : Shipping { }
public class DoorDelivery : Shipping { }
public abstract class ProcessFactory
{
public abstract AccountOpening GetAccountOpening();
public abstract Shipping GetShipping();
}
public class StandardProcess : ProcessFactory
{
public override AccountOpening GetAccountOpening()
{
return new Major();
}
public override Shipping GetShipping()
{
return new InPersonCollection();
}
}
public class MinorProcess : ProcessFactory
{
public override AccountOpening GetAccountOpening()
{
return new Minor();
}
public override Shipping GetShipping()
{
return new InPersonCollection();
}
}
public class Consumer
{
private AccountOpening _accountOpening;
private Shipping _shipping;
public Consumer(ProcessFactory factory)
{
_accountOpening = factory.GetAccountOpening();
_shipping = factory.GetShipping();
}
}
public class Main
{
Consumer standardProcess = new Consumer(new StandardProcess());
Consumer minorProcess = new Consumer(new MinorProcess());
}
The above addresses the discussed problem statement using AbstractFactory design pattern. We have related the template code and the example code
- AbstractProductA and abstractProductB are AccountOpening and Shipping
- ProductA1 and ProductA2 are Major and Minor
- ProductB1 and ProductB2 are InPersonCollection and DoorDelivery
- AbstractFactory is ProcessFactory
- ConcreteFactoryA and ConcreteFactoryB are StandardProcess and MinorProcess.
Conclusion
As you have seen in this post, we have covered the first type of design pattern which is creational design patterns. In our subsequent blogs, we will look into other types for a holistic understanding of design patterns.