tags:

views:

666

answers:

12

I'm reading a lot about good and bad practices in OOP design. It's nice to know your design is bad, or good. But how do you get from bad to good design? I've split the interface (xaml) and codebehind from the main businesslogic class. That last class is growing big. I've tried splitting it up into smaller classes, but I'm stuck now. Any ideas on how to split large classes? The main class has 1 list of data of different types. I'm doing calculations on the total, but also on the individual types. I've got methods to perform these calculations which are called from events handled in the codebehind. Any ideas where to go from here?

Additional Info:

We are already about 6 months into this project. I've worked with object oriented laguages for years (first c++, java and now c#), but never on a large project like this one. I believe we've made some wrong turns in the beginning and I think we need to correct these. I can't specify any details on this project at the moment. I'm going to order one or two books about design. If I separate all the classes, how do I stick them back together? Maybe it's even better to continue this way to the first release and rebuilt parts after that, for a second release?

+10  A: 

Practice and read. Repeat :)

Some recommended books:

  • Clean Code by Robert C Martin
  • GoF Design Patterns
  • Refactoring by Martin Fowler

Personally I also liked Head First Design Patterns, but the style may not be for everyone. There's a similar book called C# 3.0 Design Patterns (see ora.com). It has much of the same stuff, but in a more traditional manner.

Brian Rasmussen
An additional + for refactoring. If you do nothing else, memorize chapter 3--the bad code smells--and understand the reasons behind them. Just understanding the principles behind these "Smells" will help your OO code more than any other single chapter I've ever read.
Bill K
I agree - knowing how to identify code smells is very valuable.
Brian Rasmussen
+4  A: 

I highly recommend picking up Code Complete. It's a great book that offers tons of good advice on questions like yours.

To give you a quick answer to your question about how to split large classes, here's a good rule of thumb: make your class responsible for one thing, and one thing only. When you start thinking like that, you quickly can identify code that doesn't belong. If something doesn't belong, factor it out into a new class, and use it from your original class.

Edit: Take that thinking down to the "method" level, too - make your methods responsible for one thing, and one thing only. Helps break large (>50 line) methods down very quickly into reusable chunks of code.

unforgiven3
Splitting resposibilities is what I want, but I've got so many methods depending on eachother. Small methods, with the exception of a few +/- 50 liners all 10 lines or less.
Sorskoot
Can you group those small methods into several categories?
unforgiven3
Yes. I've tried placing them in there own classes, but because they're all sharing and working on the same list it's hard to pull them apart. I even named the methods to there catagories, so I know what they're for by looking at there names.
Sorskoot
+2  A: 

Refactoring by Martin Fowler is an excellent book about how to change the design of your software without breaking it.

Design Patterns works similarly to algorithims but tells you how to combine objects to perform various useful tasks.

Finally Martin Fowler has a variety of useful design pattern for applications. For example Passive View

RS Conley
+1  A: 

Michael Feathers's "Working Effectively with Legacy Code" is supposed to be very good, but I'll confess that I haven't read it myself.

Same goes for "Refactoring to Patterns."

duffymo
+1  A: 

I found that working on a complex 'assignment' without help and then seeing how someone else did it was a big learning experience for me.

One assignment in particular was creating a bank-like program where we had to track transactions and be able to calculate interest earned and things like that. It really was my first OOP program and a really great one because of its complexity. It gets far too confusing (for a beginner) to do it in a linear style without making errors.

Joe Philllips
+3  A: 

Change the way you think about objects. Every object should have one very specific responsibility. If you have a class named something generic, like "MainBusinessLogic" you are probably doing something wrong.

Great place to start: Read David West’s Object Thinking.

The wording of 'responsibility' is a little ambiguous.
Joe Philllips
The naming of the that class is one of the things I think I did well. It's one noun, explaining what it is, like Counter (in a shop).
Sorskoot
Of all the books listed, Object Thinking is the most challenging - can take years to make sense of in the real world...
goofballLogic
+2  A: 

The main class has 1 list of data of different types. I'm doing calculations on the total, but also on the individual types. I've got methods to perform these calculations which are called from events handled in the codebehind. Any ideas where to go from here?

If there are a lot of computations based on the contents of the list have you considered moving operations into a customised list class? Same goes for the operations on the specific types, perhaps they could live inside the types?

In terms of performing similar yet different operations on different types consider using the state pattern (see this as a replacement for switch statements) which enables you to treat the entities in a uniform manner.

A lot of OOP is about throwing away a "top down"/micromanagement approach and considering a "bottom up"/self-sufficient approach. It's worth remembering that neither approach is "correct" in isolation. Creating maintainable code is about finding a sensible balance which requires a lot of thought and usually develops through experience.

Quibblesome
I like the idea of moving the list to a list class. I have to look into state pattern.
Sorskoot
+3  A: 

This is just an addendum to some fine book suggestions here.

