-
Notifications
You must be signed in to change notification settings - Fork 0
Design Pattern Basis
Design patterns are solutions to general problems that software developers faced during software development.
Software design principles represent a set of guidelines that helps us to avoid having a bad design.
-
- open for extension but closed for modifications.
-
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
Dependency Inversion Principle states that we should decouple high level modules from low level modules, introducing an abstraction layer between the high level classes and low level classes.
By applying the Dependency Inversion the modules can be easily changed by other modules just changing the dependency module.
-
Clients should not be forced to depend upon interfaces that they don't use.
This principle teaches us to take care how we write our interfaces. When we write our interfaces we should take care to add only methods that should be there. If we add methods that should not be there the classes implementing the interface will have to implement those methods as well. For example if we create an interface called Worker and add a method lunch break, all the workers will have to implement it. What if the worker is a robot?
-
A class should have only one reason to change.
In this context a responsibility is considered to be one reason to change. This principle states that if we have 2 reasons to change for a class, we have to split the functionality in two classes. Each class will handle only one responsibility and on future if we need to make one change we are going to make it in the class which handle it. When we need to make a change in a class having more responsibilities the change might affect the other functionality of the classes.
-
- Derived types must be completely substitutable for their base types. This principle is just an extension of the Open Close Principle in terms of behavior meaning that we must make sure that new derived classes are extending the base classes without changing their behavior. The new derived classes should be able to replace the base classes without any change in the code.
These design patterns provide a way to create objects while hiding the creation logic.
Creates an instance of several derived classes.
abstract class Shape
{
abstract void Draw();
}
class Circle : Shape
{
public override void Draw()
{
// circle draw
}
}
class Rectangle : Shape
{
public override void Draw()
{
// rectangle draw
}
}
Factory usage -
class ShapeFactory
{
public Shape GetShape(string type){
if(type=="circle")
return new Circle();
else if (type=="rectangle")
return new Rectangle();
return null;
}
}
Creates an instance of several families of classes.
A class of which only a single instance can exist
public class MyObject
{
private static MyObject _instance = new MyObject();
public static MyObject Instance
{
return _instance;
}
}
A fully initialized instance to be copied or cloned. It allows an object to create customized objects without knowing their class or any details of how to create them.
These design patterns concern class and object composition.
- Match interfaces of different classes
- Adapter pattern works as a bridge between two incompatible interfaces.
- This pattern involves a single class which is responsible to join functionalities of independent or incompatible interfaces.
- Separates an object’s interface from its implementation.
- The intent of this pattern is to decouple abstraction from implementation so that the two can vary independently.
- This pattern involves an interface which acts as a bridge which makes the functionality of concrete classes independent from interface implementer classes. Both types of classes can be altered structurally without affecting each other.
** Q. Difference between Adabpter & Bridge**
The Adapter pattern is more about getting your existing code to work with a newer system or interface. The Bridge pattern is going to allow you to possibly have alternative implementations of an algorithm or system.
- A tree structure of simple and composite objects.
- Composite pattern is used where we need to treat a group of objects in similar way as a single object.
- Composite pattern composes objects in term of a tree structure to represent part as well as whole hierarchy.
- Composite lets clients treat individual objects and compositions of objects uniformly.
- Add responsibilities to objects dynamically.
- This pattern creates a decorator class which wraps the original class and provides additional functionality keeping class methods signature intact.
Example-
//Create abstract decorator class implementing the Shape
class ShapeDecorator : Shape
{
Shape _decoratedShape = null;
public ShapeDecorate(Shape decoratedShape)
{
_decoratedShape = decoratedShape;
}
public override void Draw(){
_decoratedShape.Draw();
}
}
- A single class that represents an entire subsystem.
- Facade pattern hides the complexities of the system and provides an interface to the client using which the client can access the system.
- This pattern involves a single class which provides simplified methods required by client and delegates calls to methods of existing system classes.
Exampel -
//create facade class for shape maker
class ShapeMaker
{
Shape _circle;
Shape _rectangle;
public ShapeMaker(){
_circle = new Circle();
_rectangle = new Rectangle();
}
public void DrawRectangle(){
_rectangle.Draw();
}
public void DrawCircle(){
_circle.Draw();
}
}
These design patterns are specifically concerned with communication between objects.
- A way of passing a request between a chain of objects.
- The objects become parts of a chain and the request is sent from one object to another across the chain until one of the objects will handle it.
- Sequentially access the elements of a collection.
- This pattern is used to get a way to access the elements of a collection object in sequential manner without any need to know its underlying representation.
- A way of notifying change to a number of classes.
- Defines simplified communication between classes.
- Mediator pattern is used to reduce communication complexity between multiple objects or classes. This pattern provides a mediator class which normally handles all the communications between different classes and supports easy maintenance of the code by loose coupling.
- Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
- In Strategy pattern, a class behavior or its algorithm can be changed at run time.
abstract class SortStrategy
{
public abstract void Sort(List<string> list);
}
class QuickSort : SortStrategy
{
public override void Sort(List<string> list)
{
list.Sort(); // Default is Quicksort
Console.WriteLine("QuickSorted list ");
}
}
class MergeSort : SortStrategy
{
public override void Sort(List<string> list)
{
//list.MergeSort(); not-implemented
Console.WriteLine("MergeSorted list ");
}
}
// context class
class SortedList
{
private List<string> _list = new List<string>();
private SortStrategy _sortstrategy;
public void SetSortStrategy(SortStrategy sortstrategy)
{
this._sortstrategy = sortstrategy;
}
public void Sort()
{
_sortstrategy.Sort(_list);
}
}
SortedList studentRecords = new SortedList();
studentRecords.SetSortStrategy(new QuickSort());
studentRecords.Sort();
- In Template pattern, an abstract class exposes defined way(s)/template(s) to execute its methods. Its subclasses can override the method implementation as per need but the invocation is to be in the same way as defined by an abstract class.
- Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.
abstract class Game
{
abstract void Start();
abstract void End();
//template method
void Play()
{
Start();
End();
}
}
class Football : Game
{
override void Start(){
//football start
}
override void End(){
//football end
}
}
class Cricket : Game
{
override void Start(){
//cricket start
}
override void End(){
//cricket end
}
}