-2

I have fair understanding of interface/abstract class/class however just trying to understand something else. Look at below code:

namespace AbstractClassExample
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseEmployee fullTimeEmployee = new FullTimeEmployee();

            BaseEmployee contractEmployee = new ContractEmployee();
        }
    }

    public abstract class BaseEmployee
    {
        public string EmployeeID { get; set; }
        public string EmployeeName { get; set; }
        public string EmployeeAddress { get; set; }

        public abstract double CalculateSalary(int hoursWorked);
    }

    public class FullTimeEmployee : BaseEmployee
    {
        public override double CalculateSalary(int hoursWorked)
        {
            //do something
        }
    }

    public class ContractEmployee : BaseEmployee
    {
        public override double CalculateSalary(int hoursWorked)
        {
            //do something
        }
    }
}

however I fail to get below lines (1st approach):

BaseEmployee fullTimeEmployee = new FullTimeEmployee();
BaseEmployee contractEmployee = new ContractEmployee();

Why not written this way instead (2nd approach):

FullTimeEmployee fullTimeEmployee = new FullTimeEmployee();

it is completely okay to use 2nd approach it will work coz of relation. How would any developer in the work know if above abstract class is in DLL. Probably, will use 1st approach when you've code with you or sort of documentation. Isn't it?

Similar example would also be valid for interface declaration. like:

interface IPointy {
    void MyMethod();
}

class Pencil : IPointy {
    void MyMethod() {
    }

    void MyOtherMethod() {
    }
}

IPointy itPt = new Pencil();

Isn't 1st approach making it complex? What's good practice? Any good practice vs bad practice with 1st & 2nd?

user1480864
  • 1,455
  • 3
  • 16
  • 23
  • http://softwareengineering.stackexchange.com/questions/106601/in-simple-words-what-are-are-the-purposes-of-abstract-classes-and-or-interfaces – ASh Mar 23 '17 at 07:28
  • You can read about `SOLID Design Principals`. Specifically `Dependency injection` / `Inversion of Control` – uTeisT Mar 23 '17 at 07:28

3 Answers3

0

Using the first approach enables Polymorphism.

Let's say you have a company class:

class Company {

}

Of course, companies have full-time and contract employees. Let's add them as properties:

public FullTimeEmployee[] EmployeesFullTime { get; set; }
public ContractEmployees[] EmployeesContract { get; set; }

This seems all good.

But, what if your company can now have yet another kind of employee that has a different way of calculating his salary? You have to add another property:

public FullTimeEmployee[] EmployeesFullTime { get; set; }
public ContractEmployee[] EmployeesContract { get; set; }
public AnotherKindOfEmployee[] EmployeesOther { get; set; }

That's no good, is it? Every time you add a new kind of employee, you have to add another property!

That's why you use BaseEmployee, it does not care about what kind of employee it holds, and it can still calculate salary!

public BaseEmployee[] AllEmployees { get; set; }
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • 1
    I'd thought of adding a similar answer (already half written) but then realised that this is just about *base types* and inheritance. If that's what the OP is asking about, okay then, but why is there such a focus in the question on *abstract* types? – Damien_The_Unbeliever Mar 23 '17 at 07:33
  • @Damien_The_Unbeliever Abstract types just prevent you from creating an instance of it. There can never be an instance of `BaseEmployee` because every employee's salary is calculated differently. – Sweeper Mar 23 '17 at 07:39
  • 1
    I know - my point is though that the questions focus appears to be towards abstract types, not just plain inheritance. Your answer is equally applicable even when the base type isn't abstract so I'm struggling to know *whether it's dealing with what the OP is trying to ask*. – Damien_The_Unbeliever Mar 23 '17 at 07:43
  • There maybe be different rates applicable for different types of employees hence used different class. BTW, Above example just used as a reference by me to understand why abstract class used as type? I just copied it from somewhere – user1480864 Mar 23 '17 at 07:44
  • @user1480864 Do you understand what I mean? Abstract types prevent you from initializing `BaseEmployee`. This means that you must create one of its subclasses (and it makes sense in the real world, right?). But, whatever kind of employee one is, one's salary can always be calculated. – Sweeper Mar 23 '17 at 07:49
  • 1
    @Sweeper Yes, I do understand that you can't initialize Abstract types. Let's not stick to employee/salary example. Question is on any best practice. When I can use FullTimeEmployee fullTimeEmployee = new FullTimeEmployee(); why its used BaseEmployee fullTimeEmployee = new FullTimeEmployee();? – user1480864 Mar 23 '17 at 07:53
  • @user1480864 You want to use a subclass type as the variable's type when you want to only store that type of stuff. You want to use the base class type as the variable's type when you want to be able to store different subclasses of the base type in the variable, but still have some common functionality (calculate salary) and type-safety. – Sweeper Mar 23 '17 at 07:57
