There is no need to have specific subclasses for every person.
You're right, those should be instances instead.
Goal of subclasses: to extend parent classes
Subclasses are used to extend the functionality provided by the parent class. For example, you may have:
A parent class Battery
which can Power()
something and can have a Voltage
property,
And a subclass RechargeableBattery
, which can inherits Power()
and Voltage
, but can also be Recharge()
d.
Notice that you can pass an instance of RechargeableBattery
class as a parameter to any method which accepts a Battery
as an argument. This is called Liskov substitution principle, one of the five SOLID principles. Similarly, in real life, if my MP3 player accepts two AA batteries, I can substitute them by two rechargeable AA batteries.
Note that it is sometimes difficult to determine whether you need to use a field or a subclass to represent a difference between something. For example, if you have to handle AA, AAA and 9-Volt batteries, would you create three subclasses or use an enum? “Replace Subclass with Fields” in Refactoring by Martin Fowler, page 232, may give you some ideas and how to move from one to another.
In your example, Karl
and John
don't extend anything, nor do they provide any additional value: you can have the exactly same functionality using Person
class directly. Having more lines of code with no additional value is never good.
An example of a business case
What could possibly be a business case where it would actually make sense to create a subclass for a specific person?
Let's say we build an application which manages persons working in a company. The application manages permissions too, so Helen, the accountant, cannot access SVN repository, but Thomas and Mary, two programmers, cannot access accounting-related documents.
Jimmy, the big boss (founder and CEO of the company) have very specific privileges no one has. He can, for example, shut down the entire system, or fire a person. You get the idea.
The poorest model for such application is to have classes such as:
Person
┎━━━━┰━┸━━┰━━━┒
Helen Thomas Mary Jimmy
because code duplication will arise very quickly. Even in the very basic example of four employees, you will duplicate code between Thomas and Mary classes. This will push you to create a common parent class Programmer
. Since you may have multiple accountants as well, you will probably create Accountant
class as well.
Person
┎━━━━━━━━╁━━━━━━━┒
Accountant Programmer Jimmy
┃ ┎━━┸━━┒
Helen Thomas Mary
Now, you notice that having the class Helen
is not very useful, as well as keeping Thomas
and Mary
: most of your code works at the upper level anyway—at the level of accountants, programmers and Jimmy. The SVN server doesn't care if it's Thomas or Mary who needs to access the log—it only needs to know whether it's a programmer or an accountant.
if (person is Programmer)
{
this.AccessGranted = true;
}
So you end up removing the classes you don't use:
Person
┎━━━━━━╁━━━━━┒
Accountant Programmer Jimmy
“But I can keep Jimmy as-is, since there would always be only one CEO, one big boss—Jimmy”, you think. Moreover, Jimmy is used a lot in your code, which actually looks more like this, and not as in the previous example:
if (person is Jimmy)
{
this.GiveUnrestrictedAccess(); // Because Jimmy should do whatever he wants.
}
else if (person is Programmer)
{
this.AccessGranted = true;
}
The problem with that approach is that Jimmy can still be hit by a bus, and there would be a new CEO. Or the board of directors may decide that Mary is so great that she should be a new CEO, and Jimmy would be demoted to a position of a salesperson, so now, you need to walk through all your code and change everything.