Dependency Frameworks and Parameterized Constructors

April 23rd, 2012 Comments

Most dependency injection frameworks support some kind of object initialization through parameterized constructors. The question is whether or not it’s a good idea to use that feature of DI frameworks.

Consider the following code:
Read more…

LSP: Liskov Substitution Principle

August 23rd, 2006 Comments

The Liskov Substitution Principle dictates when and where it is correct to derive a subtype from an existing supertype. As defined by Barbara Liskov and Jeannette Wing, it essentially states that:

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.

What it means is that if S is a subtype of T, then a function q(x) must behave in the same manner irrespective of the type of x whether it be S or T. In other words, if a piece of code behaves differently for a subtype than a supertype, then that code violates the LSP (and consequently the OCP).

Let’s say that we work for a bank and that we have a simple teller application. Currently the teller application works with checking and savings account types.

public abstract class Account
{
    public abstract double CurrentBalance { get; }
    public abstract void Deposit(double amount);
}

public class CheckingAccount : Account
{
    private double _currentBalance;

    public override double CurrentBalance
    {
        get { return _currentBalance; }
    }

    public override void Deposit(double amount)
    {
        _currentBalance += amount;
    }
}

public class SavingsAccount : Account
{
    /* Savings account implementation */
}

Our bank just entered the mortgage business so we need to modify our teller application to support this new account type. During the short analysis phase we decide that since mortgage account is type of an account (IS-A relationship), we can simply create a new mortgage class deriving from the Account abstract class, override a method and a property, and we should be in business. We add a new class as follows:

public class MortgageAccount : Account
{
    private double _currentBalance;

    public override double CurrentBalance
    {
        get { return _currentBalance; }
    }

    public override void Deposit(double amount)
    {
        _currentBalance -= amount;
    }
}

Everything sounds logical, that is until we come across this code in the teller application:

public class Bank
{
    public void ReceiveMoney(Account account, double amount)
    {
        double oldBalance = account.CurrentBalance;
        account.Deposit(amount);
        Debug.Assert(account.CurrentBalance = oldBalance + amount);
    }
}

Based on our pre-existing code, the ReceiveMoney() method is making a reasonable assumption that the new balance should be equal to the old balance plus the newly deposited amount. This assumption is, however, violated if we pass an instance of the Mortgage class to this method. This is a clear violation of the LSP.

OCP Open-Closed Principle

August 21st, 2006 Comments

Another fundemental principle of object-oriented software design is the Open-Closed Principle. According to Uncle Bob (Robert Martin), this principle states that:

Software entities (classes, modules, functions, etc.) should be
open for extension, but closed for modification.

Essentially, what this means is that software’s design should be such that it’s behavior can be modified by extending the existing source code rather than modifying it. Consider the following example:

public class CheckingAccount
{
    private double _currentBalance;

    public void ProcessDeposit(double amount)
    {
        _currentBalance += amount;
    }
}

public class MortgageAccount
{
    private double _currentBalance;

    public void ProcessPayment(double amount)
    {
        _currentBalance -= amount;
    }
}

public class Bank
{
    public void ReceiveMoney(object account, double amount)
    {
        if (account.GetType() == typeof(CheckingAccount))
        {
            CheckingAccount checkingAccount = (CheckingAccount)account;
            checkingAccount.ProcessDeposit(amount);
        }
        else if (account.GetType() == typeof(MortgageAccount))
        {
            MortgageAccount mortgageAccount = (MortgageAccount)account;
            mortgageAccount.ProcessPayment(amount);
        }
    }
}

Notice that the ReceiveMoney() method of the Bank class has to determine the type of account passed in. Only based on that information it can perform the appropriate business logic. Later if we add another account type then we may need to modify this code in order for it to work properly. The above code is, hence, in violation of the Open-Closed principle.

So how can we revise our code to conform to the Open-Closed Principle? Abstraction is the answer. Let’s take a look:

public abstract class Account
{
    public abstract void Deposit(double amount);
}

public class CheckingAccount : Account
{
    private double _currentBalance;

    public override void Deposit(double amount)
    {
        _currentBalance += amount;
    }
}

public class MortgageAccount : Account
{
    private double _currentBalance;

    public override void Deposit(double amount)
    {
        _currentBalance -= amount;
    }
}

public class Bank
{
    public void ReceiveMoney(Account account, double amount)
    {
        account.Deposit(amount);
    }
}

Notice how we got rid of all conditional logic in the ReceiveMoney() method. Also notice that if we later add a new account type, the ReceiveMoney() method will work without any modifications. The revised version of our code above, hence, conforms to the Open-Closed Principle.

Let’s now consider what happens to our code if a new business rule is added that requires an e-mail notification to be sent to the account holder whenever a deposit is made. Obviously, we’ll have to modify the ReceiveMoney() method to perform that logic. Since we can’t account for all possible scenarios, we can never acheive perfect closure. With that in mind, design the simplest software that will do the job, and conform to OCP or other such design principles where you see the need.