Software Architecture Cheat Blog 3: Open Closed Principle

Define Principle

Open Closed Principle (OCP) states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modifications.

Violation Example

Lets consider a simple banking module that is responsible for calculating interest of accounts respect to account type. Say app owner bank offers to their client three types of accounts : savings accounts, basic checking accounts, money market deposit account. Each of the account type has different interest rate, and interest is calculated on different other factors such as amount deposited, average balance per month and so on. Thus based on this business rules we designed this class Accounts which calculates interest based on account types.

public enum AccountTYpe
    {
        Savings,
        Checking,
        MoneyMarket 
    }

    public class Accounts
    {
        private AccountTYpe accountType { get; set; }

        public Decimal CalculateInterest()
        {
            Decimal interest = 0;
            if (accountType == AccountTYpe.Savings)
            {
                //consider business rules and calculate interest of savings account
                interest = 2.50m;
            }
            else if (accountType == AccountTYpe.Checking)
            {
                //consider business rules and calculate interest of checking account
                interest = 3.50m;
            }
            else
            {
                //consider business rules and calculate interest of money market account
                interest = 5.00m;
            }

            return interest;
        }
    }

Above solution work perfectly for our client bank and app went to live and every one is happy. Few months later bank realize other banks are making money from certificates of deposit account , so we should do that, lets change our system to accommodate that. Now our revised system looks like this,

public enum AccountTYpe
    {
        Savings,
        Checking,
        MoneyMarket,
        CertificatesOfDeposit
    }

    public class Accounts
    {
        private AccountTYpe accountType { get; set; }

        public Decimal CalculateInterest()
        {
            Decimal interest = 0;
            if (accountType == AccountTYpe.Savings)
            {
                //consider business rules and calculate interest of savings account
                interest = 2.50m;
            }
            else if (accountType == AccountTYpe.Checking)
            {
                //consider business rules and calculate interest of checking account
                interest = 3.50m;
            }
            else if (accountType == AccountTYpe.CertificatesOfDeposit)
            {
                //consider business rules and calculate interest of certificates of deposit account
                interest = 6.35m;
            }
            else
            {
                //consider business rules and calculate interest of money market account
                interest = 5.00m;
            }

            return interest;
        }
    }

Considering the above scenario its not hard to guess, it will go through same process each time bank wants to launch another account type or made change on existing account types business rules. This is where open closed principle violates.

Resolution

To solve this We can create an common Interface which will be implemented by all classes represent account types. Benefit is later change can be accommodate better without modifying other corresponding class.

public interface IAccount
    {
        Decimal CalculateInterest();
    }

    public class SavingsAccounts:IAccount
    {
        public decimal CalculateInterest()
        {
            return 2.50m;
        }
    }

    public class CheckingAccounts : IAccount
    {
        public decimal CalculateInterest()
        {
            return 3.50m;
        }
    }

    public class MoneyMarketAccounts : IAccount
    {
        public decimal CalculateInterest()
        {
            return 5.00m;
        }
    }

Through approach we can add as many account type is needed, all we need is add another class for that account type. Also modification of business logic resides in respective account type class. Hence IAccount interface implement the idea of open for extension but closed for modifications.

Software Architecture Cheat Blog 2: Single Responsibility Principle

Define Principle

Single Responsibility Principle (SRP) states that there should never be more than one reason for class to change.

Violation Example

Lets consider this sample class Customer

public class Customer
    {
        public String Name { get; set; }
        public String Address1 { get; set; }
        public String City { get; set; }
        public String State { get; set; }
        public String Zip { get; set; }
        public String Email { get; set; }

        //Responsibility # 1 : save customer information
        public bool SaveCustomer()
        {
            if (ValidateCustomer())
            {
                //Save customer data
                SendWelcomeEmail();
            }

            return true;
        }

        //Responsibility # 2 : send email
        private void SendWelcomeEmail()
        {
            //send welcome email to customer
        }

        //Responsibility # 3 : validate customer data
        private bool ValidateCustomer()
        {
            //Check if the emaill is already registered

            return true;
        }
    }

While SRP principle states no class should contains more then one responsibility. Customer class itself  carries three responsibility, save data/validate data/email.  If further we want to add additional validation rule, or need to update email function to facilitate admin email we have to change the Customer class, thus violates SRP.

Resolution

To solve this We have to break down each class respect to responsibility.

    public interface ICustomerInfo
    {
        String Name { get; set; }
        String Email { get; set; }
    }

    public class Customer: ICustomerInfo
    {
        public String Address1 { get; set; }
        public String City { get; set; }
        public String State { get; set; }
        public String Zip { get; set; }
        public String Name { get; set; }
        public String Email { get; set; }        
    }

    public class Emailer
    {
        public void SendWelcomeEmail(ICustomerInfo customerInfo)
        {
            //send welcome email to customer
        }
    }

    public class CustomValidator
    {
        public bool ValidateCustomer(ICustomerInfo customerInfo)
        {
            //Check if the emaill is already registered
            return true;
        }
    }

    public class CustomerManager
    {
        ICustomerInfo _custInfo;
        public void SaveCustomer()
        {
            _custInfo = new Customer();
            if (new CustomValidator().ValidateCustomer(_custInfo))
            {
                new Emailer().SendWelcomeEmail(_custInfo);
            }
        }        
    }

This makes ICustomerInfo independent of the Customer class which makes it easier to maintain and extend. Emailer and Validation is now handle single responsibility and do not depend on entire customer object.Further we can modify validation rules, emailer logic’s as needed without effecting core Customer class.

Software Architecture Cheat Blog 1: Design Principles

This blog series will gather summary of all the software architecture design principles under one roof. Intention is to keep in touch with these principles.
First one is called “SOLID”, which is combination of five basic designing principles,

Single Responsibility Principle

Example: http://blog.sanaulla.info/2011/11/16/solid-single-responsibility-principle/
There should never be more than one reason for class to change.

Open – Closed Principle

Example: http://blog.sanaulla.info/2011/11/19/solid-open-closed-principle/

Software Entities (Classes, Modules, Functions, etc.) should be open for extension but closed for modifications.

Liscov Substitution Principle

Example: http://lassala.net/2010/11/04/a-good-example-of-liskov-substitution-principle/
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

Interface Segregation Principle

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

Dependency Inversion Principle

A. High level modules should not depend on low level module. Both should depend on abstraction. Both should depend on Abstraction.
B. Abstraction should not depend on details. Details should depend on Abstraction.
Other important principles are

DRY (Don’t Repeat Yourself)

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
Aimed at reducing repetition of information of all kinds, especially useful in multi-tier architectures.

YAGNI – You ain’t gonna need it

Always implement things when you actually need them, never when you just foresee that you need them.

KISS – Keep it simple, Stupid!

Most systems work best if they are kept simple rather than made complex, therefore simplicity should be a key goal in design and unnecessary complexity should be avoided.

Seven Principles Of Software Development

  1. The Reason It All Exists
  2. KISS (Keep It Simple, Stupid!)
  3. Maintain the Vision
  4. What You Produce, Others Will Consume
  5. Be Open to the Future
  6. Plan Ahead for Reuse
  7. Think

REF: http://csharpsimplified.wordpress.com/2010/10/01/design-principle/