views:

863

answers:

12

I recently read an interesting comment on an OOP related question in which one user objected to creating a "Manager" class:

Please remove the word manager from your vocabulary when talking about class names. The name of the class should be descriptive of its' purpose. Manager is just another word for dumping ground. Any functionality will fit there. The word has been the cause of many extremely bad designs

This comment embodies my struggle to become a good object-oriented developer. I have been doing procedural code for a long time at an organization with only procedural coders. It seems like the main strategy behind the relatively little OO code we produce is to break the problem down into classes that are easily identifiable as discrete units and then put the left over/generalized bits in a "Manager" class.

How can I break my procedural habits (like the Manager class)? Most OO articles/books, etc. use examples of problems that are inherently easy to transform into object groups (e.g., Vehicle -> Car) and thus do not provide much guidance for breaking down more complex systems.

+7  A: 

The single responsibility principle helps me break objects into manageable classes that make sense.

Each object should do one thing, and do it well without exposing how it works internally to other objects that need to use it.

Chris Ballance
+5  A: 

You can learn good object-oriented design principles by studying design patterns. Code Complete 2 is a good book to read on the topic. Naturally, the best way to ingrain good programming principles into your mind is to practice them constantly by applying them to your own coding projects.

Bill the Lizard
Agreed. I don't think you can understand OO without understanding the problems it's meant to solve, and that basically means understanding design patterns.
dsimcha
I agree with your post, except that I don't think Code Complete 2 is necessarily a good book on "design patterns". It is a little lower level, a little finer grained than that imo. Maybe GoF, HeadFirst Design Patterns, or something.
Simucal
Code Complete only presents about a dozen design patterns, but I think it's a better book on program design in general than either GoF or HFDP.
Bill the Lizard
+6  A: 

Reading and then practicing OO principles is what works for me. Head First Object-Oriented Analysis & Design works you through examples to make a solution that is OO and then ways to make the solution better.

Craig Delthony
The main objection I have with the Head First books is that they simplify things too much. It's much easier to separate concerns and obey SRP when you're dealing with a system designed around DogDoors and GuitarSpecs.
vg1890
Real life code is much more complex than the examples in Head First books. I bet the problem for the authors is making their examples clear enough to see what they are teaching but complex enough to seem like a real world example.
Craig Delthony
+5  A: 

A 'manager' class will often:

  1. Interogate something's state
  2. Make a decision based on that state

As an antidote or contrast to that, Object-Oriented design would encourage you to design class APIs where you "tell don't ask" the class itself to do things itself (and to encapsulate its own state): for more about "tell don't ask" see e.g. here and here (and maybe someone else has a better explanation of "tell don't ask" but these are first two articles that Google found for me).

It seems like the main strategy the little OO code we produce is to break the problem down into classes that are easily identifiable as discrete units and then put the left over/generalized bits in a "Manager" class.

That may well be true even at the best of times. Coplien talked about this towards the end of his Advanced C++: Programming Styles and Idioms book: he said that in a system, you tend to have:

  • Self-contained objects
  • And, "transactions", which act on other objects

Take, for example, an airplane (and I'm sorry for giving you another vehicular example; I'm paraphrasing him):

  • The 'objects' might include the ailerons, the rudder, and the thrust
  • The 'manager' or autpilot would implement various commands or transactions

For example, the "turn right" transaction includes:

  • flaps.right.up()
  • flaps.left.down()
  • rudder.right()
  • thrust.increase()

So I think it's true that you have transactions, which cut across or use the various relatively-passive 'objects'; in an application, for example, the "whatever" user-command will end up being implemented by (and therefore, invoking) various objects from every layer (e.g. the UI, the middle layer, and the DB layer).

So I think it's true that to a certain extent you will have 'bits left over'; it's a matter of degree though: perhaps you ought to want as much of the code as possible to be self-contained, and encapsulating, and everything ... and the bits left over, which use (or depend on) everything else, should be given/using an API which hides as much as possible and which does as much as possible, and which therefore takes as much responsibility (implementation details) as possible away from the so-called manager.

Unfortunately I've only read of this concept in that one book (Advanced C++) and can't link you to something online for a clearer explanation than this paraphrase of mine.

ChrisW
+12  A: 

First of all, I'd stop acting like procedural code is wrong. It's the right tool for some jobs. OO is also the right tool for some jobs. So is functional. Each paradigm is just a different point of view of computation, and exists because it's convenient for certain problems, not because it's the only right way to program. In principle, all three paradigms are mathematically equivalent, so use whichever one best maps to the problem domain. IMHO, if using a multiparadigm language it's even ok to blend paradigms within a module if different subproblems are best modeled by different worldviews.

