This book is a masterpiece. Buy it and read it - it will make you a better programmer.
There are several Object-oriented concepts every programmer is familiar with. But is adhering to these concepts enough for writing reusable and maintainable code? How to identify that a class does too much or too little? How to distribute system intelligence? Is knowing how to inherit one class from another always reduces code duplication and makes these classes easier to work with? Or maybe composition is always preferable?
This book answers a lot of such questions by providing 60 practical guidelines on how to design classes and interactions between them. It also discusses cases where these guidelines contradict each other and gives practical advice on how to decide which approach to choose and why.
This book will be helpful for an experienced programmer, less so for a novice.
However, there are three things that might discourage an unknowing person from buying the book: it was published in 1996, the code examples are written in C++ and it is only available in paperback (or at least I was able to find only a printed version). One should nevertheless try to get their hands on it - the amount of wisdom it contains is enormous.