Showing posts with label Design Principles. Show all posts
Showing posts with label Design Principles. Show all posts

Wednesday, 27 August 2014

Single Responsibility Principle

According to the single responsibility principle:
A class,a method ,an assembly should have one and only one reason to change.

Explanation:


  • If we have more than one reasons ,say you have n reasons to change for a class, we have to split the functionality in into n number 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.
  • The Single Responsibility Principle represents a good way of identifying classes during the design phase of an application and it reminds you to think of all the ways a class can evolve.
  • A good separation of responsibilities is done only when the full picture of how the application should work is well understand.

Example

Suppose I have a project for customer management , using which users can
a. Do CRUD(Create Retrieve Update Delete) operations on customer data
b. Send mails for CU to customer and D to Administrator
c. and also, we have a requirement to log exceptions if there is a failure.
So, the first step we do is create three layers in the CustomerManagementProject:
a. UI
b. BAL
c. DAL
Now, UI takes care of all UI related functionality and DAL takes care of all data related functionality.
The middle tier has the business rules to
a. Create/Update/Delete Customers
b. Sending email and email format
c. Log Exceptions.
The BAL class looks like:
The sample sequence diagram is:
Now,
if there is a change in the business rules to send email or log exceptions we are going to touch the manage customerBAL class.
So, this class is violating SRP.

How To Resolve:


1. Although BAL has to do all the three functions of managing customers, sending mails and logging errors.
Let us have three separate projects:
a. ManageCustomerBAL
b.Exceptions
c.Email

So, now my solution looks something like:
Even though we have moved exception and email to two different projects, but still then we need to create an object of the exception class and log exceptions.
For example my BAL refers to Exception like

Add reference
Create an instance of the class
Call the method
So, now the sequence diagram looks like:
Similarly to log email exceptions we have another class in LogException namespace.
But suppose, the requirement changes and I need to add the exception to event viewer as well what do I do?

You cannot come again and instance of another class in ManageCustomerBAL.
If you create it you are again violating SRP.

So, what should I do??

Now , we need to add an interface for logging. The exception class and the BAL refer to the exception logging methods using interface.
So,What we need to do is replace the FileException Class with a generic interface and extend the FileException class from this interface.
Step 1: Create a project to contain the Interface.
Step 2: And add method to the interface.


Step 4 : Extend this Interface in all the exception classes

Step 5: Remove the reference to Exception from BAL and add reference to IInterface.
Step 6: Add reference for the interface in BAL





Now that we have added an interface , there is going to be no change in the BAL if we are going to add a new class to the LogException Namespace.
The problem is how do I create an object for the interface?
So, I should use a factory class.
Step 7: Add a ExceptionFactory project 
Step 8 : Add reference to Exception and IInterface to the ExceptionFactory project.
The interface is what which will help to identify the classes properly.
and how do you identify the class?
Step 9:You have a key-value pair in the web.config file something like

Step 10: In the ExceptionFactory add a  method which reads this web.config and creates objects of required classes.

Step 11: Refer factory class in BAL
So, whenever you have a change in requirement you need to change only at two places:
a. The ExceptionFactory
b.The LogException class.

Dependency Inversion Principle

(A) High level modules should not depend upon low level modules. Both should depend upon abstractions. (B) Abstractions should not depend upon details. Details should depend upon abstractions.

Explanation