The better I get at OO, the more I seem to reduce object size. It's not like I'm going for small object size or anything, but it seems to be happening.

Keeping them small, single responsibility, simple to use and understand--all critical. Each object should be as close to bullet-proof as possible, check your parameters and never allow your object into an invalid state. Define all valid states clearly in documentation.

Any time you create an class, create a test for that class. It not only tests your code, but forces you to consume your own design. Always think of your class from that "Outside view". Make sure you are not asking too much of the person using your class and anything you are asking of him should be documented in the interface. Often I just throw a quick main into a class if there is no testing framework available--it places an example of how to use your code right there in the same file.

In coding, nearly all my time is spent trying to figure out what someone else did. If I was able to just put out code using known or well documented APIs, my job would be trivial and schedules significantly shorter.

Design-first can be hard. Consider coding ability as similar to sports ability. Most of us play in our driveways, a few play on local sports teams. To do good up-front design on a complicated project is the task of a national league player, they are one in a million. Accept this and plan for change--iterations are your friend. (By the way, most of us THINK we're at the state level easily. We're not).

Bill K
Sorry to nitpick but don't you mean classes or types and not objects?
Brian Rasmussen
Yes, I have a disorder that causes me to substitute related terms in conversation at times. (Hmm, I wonder if there is such a thing! If there is, anyway, I have it--and if not, it should be named after me)
Bill K
+3  A: 

In addition to Brian's recommendation of Clean Code by Robert C Martin, you may want to read up on "Uncle Bob's" SOLID Principles of Object Oriented Design.

You can hear him talking about the SOLID Principles on Hanselminutes Podcast 145 and clean code on .NET Rocks! Show #388. There is also more with him on .NET Rocks! Show #410, but what he talks about isn't really related to your question, I just included it in case you enjoyed the first two.

Of the three podcasts I preferred the Hanselminutes.

Grant Wagner
I just love both those shows. I'm listening to the .net Rocks #410 right now :)
Sorskoot
A: 

I can only say what works for me, and I haven't really found anyone who works the same way, so I'm not sure if it will help you very much.

Basically, my approach is to have as few classes as possible, but no fewer.

First, the only time you need to save information is if you receive it at time A and need it at a later time B. If you get it and work on it at the same time, it may be that there's no need to store it.

Second, what do you do with it? If you're going to be iterating through it in certain simplified ways, you can think of it as an instruction set, and the program that operates on it as an interpreter of an instruction set. In that case, you might want to actually design a byte-code instruction set, with an interpreter, and encode your information in that instruction set.

Third, how often does the information change? If some of the information changes only very infrequently, you might be able to use partial evaluation (i.e. code generation). That is, you take the infrequently changing information and use it to generate an ad-hoc program that does what your overall program would do with that information, but much faster. The code generator is easy to write because it only deals with part of the input information, the part that changes slowly.

Much of the data structure I see these days exists to support UI. This is clearly not holding objects that the user cares about, and ideally you shouldn't have to care either. So I built a DSL for UIs that hides all that hoo-haw.

Stay away from events and notifications if you possibly can, because they happen at points in time and therefore represent incremental changes in state. You will have to be mightily worried about their possibly being dropped, duplicated, or misordered. Often they are used on the theory that a simple polling style would be "less efficient" when in fact often the opposite happens.

So when I see people getting all wrapped up in the technology of classes and so on, it is usually because they are making too big a deal of data structure.

Just my downvote-bait...

Mike Dunlavey
A: 

I recommend Feather's Working Effectively with Legacy Code, available and searchable on Safari, over Refactoring. It's full of useful and empathetic chapters like I Don't have Much Time and I Have to Change It and My Application Has No Structure.

Perspectives to consider:

  1. Automating design quality testing - look for tools which provide metrics on design quality, as a cross-check to your design decisions.
  2. Code Testability - do any of your refactorings help or hinder test-driven development, writing unit tests or writing functional tests? How hard will it be to test large pieces of the architecture once integrated?
  3. Justification - how do you defend these decisions, both from a cynical CYA to management and, more importantly, so your team believe in the re-design. Can you easily and consistently explain why a change was made and will that explanation be easy to find in 6 months time?

Personal preferences in design, especially refactoring:

  1. Classes for Concepts - when in doubt, if there is a single clear concept, it should be wrapped in a class rather than implied as a behaviour in one or methods.
  2. Lots of communicating little things with responsibilities are easier to think about and audit. When in doubt about how a design maps to the real world, go back to the old Responsibility-Driven-Design approach of writing down the responsibilities of each class. If you find this hard, your concepts of what each class does may be muddled or they are doing too much.
  3. Don't be scared by things being big if they are regular. Sometimes, eg: lots of event handlers on GUIs, you will legitimately have classes with far more methods or properties than metrics recommend.
Andy Dent
A: 

Try writing more testable code, this alone forced me to research and implement improved OOP practices/design concepts.

Toran Billups