I'm going to use this opportunity to put down a number of related but different thoughts on object-oriented philosophy, as a sort of request for comments. If the admins see fit to close, I may make it a blog post instead. But I am asking questions, so I think it's a suitable community wiki.
Suppose I have an abstract class Bird
. Supposing that we're taking a fairly typical OO philosophy here, Bird
is an object that is capable of maintaining and changing state. The obvious example of a state for this simple model is whether or not our Bird
is flying or not.
So, Bird
has a method fly()
.
Or does it? I've seen this very simple example given in introductory books, and in my opinion, it's subtly misleading. If you're maintaining state, you don't need to tell an object to continue to be in a given state; it just is. That's what an object does. So really, you want Bird
to have a method takeoff()
.
Suppose at some point we created our Bird
object from another class, Egg
. At some point, we might call a method Egg.hatch()
which has the return type Bird
. Assuming we're coding to an interface, Egg
knows what type of bird it's an egg from, and the returned instance from hatch()
knows what type of bird it is also. But we can't really say that there's anything in common between an Egg
and a Bird
that hatches from it. are we really saying that we want specific implementations of these classes to have a reference to an instance of some sort of BirdArchetype
that represents the characteristics of all instances of that species, so that both Egg
and Bird
know their own kind, independently but consistently? Is there any well-known pattern for this kind of relationship?
Whose responsibility is it to ensure that the Egg changes state so that it's no longer capable of hatching again, or doing most of the other things that eggs normally do?
Philosophically, do we want to introduce the concept of a breakable object? Perhaps Egg
should be coded so that it throws an exception if you try to do any of the things that change its state more than once. It's up to the calling code to keep track of the client object's lifecycle.
What else can we do with an egg? Perhaps we could cook it. If it were me, I'd code this so that calling cook()
on an egg 'breaks' the Egg
instance and returns an instance of a new object, FriedEgg
. How is this different to telling a Bird
to takeoff()
? It's absurd to suggest that taking off makes it a fundamentally different bird. Yet the reasoning is the same; a bird that's flying (usually) can't do most of the other things that birds do, because it's busy. Arguably then, Bird
is breakable as while it's in the flying state, calling some of its other methods won't work.
I suppose the only real distinction here is that the Bird
can become un-broken. Is the concept of thermodynamic reversibility really that crucial to programming very simple models like this?