Scenario 1
You work in an organization where you and your colleagues tend to travel a lot. Generally you travel by air and every time you need to catch a flight, you arrange for a pickup by a cab. You are aware of the airline agency who does the flight bookings, and the cab agency which arranges for the cab to drop you off at the airport. You know the phone numbers of the agencies, you are aware of the typical conversational activities to conduct the necessary bookings.
Thus your typical travel planning routine might look like the following :
Decide the destination, and desired arrival date and time
Call up the airline agency and convey the necessary information to obtain a flight booking.
Call up the cab agency, request for a cab to be able to catch a particular flight from say your residence (the cab agency in turn might need to communicate with the airline agency to obtain the flight departure schedule, the airport, compute the distance between your residence and the airport and compute the appropriate time at which to have the cab reach your residence)
Pickup the tickets, catch the cab and be on your way
Now if your company suddenly changed the preferred agencies and their contact mechanisms, you would be subject to the following relearning scenarios
The new agencies, and their new contact mechanisms (say the new agencies offer internet based services and the way to do the bookings is over the internet instead of over the phone)
The typical conversational sequence through which the necessary bookings get done (Data instead of voice).
Its not just you, but probably many of your colleagues would need to adjust themselves to the new scenario. This could lead to a substantial amount of time getting spent in the readjustment process.
Scenario 2
Now lets say the protocol is a little bit different. You have an administration department. Whenever you needed to travel an administration department interactive telephony system simply calls you up (which in turn is hooked up to the agencies). Over the phone you simply state the destination, desired arrival date and time by responding to a programmed set of questions. The flight reservations are made for you, the cab gets scheduled for the appropriate time, and the tickets get delivered to you.
Now if the preferred agencies were changed, the administration department would become aware of a change, would perhaps readjust its workflow to be able to communicate with the agencies. The interactive telephony system could be reprogrammed to communicate with the agencies over the internet. However you and your colleagues would have no relearning required. You still continue to follow exactly the same protocol as earlier (since the administration department did all the necessary adaptation in a manner that you do not need to do anything differently).
Dependency Injection ?
In both the scenarios, you are the client and you are dependent upon the services provided by the agencies. However Scenario 2 has a few differences.
You don't need to know the contact numbers / contact points of the agencies – the administration department calls you when necessary.
You don't need to know the exact conversational sequence by which they conduct their activities (Voice / Data etc.) (though you are aware of a particular standardized conversational sequence with the administration department)
The services you are dependent upon are provided to you in a manner that you do not need to readjust should the service providers change.
Thats dependency injection in “real life”. This may not seem like a lot since you imagine a cost to yourself as a single person – but if you imagine a large organization the savings are likely to be substantial.
Dependency Injection in a Software Context
Software components (Clients), are often a part of a set of collaborating components which depend upon other components (Services) to successfully complete their intended purpose. In many scenarios, they need to know “which” components to communicate with, “where” to locate them, and “how” to communicate with them. When the way such services can be accessed is changed, such changes can potentially require the source of lot of clients to be changed.
One way of structuring the code is to let the clients embed the logic of locating and/or instantiating the services as a part of their usual logic. Another way to structure the code is to have the clients declare their dependency on services, and have some "external" piece of code assume the responsibility of locating and/or instantiating the services and simply supplying the relevant service references to the clients when needed. In the latter method, client code typically is not required to be changed when the way to locate an external dependency changes. This type of implementation is considered to be an implementation of Dependency Injection and the "external" piece of code referred to earlier is likely to be either hand coded or implemented using one of a variety of DI frameworks.

Benefits

  • Dependency Injection enables user to write loosely coupled code 
  • Dependent object give up control of managing their dependencies and instead let a Composition Root inject the dependencies into them.
  • DI + Repository + Programming against Interfaces overall help in separation of concerns and greatly improves the overall application maintainability.
  • No Framework is required to do the changes.

Practical Examples:

MEF, Rhino, Spring etc.

Conclusion:

Use the principle intelligently.

Interface segregation principle


Clients should not be forced to depend upon interfaces that they don't use.

Explanation:

When designing an application the abstraction of modules plays a vital role.We should take care that a client doesn't implement an interface , just because it has to implement.
Such an interface is named fat interface or polluted interface. Having an interface pollution is not a good solution and might induce inappropriate behavior in the system.
So instead of one large interface we should have many smaller interfaces with grouped behavior.


Example:

For example you have a service class for working with Category . And IManageCategory interface exposes
 three methods
  •  Add
  • Update
  •  and Delete.
 Because of some reasons of deployment and security we have to  divide this CategoryClass to  two 
different classes

  •  CreateCategory will implement the Add method and run in untrusted environments. 
  •  UpdateCategory, will implement the other two methods and will only run in secure verified and authenticated context. 
Now if I use the IManageCategory class I have to implement dummy void Update and Delete in my CreateCategory class and dummy void Add in my UpdateCategory class.
So, this has violated the design rule of Interface seggregation.

So, we have to divide the IManageCategory to two interfaces IAddCategory an IUpdateCategory.

Advantages of Using ISP:

  1. Better Understanadability
  2. Better Maintenability
  3. High Cohesion
  4. Low Coupling

Limitations

ISP like any other principle should be used intelligently when necessary otherwise it will result in a code containing lots of interfaces containing one method. So the decision should be taken intelligently.

The Liskov Substitution Principle

This principle states that :“ Any derived class should be substitutable for their base classes”

Explanation

We must make sure that the new derived classes just extend without replacing the functionality of old classes. Otherwise the new classes can produce undesired effects when they are used in existing program modules.
Likov's Substitution Principle states that if a program module is using a Base class, then the reference to the Base class can be replaced with a Derived class without affecting the functionality of the program module.

How to know its violated?

  • A subclass that does not keep all the external observable behavior of it's parent class
  • A subclass modifies, rather than extends, the external observable behavior of it's parent class.
  • A subclass that throws exceptions in an effort to hide certain behavior defined in it's parent class
  • A subclass that overrides a virtual method defined in it's parent class using an empty implementation in order to hide certain behavior defined in it's parent class

Example

