Support us .Net Basics C# SQL ASP.NET ADO.NET MVC Slides C# Programs Subscribe Buy DVD

Part 3 - Why and when should we use an abstract class

Suggested Videos:
Part 1 - Can you store different types in an array in c#
Part 2 - What is jagged array



This question can also be asked in a slightly different way
Give an example of where we could use an abstract class?

Let us understand when to use an abstract class with an example. An organisation has 2 types of employees
1. FullTimeEmployee
2. ContractEmployee



public class FullTimeEmployee
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int AnnualSalary { get; set; }

    public string GetFullName()
    {
        return this.FirstName + " " + LastName;
    }

    public int GetMonthlySalary()
    {
        return this.AnnualSalary / 12;
    }
}

public class ContractEmployee
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int HourlyPay { get; set; }
    public int TotalHoursWorked { get; set; }

    public string GetFullName()
    {
        return this.FirstName + " " + LastName;
    }

    public int GetMonthlySalary()
    {
        return this.HourlyPay * this.TotalHoursWorked;
    }
}

public static void Main()
{
    FullTimeEmployee fte = new FullTimeEmployee()
    {
        ID = 101,
        FirstName = "David",
        LastName = "Pie",
        AnnualSalary = 60000
    };

    Console.WriteLine(fte.GetFullName());
    Console.WriteLine(fte.GetMonthlySalary());

    Console.WriteLine("-------------");

    ContractEmployee cte = new ContractEmployee()
    {
        ID = 102,
        FirstName = "Sam",
        LastName = "Brooks",
        HourlyPay = 100,
        TotalHoursWorked = 40
    };

    Console.WriteLine(cte.GetFullName());
    Console.WriteLine(cte.GetMonthlySalary());
}

Notice that, we have designed FullTimeEmployee and ContractEmployee classes as stand-alone classes. Regardless of the employee type, all employees in the organisation are going to have ID, FirstName and LastName properties. We also compute the FullName of any employee by concatenating their FirstName and LastName. This means that, the above two classes (FullTimeEmployee & ContractEmployee) are related and there is, a lot of common functionality duplicated in them. The problem with this design is that, tomorrow, if want to introduce MiddleName property and if we have to include it in the computation of FullName, then we have to make the same change in both the classes. So code maintainability is going to be a big issue with this design. 

To avoid these issues, we can move the common functionality into a base class. Using a common base class, we are going to get rid of the duplicated code. 

The obvious next question is, How should we design the base class?
1. Should we design it as an abstract class 
OR
2. Should we design it as a Concrete (Non abstract) class 

Let's see what's going to happen, if we design the base class as a concrete class. All the common code is now present in BaseEmployee class.
public class BaseEmployee 
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string GetFullName()
    {
        return this.FirstName + " " + LastName;
    }

    public virtual int GetMonthlySalary()
    {
        throw new NotImplementedException();
    }
}

Notice that, now the "FullTimeEmployee" class inherits from "BaseEmployee" class and has only the code that is specific to it.
public class FullTimeEmployee : BaseEmployee
{
    public int AnnualSalary { get; set; }

    public override int GetMonthlySalary()
    {
        return this.AnnualSalary / 12;
    }
}

Along the same lines, "ContractEmployee" class inherits from "BaseEmployee" class and has only the code that is specific to it.
public class ContractEmployee : BaseEmployee
{
    public int HourlyPay { get; set; }
    public int TotalHoursWorked { get; set; }

    public override int GetMonthlySalary()
    {
        return this.HourlyPay * this.TotalHoursWorked;
    }
}

So, with the above design we got rid of duplicated code, but we introduced another problem. Since "BaseEmployee " is a concrete (Non abstract) class, there is nothing stopping us from creating an instance of BaseEmployee class and using it. In the Main() method, someone could instantiate "BaseEmployee" class as shown below.

public static void Main()
{
    BaseEmployee be = new BaseEmployee()
    {
        ID = 101,
        FirstName = "David",
        LastName = "Pie",
    };

    Console.WriteLine(be.GetFullName());
    Console.WriteLine(be.GetMonthlySalary());
}

The above design is bad for 2 reasons
1. We only have 2 types of employees in our organisation - ContractEmployee  & FullTimeEmployee. The developers should only able to instantiate ContractEmployee  & FullTimeEmployee classes and not BaseEmployee class.
2. We get a run time error, if we invoke GetMonthlySalary() method on BaseEmployee class.

To get rid of the second issue, we can make the following modifications
1. Remove GetMonthlySalary() virtual method from BaseEmployee class
2. Remove "override" keyword from GetMonthlySalary() method in ContractEmployee and FullTimeEmployee classes.

With the above changes, we won't get the runtime error, but we would still be able to instantiate BaseEmployee class. So to prevent BaseEmployee class from being instantiated, let's mark it as an abstract class.

One more change is to introduce GetMonthlySalary() as an abstract method in BaseEmployee class. This will ensure that, all the classes that derive from BaseEmployee class, 
1. Will either provide implementation for GetMonthlySalary() method 
OR
2. The derived class will also be marked as an abstract class.

