Software Design problems, anyone?
You learn math by solving problems. Problems frame the way you learn, give you a tangible proof that you’re progressing, give you a sense of meaning and achievement. How do you learn physics? By studying the books of course, but then solving physics problems is very important. How do you learn to play deep games such as chess or go? By playing, mostly. And then by pondering and solving problems. Electronics? Chemistry? Building science? Genetics? The books on these subjects are full of problems.
How do you learn good software design? I don’t know. The books that I’ve read explain principles, and provide examples. Rarely I’ve seen books that contain problems, exercises, or challenges. (Notable exceptions: William Wake’s Refactoring Workbook and Ka Iok Tong’s Essential Skills for Agile Development.)
I propose that we assemble a collection of problems meant to develop and discuss software design. A good problem for this goal would problem *not* have a single correct answer, for design and engineering are always a matter of compromises. A good problem should be a means to discuss the various choices and tradeoff, and worse and better ways to solve it. A good problem should be a small framework.
Let’s start! Here is a problem that I find interesting. The good old Fizz-Buzz problem goes like this:
Write a program that prints the numbers in order from 1 to 100, with the exception that when a number is a multiple of 3, it prints “Fizz”. When a number is a multiple of 5, it prints “Buzz”. And when a number is multiple of both, it prints “FizzBuzz”. In all other cases, it just prints the decimal representation of the number.
There is an obvious way to solve this exercise, of course. It’s a very simple problem, from the point of view of programming. I would have the student solve it however they like. Most solutions contain a 3-way IF. I would then ask students to remove duplication. Early XP books were strong on removing duplication, for a good reason. It takes a bit of training to see how much duplication can creep in even such a small bit of programming.
The usual objection I get at this point is that it makes no sense to go this deep in removing duplication for such a small and trivial example. They also will say that the 3-IFs version is more readable than any version where duplication is removed. This is the crux of the matter.
I then continue the exercise by adding the requirement that
For multiples of 7, the program prints “Bang”.
Easy, they say. Add a fourth IF. Not so fast, I say :-)
For multiples of 7 and 3, the program prints “FizzBang”. For multiples of 5 and 7, the program prints “BuzzBang”. For multiples of 3, 5, 7, the program prints “FizzBuzzBang”!
Now we have an exploding number of IFs. If the next requirement is of the same sort as this one, we see how the IF-chain solution becomes untenable :-) Now solve this!
Update:
- I got the idea of using FizzBuzz as a design example from Giordano Scalzo, who presented it at the Milano XPUG and posted a solution on slideshare
- Other sources of problems, in no particular order: the Refactoring to patterns book by Joshua Kerievsky. The list of katas by Dave Thomas. The Refactoring in Ruby book by William Wake and Mike Rutherford. The Ruby Quiz site. I’m not merely looking for programming problems. I’m looking for design problems. The difference is that I don’t just want a problem that requires a correct or efficient solution. I want a problem that requires a solution that is easy to understand and change.
June 18th, 2010 at 13:41
peccato che nessuno abbia proposto qualche soluzione, il problema e’ carino anche se “artificiale” (e in un ottica di “vero” design questo e’ sempre un po’ problematico), peraltro il codice che ho visto su slideshare (oltre a risolvere un problema leggermente diverso) e’… come dire… migliorabile : )
ottima iniziativa comunque : )
June 18th, 2010 at 14:25
Hola Carlo,
Grazie per il tuo commento. Io il FizzBuzz lo vedo un po’ come uno strumento per illustrare alcune tecniche di design.
Più che soluzioni al FizzBuzz :-) mi piacerebbe vedere contributi a una “biblioteca” di problemi di design… (hint! hint!)
Matteo
June 19th, 2010 at 11:30
>Più che soluzioni al FizzBuzz :-) mi piacerebbe vedere contributi a una “biblioteca” di problemi di design…
—
:)) certo… come dicevo, e’ una bella iniziativa, magari riesco anche a contribuire in qualche modo.
due consigli (non richiesti, ma ok : )
– capisco che introdurre vincoli (es. “non potete usare get”) a volte spinge a generare soluzioni inusuali, pero’ i vincoli artificiali sarebbero da evitare ove possibile (anche perche’ riducendo lo spazio del progettista si riduce anche la discussione delle alternative)
– sarebbe poi carino provare a vedere come si comportano le diverse soluzioni di fronte a cambiamenti nei requisiti (non note a priori, altrimenti…). Questo e’ in genere piu’ interessante rispetto ad un confronto del tipo “il mio e’ piu’ open/closed del tuo” che ogni tanto vedo in giro (peraltro in genere non viene identificato il subset di estensioni per cui il sistema risulta davvero “open”).
Volendo contribuire subito : ), ed avendo lavorato ad alcuni sistemi di cassa per un paio di clienti diversi, propongo qualche variante ai requisiti del problema #2, giusto per vedere la flessibilita’ delle soluzioni trovate:
a) ogni 3 prodotti, il piu’ economico e’ gratis. Nel caso di acquisto di N > 3 prodotti, la scelta puo’ essere mirata a favore del negoziante (tipicamente) o del cliente.
b) sconti cross-product, ovvero: ogni N prodotti di tipo A acquistati, M prodotti di tipo B sono gratis (o scontati di X%).
c) [difficile, ma real-world] le regole del caso (b) possono sovrapporsi, e va trovata la soluzione complessiva piu’ favorevole per il cliente. Nel caso piu’ elementare (e raro)
N1 A => M1 B gratis
N2 B => M2 A gratis
Se compro K1 prodotti A e K2 prodotti B, come vanno prezzati?
(ovviamente i casi reali sono + complessi, le sovrapposizioni non sono mai del tipo simmetrico dato sopra, ma ok, una buona soluzione dovrebbe gestire tutti i casi).
Sarebbe divertente vedere quanto codice (e quanta “struttura”) si salva di fronte ad ogni modifica, e quanto i vincoli artificiali abbiano migliorato o peggiorato la flessibilita’ del design…
ciao
Carlo
June 20th, 2010 at 08:48
Sono d’accordo con quello che dici sui vincoli artificiali, eccetto che possono avere una funzione didattica. Per esempio, nel caso del supermarket checkout, spingono a uscire dallo stile procedurale “dammi X, Y, Z che li uso per fare un conto” per passare a uno stile più OO “tu che hai i dati necessari, fai questo conto”. E dico “didattico” non per fare il professore, ma proprio nel senso che serve per imparare; anch’io ho imparato delle cose utili costringendomi a risolvere l’esercizio con questi vincoli.
D’accordo anche con l’applicazione di nuovi requisiti “a sorpresa” come test della bontà del design.
Belli i tuoi contributi al checkout!! Ne aggiungo uno anch’io:
“Il risultato non deve essere semplicemente il numero del totale, ma deve essere una lista di tutti i prodotti con i loro prezzi, con l’indicazione di tutti gli sconti applicati”.
(Spoiler) Dal punto di vista del design, se uno ha introdotto prima un “collecting parameter” che tiene traccia del totale, implementare questo nuovo requisito impatta solo il collecting parameter, mentre le altre classi (quasi) non vengono toccate. Questo collecting parameter prende chiaramente il ruolo di Scontrino.
June 20th, 2010 at 15:06
si si Matteo, diciamo la stessa cosa : ), tu parli di funzione didattica, io dico che spingono “a generare soluzioni inusuali”, sono due facce della stessa medaglia…
comunque, io ogni tanto faccio un salto sul tuo blog, seguiro’ gli sviluppi : )
ciao
Carlo
February 25th, 2011 at 04:37
Strategy pattern