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?
This is why I don't particularly care for modeling the real world and using that to explain/define OO philosophy.
Unless you're building a simulator (in which the answers to these questions become immediately obvious when one looks at the simulator context) it's far too easy to go off into the weeds and confuse yourself.
Perhaps having mutating state in an object is all wrong ;-)
Shouldn't Bird.takeoff() return a FlyingBird which can land()? (It clearly doesn't make sense for a bird which is not flying to land.)
Similarly, for an egg:
egg = Egg()
incubatedEgg = egg.incubate()
bird = incubatedEgg.hatch()
However, the return type of an operation might not always be limited to one value. For instance, during incubating the embryo could die.
egg = Egg()
egg.incubate() ->
if "still alive" (aliveEgg) ->
aliveEgg.hatch()
else "died in incubation" (deadEgg) ->
deadEgg.discard()
Happy philosophizing :-)
"Fly" reasonably means "begin flying!" (I would say it's used more often that way in natural speech). You don't have to interpret it to mean "sustain flight!" That's why we reasonably use "fly" and "land" (in natural english) as antonyms. (Evidence for the skeptical)
You're assuming the egg is created ex nihilo in your model. That's not really a reasonable assumption. The bird came from an egg, but the egg came from a bird. Assuming a reasonable purpose for modelling an egg, it's to model the time-delay procreation of bird species. That means the baby bird is formed by the sexual reproduction of two other birds. The vehicle of generation of the baby bird is the egg. The egg doesn't need to know what kind of bird is in it; that bird was created by the parents and wrapped in a generic egg (that perhaps has variable properties based on the mother.
In other words, your model isn't really well-formed with regard to this question.
It's the egg's responsibility to see that it doesn't hatch again (a check in the hatch function). See the discussion of "imperatives" below.
The concept of "breakable" is probably superfluous to your model. Are you concerned with the egg's breakability, or it's hatchability? If the breaking is a significant part of the data you are trying to model (for example, if anything in your model essentially depends upon the brokenness of the egg, then model it. Either way, hatching doesn't happen when the egg breaks, it happens when the egg hatches. One of the consequences of hatching is breaking, not the other way around.
The reason egg.cook() seems wrong is because it is. Verb methods should take the object as the subject. Your statement "egg.cook()" is telling the egg to cook, which is rarely how we intend to use the verb "cook" in commands with respect to eggs. You probably really want a chef.cook(food) -- where the "food" arg is anything that derives from food (or better yet, has a Role (a la Moose) "isCookable".
Remember, programming languages are imperative languages; interrogation, exhortation, and other "natural language" functions are inherently misplaced in programming languages, even if we achieve some version of it through commands (we emulate interrogation through commands; "TELL ME YOUR STATE!" is the imperative interpretation of the interrogative "What's your state?")
A good program removes as much information as possible from it's models while meeting all functional specifications. That is, you have a specification (preferably in the form of a test suite). Meaningless information is information that isn't necessary to pass any of the tests. So, you implement one feature at a time, introducing the cookability of eggs when you have a test that quantifies that property via specification (again, read: tests). If you implement breakability of objects and realize that you can accomplish the same tests without it, you improve the program by removing it. Now, a really clever programmer is able to build his models in such a way as that they are extensible -- that is, that new properties can be added easily as new requirement specifications are produced.
Note also, it is possible to have a bad specification. If you can't tell whether something is superfluous or not, the specs (read: tests) are either incorrect or incomplete with respect to that aspect of the program.
Seems more like an issue of naming. For example, in java you might query the bird's state as "getFly()", where you call it "fly()" as python would. Here, you're telling it to take off like you're changing the state of the bird, which is a little ambiguous to the reader of the code.
In the case of the Egg, I don't see any problem with the species of bird the egg handles as being part of the egg. As for who handles whether the egg is hatched, you could just keep a reference inside the egg to the bird that's hatched. An egg physically hatches, so it's up to me to see if it's hatched, so I don't stick an egg shell in an incubator for months expecting a chicken to pop out. In my opinion, it's up to the calling code to know who has hatched. If an egg hatches a bird, it's okay to keep a reference to that bird inside the egg if that helps keep your program organized. I would think it would be a convenience to anyone who uses that class.
There's no reason why you can't make an egg also be in a fried state, where any attempts to hatch it fail.
As to the absurdity of a different object for a bird in flight being a different bird than a grounded one, that's hard to justify. We may reason in our minds that it's the same bird, but in respect to object-oriented programming, we could certainly make them different objects if that meets the needs of the program. Is a bird more like an egg or a plane?
As to your Bird being breakable while it's in flight, is that the bird's fault? If my dog were in it's kennel, would it be broken if I told it to go fetch a ball?
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?
This depends on whether you want to keep around the egg remains in your model or consider the egg gone.
If the former, the hatch() method sets a private hatched flag to true and any of the egg's methods which depend on it being un-hatched (including hatch() itself) check that flag and fail (whether the failure is via return code or a raised exception, depends). The latter, might be doable with certain designs but don't really see any need to.
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().
I think that your problem here is with imprecise word usage, not with software development. The authors of the fly() solution are usng "fly" verb in a sense of "start flight", e.g. as a synonym of "takeoff". Whether that's a 100% valid English usage or not is above my pay grade as ESL developer, but I certainly agree that "takeoff" is a significantly less ambiguous method name.
... so that both Egg and Bird know their own kind, independently but consistently? Is there any well-known pattern for this kind of relationship?
I'm not sure if there's an official "pattern", but having an egg constructor (or factory if you go with factory) implemented in a generic "Bird" class pass the bird type to a new "Egg" object and vice versa having "Egg" object pass its bird type to the constructor (or factory) of "Bird" is very widely used.
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()
As a commenter already pointed out, the above is an OO design problem - the egg doesn't cook itself. Therefore you can't implement "cook" method on the egg in isolation (though some "set status to cooked" method might be needed for your model, if for no other reason than make it unhatcheable. However, the FriedEgg thing needs to be constructed (if needed) by a Cook object's "cook_egg" method - which also calls "set status to cooked" on an egg.
I don't think this really comes under OO philosophy. In my opinion, you're mixing business logic with design patterns.
To address your concerns in general, if you have a method that changes the state of the object, which can cause another method to throw an exception based on this new (changed) state, it is desirable to have a state-inspection method so that you don't have to explicitly handle the exception each time.
In your case, let's say you did egg.fry() (on another note, an egg cannot fry itself. So maybe you need a Cook class that takes an egg as an argument to a fry method and returns a FriedEgg instance), and after that you did egg.hatch(). The second call must return an exception. Instead of forcing the user of your class to put that call in an explicit try...catch block, you should provide a state-inspection method; something like isFried() or isHatchable(). Then instead of:
try {
egg.hatch();
}
catch(UnhatchableException e) {
...
}
You have:
if(egg.isHatchable()) {
egg.hatch();
}
So it is the responsibility of the calling code to check and see what state the object is in, before it performs an operation that can potentially throw an exception.
Now assuming you had a Cook class and a method called fry and you did Cook.fry(egg) which returns a FriedEgg instance, what would happen if you call hatch() on it? Common sense would tell you that a fried egg cannot hatch anything!
In this case, you should have an Egg interface with LiveEgg (i.e., an egg that can hatch) and FriedEgg both implementing the Egg interface. The difference however, is in the implementation of the hatch() method in FriedEgg; it would have to throw an UnsupportedOperationException because you cannot hatch a fried egg.
The problem with modeling most real-world scenarios (like cars, animals etc.) is that sometimes they don't help in adequately explaining the relationships. This is due to the fact that OO concepts are pretty abstract.
Anyway, hope this helps.
Related
I am quite confused with the Single Responsibility Principle. The Principle states that there should only be one reason for the class to change.
The problem which I am facing is, any change to a method or any logic change in doing things would change the class. For example, consider the following class:
class Person{
public void eat(){ };
public void walk(){ };
public void breathe(){ };
public void run(){ };
public void driveCar(Car car){ };
}
Uncle Bob describes it as there should ONLY be a single person/Actor responsible for the change. I have the following two questions:
For the above class who is the actor/Person who can be responsible for change?
Wouldn't any change in the logic of eating, breathing or walking change the class Person? So doesn't that mean that every method is a reason to change as it's logic to doing things might change?
What is a reason to change
For the above class who is the actor/Person who can be responsible for the change?
An Actor is a user (including clients, stakeholders, developers, organizations) or an external system. We can argue if people are systems, yet that is not here nor there.
See also: Use case.
Wouldn't any change in the logic of eating, breathing or walking change the class Person? So doesn't that mean that every method is a reason to change as its logic to doing things might change?
No, a method is not a reason to change. A method is something that can change... but why would it? What would trigger the developer to change it?
Part of the single responsibility principle is that code should interact at most with one external system. Remember that not all actors are external systems, however, some are. I think most people will find this part of the SRP easy to understand because interaction with an external system is something we can see in the code.
However, that is not enough. For example, if your code has to compute taxes, you can hardcode the tax rate in your code. That way, it is not interacting with any external system (it is just using a constant). However, one tax reform later, the government has been revealed as a reason to change your code.
Something you should be able to do is interchange external systems (perhaps with some additional coding effort). For example, changing from one database engine to another. However, we do not want one of these changes to translate into a total rewrite of the code. Changes should not propagate, and making a change should not break something else. To ensure that, we want all the code that deals with the database engine (in this example) to be isolated.
Things that change for the same reasons should be grouped together, things that change for different reasons should be separated.
-- Robert C Martin
We can do something similar with the government example above. We probably do not want the software reading the minute of the congress, instead, we can have it reading a configuration file. Now the external system is the file system, and there would be code to interact with it, and that code should not interact with anything else.
How do we identify those reasons to change?
Your code is defined by a set of requirements. Some are functional, others not. If any of those requirements change, your code has to change. A reason to change requirements is a reason to change your code.
Note: It is possible that you do not have all your requirement documented, however, an undocumented requirement is still a requirement.
Then, you need to know from where do those requirements come from. Who or what could change them? Those are your reasons for change. It could be a change in the politics of the company, it could be a feature we are adding, it could be a new law, it could be that we are migrating to a different database engine, or different operating system, translating to another language, adapting to another country, etc.
Some of those things are externals systems with which your code interacts (e.g. the database engine), some are not (the politics of the company).
What to do with responsibilities
You want to isolate them. So you will have code that interacts with the database, and nothing else. And you will have code that implements business rules, and nothing else. And so on.
Realize that even though the implementation of each part of your code will depend on something external, their interface does not have to. Thus, define interfaces and inject dependencies, so that you can change the implementation of each part without having to change the others… that is, the implementation of parts of your code should not be a reason to change the implementation of other parts of your code.
Note: No part of your code should have multiple responsibilities. Have parts of your code deal with each responsibility, and have part of your code with the responsibility of bringing other parts together. Similarly, if a part of your code has no responsibility… there is no reason to keep it. Thus, every part of your code should have exactly one responsibility.
For your code, ask yourself, what are the requirements of the Person class. are they complete? From where do they come from? Why would they change?
Recommended viewing
For a more authoritative explanation of the single responsibility principle, see Robert C Martin - The Single Responsibility Principle (51 minutes, 8 seconds, English language) at the Norwegian Developers Conference, 2015.
Interesting question. The quote from "Uncle Bob" Martin is:
A class should have one, and only one, reason to change.
One could interpret this as saying that your Person class has five reasons to change: you might want to change the eat method, or change the walk method, or the breathe method, or the run method, or the driveTheCar method. But this is too narrow, and doesn't really capture what Martin meant by a "reason to change".
A reason to change a class means a human programmer's motivation for changing it. You would not change the eat method simply because you were motivated to change the eat method; you would change it to achieve some goal regarding a desired behaviour of your program.
If the Person class models a person for some sort of simulation, then your motivation for changing it would be that you want "to change how people's actions are modelled in the simulation". Every change you make to the class would be motivated by that reason, whether you changed one method or more than one; so the Person class has only one "reason" to change, fulfilling the SRP.
If the Person class had some other methods such as for drawing the person on the screen, then you might also want "to change the graphical appearance of your simulated people". This would be a completely different motivation than the motivation to change the way your simulation models people's actions, so the class would have two responsibilities, violating SRP.
I am reading about object oriented design principles. I came across the characteristics of a bad design.
It is hard to change because every change affects too many other
parts of the system. (Rigidity)
When you make a change, unexpected parts of the system break.
(Fragility)
It is hard to reuse in another application because it cannot be
disentangled from the current application. (Immobility)
I am able to understand the first two but the third one is little difficult for me to understand.
Is it about extracting common features of related classes in a Base class, making methods from the code which is repetitive ? But it says hard to reuse in another Application. Usually we write context specific code and Over-engineering is not a good idea, we have good principles like YAGNI (You ain't gonna need it) I find these ideas little contradicting.
Please provide your valuable thoughts for this.
Mobility example:
Assume the following classes:
Animal
Canine
Dog
As you would expect, Canine extends Animal and Dog extends Canine.
One way to poorly design Animal is to give it a method talk() which prints out bark. Perhaps, the original intent of this application was for dogs only and therefore the talk method barking was fine. But reusing this in another code base would lead to issues.
Say we want to extend Animal and create Bird. Birds don't bark :)
It's hard to image that someone would do this. But it happens all of the time. Base classes aren't abstracted out which leads to misplaced code making it difficult to correct/reuse.
One could argue that the Bird class could override the talk method. That would work, however, another developer extending Animal for yet another reason may forget to override that method... etc.
I know this isn't the best example, but it demonstrates the problem.
I know there are LOTS of reasons why you would compose a certain object inside another one. Some schools of thought have made explicit the reasons for architecting a program a certain way e.g. 'data-driven design' or 'domain-driven design'. I'm still a beginner to OOP, and it's often hard for me to understand why one object should be contained by another. Sometimes, I find myself with an object that seems awesome, and then I get to the point where I realize, "Okay, now I have to put this somewhere?" Is the reasoning behind this similar to where I'd decide put a file on my hard disk?
I have a couple guiding principles for this:
If it models a relationship in the physical world.
If the composer has data needed to construct the object.
If the composed object will be listening to the composer.
What do you look for when you make this decision?
Well, one very simple concept that helped me with this is simply the concept of "has a" versus "is a". Ask yourself, is the contained object something the containing object has, or is it something the containing object is? If it's something the containing object has, then containment is appropriate. Otherwise maybe you should be looking at inheritance.
A dog IS an animal, and has a nose, so it's:
class Animal
{
}
class Dog : Animal
{
Nose n;
}
Now this works fine. One "problem" with this approach is that you tightly couple noses and dogs, so sometimes you'll see things like containing an interface pointer rather than an object, or you might Google "Dependency Injection". But as the saying goes, "has a" and "is a" is often close enough for government work.
Early on, just try lots of examples and over time it will become natural. If you end up with spaghetti, throw some meatballs at it and try again! :)
What alternatives are you considering? Are you talking about Containment versus Inheritance, John Lockwood's comments about hasA and isA help with that issue.
Or are your perhaps talking about, Containment versus Association? There are various flavours of hasA. For example a Person may haveA Spouse, but clearly does not containA Spouse. There's a difference between changing a Spouse and changing a Nose.
The kinds of relationship that you consider:
Lifetime: Does it make sense to create a Person without a Nose? Can Noses exist without a Person? Can a Person exist without a Spouse? The answers to these questions drive the kind of operation you choose to have on Person. Probably don't need a setNose() method, though maybe we do need a wipeNose() method, and we probably do need a marry(Person) method.
Cardinality: How many Noses for a Person? How many Wheels and Seats does a Vehicle have? Answers to this determine the kinds of data structures? Just a reference? A list? An hash table?
I found it helpful to read about UML modeling, especially class diagrams. This reflects much experience of how to usefully capture various kinds of relationships.
Sometimes, I find myself with an
object that seems awesome, and then I
get to the point where I realize,
"Okay, now I have to put this
somewhere?"
From the above sentence, it sounds like you're trying to design from the bottom up. One of the thing's I've learned over the years is that top down design is the way to go. You should only write the class after you know where it needs to be used. Otherwise you just end up writing classes that "seem awesome" and contain code that might not be useful at all.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
There are two schools of thought on how to best extend, enhance, and reuse code in an object-oriented system:
Inheritance: extend the functionality of a class by creating a subclass. Override superclass members in the subclasses to provide new functionality. Make methods abstract/virtual to force subclasses to "fill-in-the-blanks" when the superclass wants a particular interface but is agnostic about its implementation.
Aggregation: create new functionality by taking other classes and combining them into a new class. Attach an common interface to this new class for interoperability with other code.
What are the benefits, costs, and consequences of each? Are there other alternatives?
I see this debate come up on a regular basis, but I don't think it's been asked on
Stack Overflow yet (though there is some related discussion). There's also a surprising lack of good Google results for it.
It's not a matter of which is the best, but of when to use what.
In the 'normal' cases a simple question is enough to find out if we need inheritance or aggregation.
If The new class is more or less as the original class. Use inheritance. The new class is now a subclass of the original class.
If the new class must have the original class. Use aggregation. The new class has now the original class as a member.
However, there is a big gray area. So we need several other tricks.
If we have used inheritance (or we plan to use it) but we only use part of the interface, or we are forced to override a lot of functionality to keep the correlation logical. Then we have a big nasty smell that indicates that we had to use aggregation.
If we have used aggregation (or we plan to use it) but we find out we need to copy almost all of the functionality. Then we have a smell that points in the direction of inheritance.
To cut it short. We should use aggregation if part of the interface is not used or has to be changed to avoid an illogical situation. We only need to use inheritance, if we need almost all of the functionality without major changes. And when in doubt, use Aggregation.
An other possibility for, the case that we have an class that needs part of the functionality of the original class, is to split the original class in a root class and a sub class. And let the new class inherit from the root class. But you should take care with this, not to create an illogical separation.
Lets add an example. We have a class 'Dog' with methods: 'Eat', 'Walk', 'Bark', 'Play'.
class Dog
Eat;
Walk;
Bark;
Play;
end;
We now need a class 'Cat', that needs 'Eat', 'Walk', 'Purr', and 'Play'. So first try to extend it from a Dog.
class Cat is Dog
Purr;
end;
Looks, alright, but wait. This cat can Bark (Cat lovers will kill me for that). And a barking cat violates the principles of the universe. So we need to override the Bark method so that it does nothing.
class Cat is Dog
Purr;
Bark = null;
end;
Ok, this works, but it smells bad. So lets try an aggregation:
class Cat
has Dog;
Eat = Dog.Eat;
Walk = Dog.Walk;
Play = Dog.Play;
Purr;
end;
Ok, this is nice. This cat does not bark anymore, not even silent. But still it has an internal dog that wants out. So lets try solution number three:
class Pet
Eat;
Walk;
Play;
end;
class Dog is Pet
Bark;
end;
class Cat is Pet
Purr;
end;
This is much cleaner. No internal dogs. And cats and dogs are at the same level. We can even introduce other pets to extend the model. Unless it is a fish, or something that does not walk. In that case we again need to refactor. But that is something for an other time.
At the beginning of GOF they state
Favor object composition over class inheritance.
This is further discussed here
The difference is typically expressed as the difference between "is a" and "has a". Inheritance, the "is a" relationship, is summed up nicely in the Liskov Substitution Principle. Aggregation, the "has a" relationship, is just that - it shows that the aggregating object has one of the aggregated objects.
Further distinctions exist as well - private inheritance in C++ indicates a "is implemented in terms of" relationship, which can also be modeled by the aggregation of (non-exposed) member objects as well.
Here's my most common argument:
In any object-oriented system, there are two parts to any class:
Its interface: the "public face" of the object. This is the set of capabilities it announces to the rest of the world. In a lot of languages, the set is well defined into a "class". Usually these are the method signatures of the object, though it varies a bit by language.
Its implementation: the "behind the scenes" work that the object does to satisfy its interface and provide functionality. This is typically the code and member data of the object.
One of the fundamental principles of OOP is that the implementation is encapsulated (ie:hidden) within the class; the only thing that outsiders should see is the interface.
When a subclass inherits from a subclass, it typically inherits both the implementation and the interface. This, in turn, means that you're forced to accept both as constraints on your class.
With aggregation, you get to choose either implementation or interface, or both -- but you're not forced into either. The functionality of an object is left up to the object itself. It can defer to other objects as it likes, but it's ultimately responsible for itself. In my experience, this leads to a more flexible system: one that's easier to modify.
So, whenever I'm developing object-oriented software, I almost always prefer aggregation over inheritance.
I gave an answer to "Is a" vs "Has a" : which one is better?.
Basically I agree with other folks: use inheritance only if your derived class truly is the type you're extending, not merely because it contains the same data. Remember that inheritance means the subclass gains the methods as well as the data.
Does it make sense for your derived class to have all the methods of the superclass? Or do you just quietly promise yourself that those methods should be ignored in the derived class? Or do you find yourself overriding methods from the superclass, making them no-ops so no one calls them inadvertently? Or giving hints to your API doc generation tool to omit the method from the doc?
Those are strong clues that aggregation is the better choice in that case.
I see a lot of "is-a vs. has-a; they're conceptually different" responses on this and the related questions.
The one thing I've found in my experience is that trying to determine whether a relationship is "is-a" or "has-a" is bound to fail. Even if you can correctly make that determination for the objects now, changing requirements mean that you'll probably be wrong at some point in the future.
Another thing I've found is that it's very hard to convert from inheritance to aggregation once there's a lot of code written around an inheritance hierarchy. Just switching from a superclass to an interface means changing nearly every subclass in the system.
And, as I mentioned elsewhere in this post, aggregation tends to be less flexible than inheritance.
So, you have a perfect storm of arguments against inheritance whenever you have to choose one or the other:
Your choice will likely be the wrong one at some point
Changing that choice is difficult once you've made it.
Inheritance tends to be a worse choice as it's more constraining.
Thus, I tend to choose aggregation -- even when there appears to be a strong is-a relationship.
The question is normally phrased as Composition vs. Inheritance, and it has been asked here before.
I wanted to make this a comment on the original question, but 300 characters bites [;<).
I think we need to be careful. First, there are more flavors than the two rather specific examples made in the question.
Also, I suggest that it is valuable not to confuse the objective with the instrument. One wants to make sure that the chosen technique or methodology supports achievement of the primary objective, but I don't thing out-of-context which-technique-is-best discussion is very useful. It does help to know the pitfalls of the different approaches along with their clear sweet spots.
For example, what are you out to accomplish, what do you have available to start with, and what are the constraints?
Are you creating a component framework, even a special purpose one? Are interfaces separable from implementations in the programming system or is it accomplished by a practice using a different sort of technology? Can you separate the inheritance structure of interfaces (if any) from the inheritance structure of classes that implement them? Is it important to hide the class structure of an implementation from the code that relies on the interfaces the implementation delivers? Are there multiple implementations to be usable at the same time or is the variation more over-time as a consequence of maintenance and enhancememt? This and more needs to be considered before you fixate on a tool or a methodology.
Finally, is it that important to lock distinctions in the abstraction and how you think of it (as in is-a versus has-a) to different features of the OO technology? Perhaps so, if it keeps the conceptual structure consistent and manageable for you and others. But it is wise not to be enslaved by that and the contortions you might end up making. Maybe it is best to stand back a level and not be so rigid (but leave good narration so others can tell what's up). [I look for what makes a particular portion of a program explainable, but some times I go for elegance when there is a bigger win. Not always the best idea.]
I'm an interface purist, and I am drawn to the kinds of problems and approaches where interface purism is appropriate, whether building a Java framework or organizing some COM implementations. That doesn't make it appropriate for everything, not even close to everything, even though I swear by it. (I have a couple of projects that appear to provide serious counter-examples against interface purism, so it will be interesting to see how I manage to cope.)
I'll cover the where-these-might-apply part. Here's an example of both, in a game scenario. Suppose, there's a game which has different types of soldiers. Each soldier can have a knapsack which can hold different things.
Inheritance here?
There's a marine, green beret & a sniper. These are types of soldiers. So, there's a base class Soldier with Marine, Green Beret & Sniper as derived classes
Aggregation here?
The knapsack can contain grenades, guns (different types), knife, medikit, etc. A soldier can be equipped with any of these at any given point in time, plus he can also have a bulletproof vest which acts as armor when attacked and his injury decreases to a certain percentage. The soldier class contains an object of bulletproof vest class and the knapsack class which contains references to these items.
I think it's not an either/or debate. It's just that:
is-a (inheritance) relationships occur less often than has-a (composition) relationships.
Inheritance is harder to get right, even when it's appropriate to use it, so due diligence has to be taken because it can break encapsulation, encourage tight coupling by exposing implementation and so forth.
Both have their place, but inheritance is riskier.
Although of course it wouldn't make sense to have a class Shape 'having-a' Point and a Square classes. Here inheritance is due.
People tend to think about inheritance first when trying to design something extensible, that is what's wrong.
Favour happens when both candidate qualifies. A and B are options and you favour A. The reason is that composition offers more extension/flexiblity possiblities than generalization. This extension/flexiblity refers mostly to runtime/dynamic flexibility.
The benefit is not immediately visible. To see the benefit you need to wait for the next unexpected change request. So in most cases those sticked to generlalization fails when compared to those who embraced composition(except one obvious case mentioned later). Hence the rule. From a learning point of view if you can implement a dependency injection successfully then you should know which one to favour and when. The rule helps you in making a decision as well; if you are not sure then select composition.
Summary: Composition :The coupling is reduced by just having some smaller things you plug into something bigger, and the bigger object just calls the smaller object back. Generlization: From an API point of view defining that a method can be overridden is a stronger commitment than defining that a method can be called. (very few occassions when Generalization wins). And never forget that with composition you are using inheritance too, from a interface instead of a big class
Both approaches are used to solve different problems. You don't always need to aggregate over two or more classes when inheriting from one class.
Sometimes you do have to aggregate a single class because that class is sealed or has otherwise non-virtual members you need to intercept so you create a proxy layer that obviously isn't valid in terms of inheritance but so long as the class you are proxying has an interface you can subscribe to this can work out fairly well.
I know about "class having a single reason to change". Now, what is that exactly? Are there some smells/signs that could tell that class does not have a single responsibility? Or could the real answer hide in YAGNI and only refactor to a single responsibility the first time your class changes?
The Single Responsibility Principle
There are many obvious cases, e.g. CoffeeAndSoupFactory. Coffee and soup in the same appliance can lead to quite distasteful results. In this example, the appliance might be broken into a HotWaterGenerator and some kind of Stirrer. Then a new CoffeeFactory and SoupFactory can be built from those components and any accidental mixing can be avoided.
Among the more subtle cases, the tension between data access objects (DAOs) and data transfer objects (DTOs) is very common. DAOs talk to the database, DTOs are serializable for transfer between processes and machines. Usually DAOs need a reference to your database framework, therefore they are unusable on your rich clients which neither have the database drivers installed nor have the necessary privileges to access the DB.
Code Smells
The methods in a class start to be grouped by areas of functionality ("these are the Coffee methods and these are the Soup methods").
Implementing many interfaces.
Write a brief, but accurate description of what the class does.
If the description contains the word "and" then it needs to be split.
Well, this principle is to be used with some salt... to avoid class explosion.
A single responsibility does not translate to single method classes. It means a single reason for existence... a service that the object provides for its clients.
A nice way to stay on the road... Use the object as person metaphor... If the object were a person, who would I ask to do this? Assign that responsibility to the corresponding class. However you wouldn't ask the same person to do your manage files, compute salaries, issue paychecks, and verify financial records... Why would you want a single object to do all these? (it's okay if a class takes on multiple responsibilities as long as they are all related and coherent.)
If you employ a CRC card, it's a nice subtle guideline. If you're having trouble getting all the responsibilities of that object on a CRC card, it's probably doing too much... a max of 7 would do as a good marker.
Another code smell from the refactoring book would be HUGE classes. Shotgun surgery would be another... making a change to one area in a class causes bugs in unrelated areas of the same class...
Finding that you are making changes to the same class for unrelated bug-fixes again and again is another indication that the class is doing too much.
A simple and practical method to check single responsibility (not only classes but also method of classes) is the name choice. When you design a class, if you easily find a name for the class that specify exactly what it defines, you're in the right way.
A difficulty to choose a name is near always a symptom of bad design.
the methods in your class should be cohesive...they should work together and make use of the same data structures internally. If you find you have too many methods that don't seem entirely well related, or seem to operate on different things, then quite likely you don't have a good single responsibility.
Often it's hard to initially find responsibilities, and sometimes you need to use the class in several different contexts and then refactor the class into two classes as you start to see the distinctions. Sometimes you find that it's because you are mixing an abstract and concrete concept together. They tend to be harder to see, and, again, use in different contexts will help clarify.
The obvious sign is when your class ends up looking like a Big Ball of Mud, which is really the opposite of SRP (single responsibility principle).
Basically, all the object's services should be focused on carrying out a single responsibility, meaning every time your class changes and adds a service which does not respect that, you know you're "deviating" from the "right" path ;)
The cause is usually due to some quick fixes hastily added to the class to repair some defects. So the reason why you are changing the class is usually the best criteria to detect if you are about to break the SRP.
Martin's Agile Principles, Patterns, and Practices in C# helped me a lot to grasp SRP. He defines SRP as:
A class should have only one reason to change.
So what is driving change?
Martin's answer is:
[...] each responsibility is an axis of change. (p. 116)
and further:
In the context of the SRP, we define a responsibility to be a reason for change. If you can think of more than one motive for changing a class, that class has more than one responsibility (p. 117)
In fact SRP is encapsulating change. If change happens, it should just have a local impact.
Where is YAGNI?
YAGNI can be nicely combined with SRP: When you apply YAGNI, you wait until some change is actually happening. If this happens you should be able to clearly see the responsibilities which are inferred from the reason(s) for change.
This also means that responsibilities can evolve with each new requirement and change. Thinking further SRP and YAGNI will provide you the means to think in flexible designs and architectures.
Perhaps a little more technical than other smells:
If you find you need several "friend" classes or functions, that's usually a good smell of bad SRP - because the required functionality is not actually exposed publically by your class.
If you end up with an excessively "deep" hierarchy (a long list of derived classes until you get to leaf classes) or "broad" hierarchy (many, many classes derived shallowly from a single parent class). It's usually a sign that the parent class does either too much or too little. Doing nothing is the limit of that, and yes, I have seen that in practice, with an "empty" parent class definition just to group together a bunch of unrelated classes in a single hierarchy.
I also find that refactoring to single responsibility is hard. By the time you finally get around to it, the different responsibilities of the class will have become entwined in the client code making it hard to factor one thing out without breaking the other thing. I'd rather err on the side of "too little" than "too much" myself.
Here are some things that help me figure out if my class is violating SRP:
Fill out the XML doc comments on a class. If you use words like if, and, but, except, when, etc., your classes probably is doing too much.
If your class is a domain service, it should have a verb in the name. Many times you have classes like "OrderService", which should probably be broken up into "GetOrderService", "SaveOrderService", "SubmitOrderService", etc.
If you end up with MethodA that uses MemberA and MethodB that uses MemberB and it is not part of some concurrency or versioning scheme, you might be violating SRP.
If you notice that you have a class that just delegates calls to a lot of other classes, you might be stuck in proxy class hell. This is especially true if you end up instantiating the proxy class everywhere when you could just use the specific classes directly. I have seen a lot of this. Think ProgramNameBL and ProgramNameDAL classes as a substitute for using a Repository pattern.
I've also been trying to get my head around the SOLID principles of OOD, specifically the single responsibility principle, aka SRP (as a side note the podcast with Jeff Atwood, Joel Spolsky and "Uncle Bob" is worth a listen). The big question for me is: What problems is SOLID trying to address?
OOP is all about modeling. The main purpose of modeling is to present a problem in a way that allows us to understand it and solve it. Modeling forces us to focus on the important details. At the same time we can use encapsulation to hide the "unimportant" details so that we only have to deal with them when absolutely necessary.
I guess you should ask yourself: What problem is your class trying to solve? Has the important information you need to solve this problem risen to the surface? Are the unimportant details tucked away so that you only have to think about them when absolutely necessary?
Thinking about these things results in programs that are easier to understand, maintain and extend. I think this is at the heart of OOD and the SOLID principles, including SRP.
Another rule of thumb I'd like to throw in is the following:
If you feel the need to either write some sort of cartesian product of cases in your test cases, or if you want to mock certain private methods of the class, Single Responsibility is violated.
I recently had this in the following way:
I had a cetain abstract syntax tree of a coroutine which will be generated into C later. For now, think of the nodes as Sequence, Iteration and Action. Sequence chains two coroutines, Iteration repeats a coroutine until a userdefined condition is true and Action performs a certain userdefined action. Furthermore, it is possible to annotate Actions and Iterations with codeblocks, which define the actions and conditions to evaluate as the coroutine walks ahead.
It was necessary to apply a certain transformation to all of these code blocks (for those interested: I needed to replace the conceptual user variables with actual implementation variables in order to prevent variable clashes. Those who know lisp macros can think of gensym in action :) ). Thus, the simplest thing that would work was a visitor which knows the operation internally and just calls them on the annotated code block of the Action and Iteration on visit and traverses all the syntax tree nodes. However, in this case, I'd have had to duplicate the assertion "transformation is applied" in my testcode for the visitAction-Method and the visitIteration-Method. In other words, I had to check the product test cases of the responsibilities Traversion (== {traverse iteration, traverse action, traverse sequence}) x Transformation (well, codeblock transformed, which blew up into iteration transformed and action transformed). Thus, I was tempted to use powermock to remove the transformation-Method and replace it with some 'return "I was transformed!";'-Stub.
However, according to the rule of thumb, I split the class into a class TreeModifier which contains a NodeModifier-instance, which provides methods modifyIteration, modifySequence, modifyCodeblock and so on. Thus, I could easily test the responsibility of traversing, calling the NodeModifier and reconstructing the tree and test the actual modification of the code blocks separately, thus removing the need for the product tests, because the responsibilities were separated now (into traversing and reconstructing and the concrete modification).
It also is interesting to notice that later on, I could heavily reuse the TreeModifier in various other transformations. :)
If you're finding troubles extending the functionality of the class without being afraid that you might end up breaking something else, or you cannot use class without modifying tons of its options which modify its behavior smells like your class doing too much.
Once I was working with the legacy class which had method "ZipAndClean", which was obviously zipping and cleaning specified folder...