With the above changes the design looks as shown below.
public abstract class BaseEmployee 
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string GetFullName()
    {
        return this.FirstName + " " + LastName;
    }

    public abstract int GetMonthlySalary();
}

public class FullTimeEmployee : BaseEmployee
{
    public int AnnualSalary { get; set; }

    public override int GetMonthlySalary()
    {
        return this.AnnualSalary / 12;
    }
}

public class ContractEmployee : BaseEmployee
{
    public int HourlyPay { get; set; }
    public int TotalHoursWorked { get; set; }

    public override int GetMonthlySalary()
    {
        return this.HourlyPay * this.TotalHoursWorked;
    }
}

So, in short, we would create an abstract class, when want to move the common functionality of 2 or more related classes into a base class and when, we don't want that base class to be instantiated.

6 comments:

  1. HiVenkat,
    how can i execute the output of this query :
    declare @sourceColName table(ID int identity(1,1),ColName nvarchar(256),compareCol char(1))
    declare @TargetColName table(ID int identity(1,1),ColName nvarchar(256),compareCol char(1))

    declare @SourceTab table( ID nvarchar(256),Name nvarchar(256),Designation nvarchar(256),joiningDate Datetime,sex char(1),test varchar(30))

    declare @TargetTab table( ID nvarchar(256),Name nvarchar(256),Designation nvarchar(256),joiningDate Datetime,sex1 char(1),test varchar(30))

    insert into @sourceColName(ColName,compareCol) values('ID',1)
    insert into @sourceColName(ColName,compareCol) values('Name',0)
    insert into @sourceColName(ColName,compareCol) values('Designation',0)
    insert into @sourceColName(ColName,compareCol) values('joiningDate',0)
    insert into @sourceColName(ColName,compareCol) values('sex',0)
    insert into @sourceColName(ColName,compareCol) values('test',0)

    insert into @TargetColName(ColName,compareCol) values('ID',1)
    insert into @TargetColName(ColName,compareCol) values('Name',0)
    insert into @TargetColName(ColName,compareCol) values('Designation',0)
    insert into @TargetColName(ColName,compareCol) values('joiningDate',0)
    insert into @TargetColName(ColName,compareCol) values('sex1',0)
    insert into @TargetColName(ColName,compareCol) values('test',0)

    insert into @SourceTab values('1','Kripesh','lead','2013-07-24 17:37:30.087','M','abc')
    insert into @SourceTab values('2','mohan','ssc','2014-07-24 17:37:30.087','M','abc')



    insert into @TargetTab values('1','Kripesh','lead','2013-07-24 17:37:30.087','F','abc')
    insert into @TargetTab values('2','mohan','lead','2013-07-24 17:37:30.087','M','eee')

    -- scripts

    declare @max int,@min int,@queryString varchar(8000)
    select @queryString='select '

    --select * from @sourceColName where compareCol=0

    select @max=MAX(ID),@min=MIN(ID) from @sourceColName where compareCol=0

    while (@min<=@max)
    begin

    set @queryString=@queryString+'stg.'+(select ColName from @sourceColName where compareCol=0 and ID=@min)+','
    +'tgt.'+(select ColName from @TargetColName where compareCol=0 and ID=@min)+',+
    (case when stg.'+(select ColName from @sourceColName where compareCol=0 and ID=@min
    )+'=tgt.'+(select ColName from @TargetColName where compareCol=0 and ID=@min)+' then ''true'' else ''false'' end ) Status ,'

    set @min=@min+1
    end

    declare @sTabName nvarchar(256),@tTabName nvarchar(256),@finalSQL nvarchar(max)

    set @queryString= substring(@queryString,0,len(@queryString))
    set @finalSQL=''
    set @sTabName='@SourceTab'
    set @tTabName='@TargetTab'
    set @finalSQL=((select @queryString+' from '+@sTabName + ' stg inner join '+ @tTabName + ' tgt on stg.' +
    (select ColName from @sourceColName where compareCol=1)
    +'=tgt.'+(select ColName from @TargetColName where compareCol=1)))

    select @finalSQL

    Thanks,
    Abhi .

    ReplyDelete
  2. So when would a base class required , Please a provide with an example.

    Thanks

    ReplyDelete
  3. its really a grt tutorial, but my doubt is ,Why we didn't use
    Child Obj =new Abstract Class()
    Why FullTimeEmployee=new FullTimeEmployee()
    Why not FullTimeEmployee=new BaseEmployee();
    Actually i am a newbie to C# please help me

    ReplyDelete
    Replies
    1. in C#, we cannot create an instance from abstract class.

      Delete
  4. No need to do that like FullTimeEmployee=new BaseEmployee(); as because both fulltime and parttime employee is already inheriting from base class

    ReplyDelete
  5. what will happen if base class is instantiated?

    ReplyDelete

If you like this website, please share with your friends on facebook and Google+ and recommend us on google using the g+1 button on the top right hand corner.