eddwo wrote:
I really liked Head First Design Patterns
but I'm still struggling with this aspect myself.

Its all very well teaching people the basics with silly examples like
 
abstract class  Animal
{
    MakeNoise();
 }

class Dog : Animal
{
     MakeNoise()
    { 
        bark();
     }
}

but that doesn't help when trying to decide things like how much responsibility each class should be given, how to create useful abstractions and reusable object models etc.

Where do you go to learn the "real" stuff?

Get a job, write some code, make mistakes, and learn from them.

There isn't any cookie cutter approach that will give you that kind of knowledge.  That's why we are computer scientists, not computer operators.  We should be experimenting with the stuff that isn't obvious.  Smiley

I've seen people OOP a concept to death in that they will write an object that manages a collection of other objects which do nothing more than return data.  Is this OOP?  Technically, yeah, but it is sure is a lot of code to go through.

Also, don't let OOP run your world.  Your job is to write software that does stuff.  It may be neat to completely encapsulate every object in such a way that Dog inherits from Animal, but if, right now, you only need a Dog, don't waste time working on Animal. 

Chances are, you'll know when you need to use inheritance because you'll find yourself writing similar code on similar objects.