Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I know design principles and design patterns in theory. But it's embarrassing to realize that it's not clear when applying it in practice.

For example, let's think about simple tic tac toe console game. How would you decide what class is for certain responsibility and how do you determine that responsibility/class is necessary for the system even before applying design patterns or principles? I understand it should be based on requirements and domain knowledge, but there are lots ways to satisfy the requirements. If I take agile (TDD) approach, there are still many ways to achieve the key requirement by making it testable.

I wish there are many materials (books or web sites) on this but most of them are on theory but rarely on practices. I would appreciate if anybody can refer to such materials with which I can practice the class designs.

share|improve this question
    
It's something you gain with experience. You try a pattern, watch it fail miserably, and gain a better understanding of how that pattern might have helped under different circumstances. I could try to explain it, but it's like teaching someone how to program by explaining it. It's more of a hands-on knowledge. –  Neil Sep 16 '11 at 16:25
    
@S.Lott, I meant there are several ways by using subjective –  Paul Sep 16 '11 at 16:37
    
@Neil That's a whole point of this question. I don't want to fail miserably and I want to tune my skills from other's mistakes and experiences. –  Paul Sep 16 '11 at 16:43
    
@S.Lott Corrected. Thanks for pointing it out. –  Paul Sep 16 '11 at 16:46
2  
I really don't see an alternative to failing miserably somewhere along the line. That in itself is vital experience. –  Philip Sep 16 '11 at 17:52
show 3 more comments

5 Answers

up vote 2 down vote accepted

If I take agile (TDD) approach, there are still many ways to achieve the key requirement by making it testable.

Yes, however, when you make the wrong decisions, there are some pretty blatant signals.

You will find very quickly that you have classes where there is nothing to test. When you find those, rethink your design, you probably don't need that class.

On the other hand, you'll also find code where you feel you are swimming in treacle while trying to write the tests. Again, rethink your design, it probably needs an abstraction layer you don't have yet.

In other cases, you'll find tests that fail intermittently. Again, this requires a rethink. Tests should always pass or always fail against the same piece of code; if they're dependant on situation then you need to abstract something out.

There are other lessons to learn but, as has been pointed out, you'll learn them more by trial and error than by reading.

However, if you really want an example to read through, there is a particularly good one in Robert C Martin's Agile Software Development, Principles, Patterns, and Practices, where two developers pair up to code a bowling score card TDD-style. They manage to fall into many of the traps of "obvious" design and show you how to adjust your design accordingly.

share|improve this answer
    
Thanks for excellent answer. I will try to pick up the book. –  Paul Sep 16 '11 at 16:59
    
@Paul: It's a great book for many reasons. I recommend it to all devs who feel instinctively that Agile is the way to go, but don't really know how to put it into practice. There is a Java version and a C# version too. –  pdr Sep 16 '11 at 17:03
add comment

Read this: http://en.wikipedia.org/wiki/Class-responsibility-collaboration_card.

And this: http://alistair.cockburn.us/Using+CRC+cards

And this: http://c2.com/doc/oopsla89/paper.html

It helps understand how to arrive at a sensible allocation of responsibility for a class definition.

share|improve this answer
    
Thanks for your reference. I almost forgot about CRC but I can see it can help. –  Paul Sep 16 '11 at 17:14
add comment

The books are written on the theory because in practice it only works for that practice example. I'd suggest just making a lot of small projects. Or analyzing the architecture of large projects.

If you feel crippled by indecision about the multiple ways in which you could solve a problem, simply pick one. Start at the beginning, work through the middle, and stop when you reach the end. Chances are you'll scrap your original decision and go with something else, but that's ok. Plan a little, work a little. Too much or too little of either will be detrimental to the whole.

Tic tac toe: First you need a board. You need a lot of other things, but you're having problems starting out, so the board is a good a place to start as any. A board has a 2D array of fields that can have three states. It needs to test for win conditions. It needs to tests for ties. These are things that COULD be done by some other object, but we're starting with the board. That could change in the future, but it's good for now.

share|improve this answer
    
+1 for "simply pick one" –  Davy8 Sep 16 '11 at 21:09
    
Thanks for your answer. I think I usually do what you described here. One thing I realized is that I haven't practice as much as you recommended. Do you have any list of small projects for design practice? –  Paul Sep 16 '11 at 23:10
    
Rogue-likes games, old arcade games (tetris, pacman, etc), a-life simulations, a graphics demo with SDL or openGL, stenography, simple AI for poker... uh... really just anything that floats your boat. –  Philip Sep 19 '11 at 13:41
add comment

There are certainly multiple ways to design a software system. Some designs are better than others, and some may be equally good. Also, software systems grow and evolve, meaning that a design that is good today may not be so good tomorrow, when a new feature must be added.

Nevertheless there are tried and tested principles and practices that help you to figure out how to decompose your problem into classes, objects, algorithms, patterns, etc. Remember, writing software is an iterative process, just like writing a book. You never get it right on the first try. Instead, you start with a rough draft, and keep refining it. The two main concepts are cohesion and coupling. You want your modules to have high cohesion, epitomized by the single responsibility principle, and low coupling. You also want your design to make sense to other people, so that it can be easily understood, implemented, or augmented.

One good approach is to draw a high-level diagram of your system, e. g. using UML. If it looks like a mess, or if parts of it do not make sense, you probably want to refactor. It may also help to explain your system to somebody else. First, you will likely see design flaws yourself as you talk. Second if other people find your design hard to follow, you should probably fix it. Another way is to explain your design in writing. Then read it. Does it make sense to you? Then give it to other people to read. Does it make sense to them?

Designing software does get easier with experience. You develop a sense for code smells, which point you to potential design flaws. However, even if you have been writing code for decades, it is still a good idea to explain your designs to other people and see if they can follow your code.

To be more concrete, I highly recommend the following two books: Clean Code by Robert C. Martin, and Refactoring by Martin Fowler.

share|improve this answer
add comment

There's no "one way" to design a piece of software. There are plenty for each case.

Creating a BDUF (Big design up front) is usually a bad idea, because you either over-design or prepare for things that are never going to be used, or even making future clean implementations impossible, retrospectively.

I recommend starting out with a top-down evolutionary design. Always keep the next highest-level piece and its immediate collaborators in mind, instantiating classes which do not yet exist as you find necessary in the caller's code, and then implementing those classes once you know that the way your code is implemented truly requires them.

The important thing to remember is that, as you progress, constantly refactor and remove duplication.

Using TDD you can do everything I said and make everything testable.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.