Question

i don't know how serious i should take inheritance..

in my application i have, as in most applications, customers, users and suppliers.

well, seems easy, but to be a stickler for details, i should do the following:

class Person {...}


class NaturalPerson extends Person {...}

class JuristicPerson extends Person {...}



class User extends NaturalPerson {...}

class Supplier extends JuristicPerson {...}

well, and what's with Customers? In my opinion they may be natural and juristic persons as well...

class JurCustomer extends JuristicPerson {...}

class NatCustomer extends NaturalPerson {...}

that'll be dumb to work with, because if i want to have all customers, i need to select the two corresponding tables...

basically i would prefer the table-per-type schema... but according to that hierarchy above, it could become a little bit complicated i think.

well, this is just an example. in many cases the hierarchy will be a lot more complex...

Était-ce utile?

La solution

Unnecessary layers of abstraction add complexity without offering any benefit.

What you have to do is determine what implementation benefit can be derived from your classes sharing a common ancestor.

I think maybe the best way for you to design your app is to start with a database schema that accurately represents the entities and relationships that you are dealing with. Then create your classes to mirror that schema (table per type).

Then when considering the functionality of similar classes at that point determine if the different types would benefit from sharing a base class. For example based in this design technique you end up with a Customer and a Supplier class. Part of the requirements dictate that you need a way to get information to print a mailing label. If the process that you go through to arrive at this information is the same, it would make sense to design in an abstract class that both would then extend that would implement that method.

This would help manage copy paste coding when designing your app.

Autres conseils

The design patterns to consider here are:

  • ActiveRecord: An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.

This is the classic Table-per-Type approach. The pattern is applicable if there is no object-relational impedance mismatch, e.g. when your tables match the object structure 1:1. If you have relational data, you have to start tucking ORM features on the ActiveRecord which is not what the pattern is for. So use this when you have a juristic_customer_table and juristic_person_table and so on.

The reality in many applications is that the OO structure does not match the DB structure because both tackle structural problems inherently different. Also, keeping both decoupled makes independent development much easier. As a consequence DataMapper are often a better, albeit more complicated choice.

  • SingleTableInheritance: Represents an inheritance hierarchy of classes as a single table that has columns for all the fields of the various classes.

Quoting POEAA: This pattern "maps all fields of all classes of an inheritance structure into a single table". When you pull the data, you run the recordset through a Mapper (which handles the CRUD as well), that knows which class to instantiate then with what data. For ease of use, the table can also hold the class name of the object. This will obviously leak implementation details of your classes into the database.


IMO both approaches are can of worms. Your inheritance hierarchy doesn't sound too plausible to me either. Remember that inheritance means Subtype is-a Supertype regarding the responsibilities. Chances are your subtypes will violate LSP very quickly, when you find that your Suppliers don't behave like a JuristicPerson at all. Ask yourself whether a Supplier needs to expose all the functionality of a Juristic Person. If not, consider this third approach:

Do not to use Inheritance at all, but Aggregation. Find the basic classes holding the data. Then make small Role objects encapsulating the responsibility of that role, e.g.

$supplier = new Supplier(new Juristic(new Person(PersonGateway::findById(1))));

Your Supplier will then hold all the logic to do Supplier-related things. You likely won't have need for anything Juristic or Person has when interacting with the Supplier. This should all be internal to the Supplier. Your public Supplier interface will be much smaller this way and more cleanly separate the responsibilities your objects can fulfill.

This way you don't create is-a relationships but has-a or uses-a. I am assuming Juristic holds some sort of business logic that is relevant to your use-cases. If the only difference between Juristic and Natural is semantic, then remove it altogether. See my blog post on Modeling the Real World for more details.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top