Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

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.

10 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
  6. It will throw an an error that cannot create an instance of the class which is marked as abstract. So , in short we cannot create an instance of that class which is defined as a abstract.

    ReplyDelete
  7. Hi , i'm new using c# i would like to know why my solution has an error , i created a abstract class like this:
    public abstract class Datos{
    public string server{get; set};
    public string user {get; set};
    public string passwd {get; set};
    }
    i created 2 other classes :
    Listar : Datos
    {

    }

    Ejecutar: Datos
    {
    }
    when i use console application it works very well but the problem is when i use windows form.
    mean how to add data from textbox in the form,
    example
    server = textServer.text
    I'm new using c# and vial net please can you help me?.

    ReplyDelete
  8. I am confused between abstract and concrete class. i know we don't create object of abstract class. if we use concrete class and do not create any object of base class and use my common code in base class so i can achieve my goal. So why we use abstract class. Can you elaborate?

    ReplyDelete
  9. When we declare class as Abstract, we CAN'T create an object of this class even if we want to(its even shown in the video) because the system will give compilation error. Meaning, let's say any developer who tries to make an object of this class won't be able to do so and this is our purpose.
    In case of concrete class, if anyone wants to create an object of this class, he CAN create an object. It won't give compilation error. Making an object is not wrong, but in some situation, we want to prevent it.

    For eg, taking the example shown in videos, lets say in real life, A company is using the program above to register its employee. They give salary 10k to full time employee and 5k to part time employee. So all employee can be either "Full Time Employee" or "Part Time Employee". The person registering it chooses the value from a dropdown against employee name list and for each employee, he chose either of the two value. So, for following employee,
    1. Mark is full time employee, so we save FullTimeEmployee Status for Mark.
    2. John is part time employee, so we save PartTimeEmployee status for John.
    3. Jesse, in system is appearing with BaseEmployee status for its name, so what does it mean, it doesn't make sense, how will we decide his salary, since salary criteria for company is either for Full Time Employee or Part Time Employee. This means its a mistake, some developer created BaseEmployee class object and passed it to front end. But base employee we had created for our code reuse and maintainability purpose mainly( and also to give some function with implementation and others without) and this class or option doesn't make sense with respect to company policy, it shouldn't have been passed to the front end even by mistake. This could have been prevented, if by some scenario, when that developer was trying to create an object of BaseEmployee class, he got an error and realized that this class object is not meant to be created. This we could achive by declaring BaseEmployee class as Abstract class.

    ReplyDelete

It would be great if you can help share these free resources