Search code examples
design-patternsclass-designclass-diagram

Design Issue: Owning a class with many different subclasses


Consider the following class diagram:

+--------+     * +------------+ 
| Person |------>|   Property |
+--------+       +------------+
                      ^
                      |
             +--------+------+
             |               |
      +----+----+     +------+-----+
      |   Car   |     |   House    |
      +---------+     +------------+

Person has many properties including Car, House, Smartphone etc. Each of these properties has different attributes (Car has an engine size, model, year... House as an address, size, etc). There are many subclasses (types of properties).

Right now my implementation is having Property an abstract class with a type field. I check the type and cast it to the right subclass by it.

My question is: Is this the only way? is this the right way to do it? It doesn't feel so right, so please share your design thoughts with me. A radical change would also be accepted.


Solution

  • It really depends on how you'd like to use those Propertys, generically or specifically (or both). If the only thing they have in common is the fact that they can be owned, I'd prefer to have a common interface between them, not a common base class. Then, you could store them in a collection and have capability-specific methods that act on subsets of the collection, which would first filter the collection based on the desired capability. Capabilities are more like interfaces and roles than types and classes; so you can have a level of independence between what you want to achieve (your intent) and how you achieve it (your implementation).

    In pseudo-C#-code, it could look like this:

    interface Property {}
    interface Valued {
      Money Value { get; }
    }
    class Person {
      private Collection<Property> properties;
      public Money TotalValue {
        get {
          return properties.OfType<Valued>().Sum(v => v.Value);
        }
      }
    }
    

    You also can filter by specific type, like if you want to do something with all Cars, by using the same technique. A more general method could return all properties of a specific type so that the client can execute some specific logic with them.