I know that this is meant to be a fairly contrived example to demonstrate the idea at play. But I would say that as a rule of thumb, you want to exhaust every possible option before relying on Subtype polymorphism. Using composition is a better solution here just based on the domain.
Marriage does not define a "type" of person any more than race, wealth, interests, etc. So using subtype polymorphism to model this case isn't appropriate. On a more technical level, most languages only support single-inheritance. So if you did have classes for MarriedPerson
, PacificIslanderPerson
, and WealthyPerson
, you would have no way to "compose" these together to describe a wealthy married Pacific-Islander.
Instead you would use simple composition within your Person
class.
public class Person {
public MarriageStatus marriageStatus;
public Race race;
public Wealth wealth;
}
Here, MarriageStatus
, Race
, and Wealth
can all be single-responsibility, and probably pretty simple. An example of MarriageStatus might be.
public class MarriageStatus {
public Datetime anniversary;
public Person husband;
public Person wife;
// TODO: In the future the stakeholder would like to support polyamory
// public List<Person> spouses;
}
If you're using a programming language like Haskell or Rust with traits (typeclasses in Haskell parlance), then you can make Person automatically act like a MarriedPerson from the function's perspective. In a more traditional OOP languages, your business logic would simply work on MarriageStatus
, Race
, and Wealth
objects. They would only accept a Person
when interplay between those three composed properties are needed.
This way you've designed yourself out of the recursive relationship and all the pitfalls of it.
I apologize if I've entirely missed the point of your question. Specifically, you say
The Person object would still exist, which is bad.
I don't think that's necessarily true. If you return a MarriedPerson object and no references to the original "Person" are around, the garbage collector will just come along and remove the old "Person" object. I might be misunderstanding you though.
The Person object would still exist, which is bad
-- No, a copy of an object representing a person in the state it existed at some moment in the past would still exist.