tags:

views:

275

answers:

4

I'm trying to wrap my head around Object Oriented programming. But I'm having some trouble. I (think) I understand the general concepts and the arguments for why OOP is a 'good' design. My problem comes when I sit down and try to write code that is OOP.

I tend to end up with programs that are either very procedural but have the occasional object tossed in for good measure....or programs that seem ridiculously long and complex for what they are doing....everything is an object; but there are many, many objects and the inheritance trees get long and ugly.

What I've been trying to find is some non-trivial examples (I've seen plenty of throw-away/pseudo code involving cats, dogs, and animals....but they don't seem to help when I actually try to code up something that needs to do something) of really well designed OOP source code. Ideally, I'm looking for something that would step me through the thought process. Like 'Okay - here is some procedural code that does XYZ. Now, here is some really great OOP code that does the same thing!'.

Thanks

+1  A: 

I know this is tagged .net, but a good example would come from PHP. Before PHP5, PHP was only partially object-oriented. Many PHP developers had the very same problem of grasping OOP.

Zend has a pretty good article here. It should be pretty easy to follow.

If you feel like you need better direction with the object oriented design process, you may want to check out MIT's Open CourseWare, specifically Foundations of Software Engineering (Lecture Notes #2) or something similar.

Jim Schubert
Incorrect. PHP4 was partially object oriented: http://us2.php.net/manual/en/language.oop.php
Robert Venables
Thanks for pointing that out, Robert. I considered it strictly procedural because it didn't fully support objects. I suppose that wasn't the best way to phrase it.
Jim Schubert
+2  A: 

The problem procedural programmers often have when starting object-oriented programming is they keep designing procedures and try to organize them as objects. That doesn't work.

Object-oriented programming is a different design methodology; a different way of thinking about the roles and responsibilities you distribute throughout your code, not just a different coding methodology.

When you get past the "dog is-a mammal" metaphors (which never translate to real applications), I would recommend this book: Object Design: Roles, Responsibilities, and Collaborations. It was the first book I read where I finally got why I had to stop viewing (in my case) C++ as "C with classes thrown in".

Robert Cartaino
+2  A: 

One very intuitive transition from a C API to a C++ API happens a lot in databases; so for this example, we'll take a quick look at the difference in using the MySQL APIs.

I'm not sure if I can copy the code from these sites (no idea what license it is), but look at the section labeled "Creating a Database" for a C demonstration, and Sample #1 for a C++ demonstration; both of these step through creating a MySQL database programmatically.

In the C API, the first argument to every function is a "handle" to the database. In the C++ API, we work with a database connection object which implicitly calls the C API with its private handle.

To look at one very specific example, to execute the query once it's been generated we have in C:

mysql_query(conn, "create database testdb")

and in C++:

query.execute();

The big difference here is that the C++ binding only shows you as much as you need to see, while in C you have to be very explicit about every little detail.

I think the database APIs are a good way to pick up some OOP principles by example, so hopefully they can help you out too.

Mark Rushakoff
+9  A: 

The reality is that such conversions wouldn't generally be good object oriented code. Why? Because object oriented code isn't simply moving functions into methods and data into members.

Instead, a good object should be responsible for all of its data and only take method parameters where those parameters are defining the data that will be operated on.

This means there isn't a 1:1 mapping from procedural functions and procedural data structures to the object oriented ones.

Having taking a look around I didn't find any examples I cared for online, so I will simply give my refactoring rules for converting procedural code to OOP.

The first step is to simply package each module as an object. In other words, just create an object that holds the data and the functions. This is horrible to a purist, but you have to start somewhere. For example, if you had a BankAccount module, you will now have a BankAccount object.

Obviously the functions were having the data passed into them from external calls. Here you are looking for how to internalize that data and make it private as much as possible. The goal should be that you get your data in your constructor (at least the starting point) and remove the parameters that used to receive the data manually and replace them with references to that now private data. Using the BankAccount object, all access to the account is now via methods on the object and the actual account data has been internalized.

Many of your functions probably returned modified versions of the data structures: stop returning that data directly and have these modifications stay within the private structures. Create accessor properties that return your now private data where necessary and mark them "obsolete" (your goal is to make the object the master of its data and only return results not the internal data). With the BankAccount object, we no longer return the actual account data, but we have properties for CurrentBalance and methods such as AverageBalance(int days) to see the account.

Eventually you will have a set of self contained objects which still bear little resemblance to what you would have done if you started with objects in your design, but at least you can continue your refactoring with your new objects. My next step usually to discover that the objects created from such refactoring have way to many responsibilities. At this point some common threads probably have been detected and you should create objects to refactor these common ideas into. If we have BankAccount, we probably have other account types and if we align the methods of all of these account types we can make Account as a base class that implements all the shared features while BackAccount, SavingsAccount and others implement the details.

Once the class structure starts to take shape it is time to feel better about the conversion. Refactoring is a process, not an endpoint, so I generally find that my class structure continues to evolve. One of the nice things about having gotten this far is that your data is private and manipulated through methods, so you can refactor the internals more and more freely as you progress.

One thing that makes this plausible to do is having good unit tests. When doing procedural to OOP conversions, I often keep the old code around as the "baseline" so I can test against it. I.e., the test can verify against the old procedural system's results. If you don't match up, it is a good idea to find out why. I find that often there is a bug... but sometimes your new cleaner code is actually doing something right that was wrong in the past.

Regarding creating "too deep" of object trees: that can be the result of getting too obsessive about inheritance. I find that composites often are a better idea, where you implemented the interfaces for multiple objects rather than trying to get all those features into a single parent object. If you are finding yourself creating parent objects that simply are blends of feature sets, consider simplifying by making an interface for each feature set and implementing those interfaces.

Godeke
Thanks for the post. I wouldn't expect a 1-1 mapping between the code. But, I was hoping we could take a problem like: http://thedailywtf.com/Articles/Knocking-Me-Off-The-Perch.aspx and implement a procedural solution and then compare/contrast it to an oo solution.
Rob P.
Yeah, I looked around and all the conversions are tiny examples that don't bring the real world concerns to the table. The problem with dog houses is that they can stand up if they are designed "wrong" as long as you use enough nails :)
Godeke