Where it is violated:
Let us say you have a class Rectangle
And say you created a child class Square 
Suppose I am going to calculate Area of a Rectangle 
This application is never going to give me correct out put.
Its always going to show area of a square and not that of the rectangle.
Where it is followed:
 Let us say consider the above example. “SavingsWithDrawform” class has a method which accepts the “Accounts” class object as shown below.
public void WithDraw( Accounts objAcc)
{
//Code Implementation of Account objects
}
The principle of LSP states that it should be legal to pass the the “SavingsAccount” object to the same method, which is as shown
public void WithDraw(SavingsAccounts objAcc)
{
//Passing the derived type should be legal
}

Where to use this?

Ideally you should use it wherever you are going to use a sub-class.
But then,
In the words of Robert Martin, Agile Principles, Patterns and Practices in C# (P.149):
"A good engineer learns when compromise is more profitable than perfection. However, conformance to LSP should not be surrendered lightly. The guarantee that a subclass will always work where its base classes are used is a powerful way to manage complexity. Once it is forsaken, we must consider each subclass individually."

Open Close Principle

Open Close Principle


What does the principle say?


OCP The Open Closed Principle: -- you should be able to extend a class's behavior, without modifying it.
The Open-Closed Principle (OCP) states that software entities (classes, modules, methods, etc.) should be open for extension, but closed for modification


We should strive to write code that doesn’t have to be changed every time the requirements change.


Importance



This is especially valuable in a production environment, where changes to source code may necessitate code reviews, unit tests, and other such procedures to qualify it for use in a product: code obeying the principle doesn't change when it is extended, and therefore needs no such effort.
Reduces the development , regression testing and maintenanace time.

How do you know its violated?


You have lots of if-else loops, switch case statements,Enums are used.
If you have not used polymorphism and inheritance in your code there are great chances that you are violating the Open-Closed Principle.

Example

Suppose I need to calculated area of various shapes rectangle , circle , square. There are various ways to do it. But if you use if..else / Switch ...Case statements , the day it is suggested to calculate the area of an ellipse you will have to give change the code and retest.
So, what is suggested is segregate the responsibility of calculating Area to a different abstract class.
say:
Inherit this in all the classes for which you will claculate area

Where should I implement this?

You should implement OCP after giving a proper thought on this.
You should not anticipate changes in requirements ahead of time, as at least my psychic abilities haven’t surfaced yet and preparing for future changes can easily lead to overly complex designs. Instead, I would suggest that we focus on writing code that is well written enough so that it’s easy to change if the requirements change.

SOLID Design Principles

SOLID : Design Principles

Software design principles represent a set of guidelines that helps us to avoid having a bad design. The design principles are associated to Robert Martin who gathered them in "Agile Software Development: Principles, Patterns, and Practices". 
According to Robert Martin there are 3 important characteristics of a bad design that should be avoided:
  • Rigidity - It is hard to change because every change affects too many other parts of the system.
  • Fragility - When you make a change, unexpected parts of the system break.
  • Immobility - It is hard to reuse in another application because it cannot be disentangled from the current application.

Following 2 characters should be taken care during designing any software:
  • High Cohesion - How focused are the responsibilities of the modules you are designing.
  • Low Coupling - The degree to which modules rely on other modules.
Principles Of Object Oriented Class Design (the "SOLID" principles)
S => SRP Single responsibility principle - the notion that an object should have only a single responsibility.

O => OCP Open/closed principle the notion that “software entities … should be open for extension, but closed for modification”. 

L => LSP Liskov substitution principle the notion that “objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program”. 

I=>  ISP Interface segregation principle the notion that “many client specific interfaces are better than one general purpose interface.” 

D => DIP Dependency inversion principle the notion that one should “Depend upon Abstractions. Do not depend upon concretions.” Dependency injection is one method of following this principle.

I think that these form the foundation of Object Oriented design that allows concise, modular, and ultimately maintainable code. This means code that you (or others) can understand and modify easily and without causing unintended consequences for the function of the application in which the code resides.

Although the above design principles are good and helpful, but they should be used intelligently otherwise this may lead to problems.

The other principle , that is of a great importance is YAGNI (You ain't gonna need it).

According to YAGNI:
  • The time spent is taken from adding, testing or improving necessary functionality.
  • The new features must be debugged, documented, and supported.
  • Any new feature imposes constraints on what can be done in the future, so an unnecessary feature now opens the possibility of conflicting with a necessary feature later.[clarification needed]
  • Until the feature is actually needed, it is difficult to fully define what it should do and to test it. If the new feature is not properly defined and tested, it may not work correctly, even if it eventually is needed.
  • It leads to code bloat; the software becomes larger and more complicated.
  • Unless there are specifications and some kind of revision control, the feature may not be known to programmers who could make use of it.
  • Adding the new feature may suggest other new features. If these new features are implemented as well, this may result in a snowball effect towards feature creep.



But then futuristic thinking is also a must while designing a software.
So, you should always take design decisions intelligently.