0

One of the reasons that you'd assign a FullTimeEmployee to a BaseEmployee is that you can put them together into a collection of FullTimeEmployees' andContractEmployees`:

List<BaseEmployee> allEmployees = new List<BaseEmployee>()
{
    new FullTimeEmployee() {...},
    new FullTimeEmployee() {...},
    new ContractEmployee() {...},
    new FullTimeEmployee() {...},
}

This has the disadvantage that you can't use (efficiently) functionality of a FullTimeEmployee that ContractEmployees don't have, but if you don't need this functionality while processing allEmployees, this method is preferable above creating two collections of employees. For instance, you could write one function that would work for both FullTimeEmployees and ContractEmployees:

private void PaySalary(List<BaseEmployee> employees)
{
    foreach (var employee in employees)
    {
        var salary = employee.CalculateSalary()
        Pay(salary, ...);
    }
}

One of the guidelines when creating an object oriented design is that you should design for change, meaning that your design should be such that you could easily add types to your design, or change internals of your classes.

Suppose you'll need a new type of employees, HiredEmployees. Because you derived them from BaseEmployee, you'll know you can calculate their salary. You don't have to change function PaySalary.

This would also have worked if you'd given your FullTimeEmployee and your ContractEmployee an interface:

interface ISalaryReceiver
{
    double CalculateSalary(int hoursWorked);
}

class BaseEmployee
{
    public string EmployeeID { get; set; }
    ...
}

class FullTimeEmployee : BaseEmployee, ISalaryReceiver
{
    public override double CalculateSalary(int hoursWorked)
    {
        ...
    }
}

class ContractEmployee : BaseEmployee, ISalaryReceiver
{
    public double CalculateSalary(int hoursWorked)
    {
        ...
    }
}

void PaySalary(List<ISalaryReceiver> employees)
{
    ...
}

This method would work. It is even ready for change: you can invent any employee as long as it implements ISalaryReceiver.

However!

Suppose your BaseEmploye has a function where it needs to CalculateSalary:

class BaseEmployee
{
    ...
    public void PaySalary()
    {
       double salary = ... // how to calculate the salary?
    }
}

You can't let BaseEmployee implement ISalaryReceiver, because a BaseEmployee doesn't know how to calculate the salary.

When using the abstract method, you can tell BaseEmployee, that every object of BaseEmployee knows how to CalculateSalary:

abstract class BaseEmployee
{
    abstract double CalculateSalary(...);
    public void PaySalary()
    {
       double salary = this.CalculateSalary(...);
       ...
    }
}

So if your base class needs functions that are different per derived class, and there is no proper default functionality, then your base class needs an abstract function. It is guaranteed that every derived class has implemented this function, and thus the base class can call it.

Because in such cases it is not meaningful to create objects of the base class (after all, the base class doesn't know how to CalculateSalary), the base class has to be declared abstract with the result that you can't create objects of base class. Only object of derived classes that implement CalculateSalary can be created.

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
0

One of the benefits of abstract classes is - you can use abstract type for argument type in the method.

Suppose you have a Report class with method Generate which during report generation need to calculate employee salary

public class Report
{
    public SalaryReport Generate(BaseEmployee employee)
    {
        // ...
        var salary = employee.CalculateSalary();
        // ...
    }
}

You don't want use if..else statement in every method where you need calculate salary.
So in Report class doesn't care how CalculateSalary implemented, it only cares that Employee class have this method.

Fabio
  • 31,528
  • 4
  • 33
  • 72