Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance: why is changing an object's type considered bad design?

While modelling my domain classes, I found that Entity Framework lets you model inheritance relationships, but doesn't support promoting a base type instance to its derived type, i.e. changing an existing Person row in the database to an Employee, which derives from Person.
Apparently, I wasn't the first to wonder about this, because this question has been asked and answered on Stack Overflow multiple times (e.g. see here and here).

As indicated in these answers, Entity Framework doesn't support this because this isn't allowed in Object-Oriented Programming either: once an instance is created, you cannot change its run-time type.
Fair enough, from a technical perspective I can understand that once a single block of memory is allocated for an object, tacking on a few extra bytes afterwards to hold the derived type's fields in will probably require the entire memory block to be reallocated, and as a consequence, will mean that the object's pointer has now changed, which in turn introduces even more problems. So I get that this would be difficult to implement, and therefore not supported in C#.

However, aside from the technical component, the answers (and here too, see final paragraph on page 1) also seem to imply that it is considered bad design when you want to change an objects run-time type, saying that you shouldn't need this kind of type-changing and should use composition for these situations instead.

Frankly, I don't get why - I'd say its perfectly valid to want to work Employee instances in the same way as you would with a Person instance (i.e. by inheriting Employee from Person), even if at some point in time a Person will be hired as an Employee, or an Employee quits and becomes a Person again.
Conceptually, I don't see anything wrong with this?

Can anyone explain this to me?

--Edit, to clarify, why is this considered bad design:

public class Person
{
    public string Name { get; set; }
}

public class Employee: Person
{
    public int EmployeeNr { get; set; }

    public decimal Salary { get; set; }
}

...but this isn't?

public class Person
{
    public string Name { get; set; }

    public EmployeeInfo EmployeeInfo { get; set; }
}

public class EmployeeInfo
{
    public int EmployeeNr { get; set; }

    public decimal Salary { get; set; }
}
like image 452
Leon Bouquiet Avatar asked Jan 26 '14 12:01

Leon Bouquiet


2 Answers

Frankly, I don't get why - I'd say its perfectly valid to want to work Employee instances in the same way as you would with a Person instance (i.e. by inheriting Employee from Person), even if at some point in time a Person will be hired as an Employee, or an Employee quits and becomes a Person again. Conceptually, I don't see anything wrong with this?

I understand this as Person -> Employee -> Person? IE it is still a person but he/she/it gets attributed with "employment" and then "downgraded" to just a person?

Inheritance is very useful when the object does not change type during runtime, for example when you create an equity-object you can be quite sure you do not want to turn it into an FRA, even though both are subclasses of securities. But whenever something might change during runtime, such as behaviour, try using composition instead. In my previous example, the security subclasses might inherit from a top-level asset class, but the methods used for putting a market value on an asset should be put there by a behavioral pattern, such as the strategy pattern, since you might want to use different methods during one runtime.

For the case you are describing you should not use inheritance. Always favor composition over inheritance whenever possible. If something changes during runtime, it should definitely not be put there by inheritance!

You could compare it to lives, a human is born a human and dies a human, a cat is born a cat and dies a cat, an object of a certain type should be created as one type and garbage collected as the same type. Don't change what something is, change how it behaves or what it knows, through behavioral composition.

I would recommend you to have a look at Design Patterns: Elements of Reusable Object-Oriented Software which quite thoroughly covers some very useful design patterns.

like image 73
flindeberg Avatar answered Sep 20 '22 14:09

flindeberg


Consider this code:

Person person   = new Person { Name = "Foo" };
Person employee = new Employee { Name = "Bar", EmployeeNr = 42 };

However employee is declared as Person, the actual instance it is referring to is of type Employee. This means you can cast it to that:

Employee employee2 = (Employee)employee;

When you try that with person:

Employee employee3 = (Employee)person;

You'll get an exception in runtime:

Unable to cast object of type 'Person' to type 'Employee'.

So you're right in that this is a technical impossibility, as C# is implemented. You can work around it though, by creating a constructor in Employee:

public Employee(Person person, int employeeNr, decimal salary)
{
    this.Name = person.Name;
    EmployeeNr = employeeNr;
    Salary = salary;
}

As you see, you'll have to instantiate the Person part of the Employee yourself. This is tedious and error-prone code, but now we come to your actual question: why could this be considered a inferior design?

The answer lies in favoring composition over inheritance:

Composition over inheritance (or Composite Reuse Principle) in object-oriented programming is a technique by which classes may achieve polymorphic behavior and code reuse by containing other classes that implement the desired functionality instead of through inheritance.

The essence:

To favor composition over inheritance is a design principle that gives the design higher flexibility, giving business-domain classes and more stable business domain in the long term. In other words, HAS-A can be better than an IS-A relationship.

To keep in the terms of your real-world example: in the future, we might have to employ Androids, who definitely don't inherit from Person, but could use a EmployeeInfo record.


As for your Entity Framework specific question:

I'd say its perfectly valid to want to work Employee instances in the same way as you would with a Person instance (i.e. by inheriting Employee from Person), even if at some point in time a Person will be hired as an Employee, or an Employee quits and becomes a Person again.

If, for now, you know you're only going to employ humans (don't forget YAGNI), I cannot imagine an example where this issue would come up. When you want to show all persons, you iterate that repository:

using (var context = new MyContext())
{
    foreach (var person in context.Persons)
    {
        PrintPerson(person);
    }
}

And when you want to list all employees, you do exactly the same, but with the Employee repository:

using (var context = new MyContext())
{
    foreach (var Employee in context.Employees)
    {
        PrintEmployee(Employee);
    }
}

And if in some cases you want to know whether someone is an employee, you'd have to hit the database again:

public Empoyee FindEmployeeByName(string name)
{
    using (var context = new MyContext())
    {
        return context.Employees.FirstOrDefault(e => e.Name == name);
    }
}

Which you can then call using a Person instance:

Empoyee employee = FindEmployeeByName(person.Name);
like image 22
CodeCaster Avatar answered Sep 22 '22 14:09

CodeCaster