Secondly, I'd read up on design patterns. It's hard to understand OO without some examples of the real-world problems it's good for solving. Head First Design Patterns is a good read, as it answers a lot of the "why" of OO.

dsimcha
+2  A: 

How many OOP programmers does it take to change a light bulb?

None, the light bulb changes itself.

;)

jms
+1  A: 

i think you should start it with a good plan. planning using CLASS Diagrams would be a good start.

you should identify the ENTITIES needed in the applicaiton, then define each entitie's ATTRIBUTES, and METHODS. if there are repeated ones, you could now re-define your entities in a way that inheritance could be done, to avoid redundancy. :D.

bgreen1989
+3  A: 

You can play around with an OO language that has very bad procedural support like Smalltalk. The message sending paradigm will force you into OO thinking.

soemirno
My thinking exactly. Use a language where you can't fall back to procedural thinking (like Java or *guhk* C++) and has a good OO model. Smalltalk is an excellent choice.
Schwern
+9  A: 

Becoming good at OO takes years of practice and study of good OO code, ideally with a mentor. Remember that OO is just one means to an end. That being said, here are some general guidelines that work for me:

  • Favor composition over inheritance. Read and re-read the first chapter of the GoF book.
  • Obey the Law of Demeter ("tell, don't ask")
  • Try to use inheritance only to achieve polymorphism. When you extend one class from another, do so with the idea that you'll be invoking the behavior of that class through a reference to the base class. ALL the public methods of the base class should make sense for the subclass.
  • Don't get hung up on modeling. Build a working prototype to inform your design.
  • Embrace refactoring. Read the first few chapters of Fowler's book.
jcrossley3
+1 for the mentor.
Nikhil Chelliah
On point #3 - what about inheritance for the sake of code re-use? That is, I want to re-use all the code from my parent and add 1 more capability. This scenario doesn't obey your statement about invoking behavior through a reference to the base class. Is this bad?
vg1890
A: 

My eureka moment for understanding object-oriented design was when I read Erin Evans' book "Domain-Driven Design: Tackling Complexity in the Heart of Software". Or the "Domain Driven Design Quickly" mini-book (which is available online as a free PDF) if you are cheap or impatient. :)

Any time you have a "Manager" class or any static singleton instances, you are probably building a procedural design.

cpeterso
+3  A: 

How can I break my procedural habits (like the Manager class)?

Make a class for what the manager is managing (for example, if you have a ConnectionManager class, make a class for a Connection). Move everything into that class.

The reason "manager" is a poor name in OOP is that one of the core ideas in OOP is that objects should manage themselves.

Don't be afraid to make small classes. Coming from a procedural background, you may think it isn't worth the effort to make a class unless it's a thousand lines of code and is some core concept in your domain. Think smaller. A ten line class is totally valid. Make little classes where you see they make sense (a Date, a MailingAddress) and then work your way up by composing classes out of those.

As you start to partition little pieces of your codebase into classes, the remaining procedural code soup will shrink. In that shrinking pool, you'll start to see other things that can be classes. Continue until the pool is empty.

munificent
A: 

I have a three step process, this is one that I have gone through successfully myself. Later I met an ex-teacher turned programmer (now very experienced) who explained to me exactly why this method worked so well, there's some psychology involved but it's essentially all about maintaining control and confidence as you learn. Here it is:

  1. Learn what test driven development (TDD) is. You can comfortably do this with procedural code so you don't need to start working with objects yet if you don't want to. The second step depends on this.

  2. Pick up a copy of Refactoring: Improving the Design of Existing Code by Martin Fowler. It's essentially a catalogue of little changes that you can make to existing code. You can't refactor properly without tests though. What this allows you to do is to mess with the code without worrying that everything will break. Tests and refactoring take away the paranoia and sense that you don't know what will happen, which is incredibly liberating. You left to basically play around. As you get more confident with that start exploring mocks for testing the interactions between objects.

  3. Now comes the big that most people, mistakenly start with, it's good stuff but it should really come third. At this point you can should reading about design patterns, code smells (that's a good one to Google) and object oriented design principles. Also learn about user stories or use cases as these give you good initial candidate classes when writing new applications, which is a good solution to the "where do I start?" problem when writing apps.

And that's it! Proven goodness! Let me know how it goes.

Ollie Saunders