Stupid Human Programming
Talk on software development.








Subscribe to "Stupid Human Programming" in Radio UserLand.

Click to see the XML version of this web page.

Click here to send an email to the editor of this weblog.


Sunday, January 29, 2006
 

What is a Software Car?

This posting (http://discuss.joelonsoftware.com/default.asp?joel.3.301232.15) on Joel On Software discussion group poses an interesting design question.

The first approach:
class Car
{
public void Wash() { /*code to wash the car*/}
}

Car car;
car.Wash();

The second approach:
class Car : public IWashable
{
//private members. This class just acts as a business object that is manipulated by "verb" objects. This maps directly to a db entity.
//no methods. just props and constructors and a dispose pattern
}

class Washer
{
public void Wash(IWashable ptr) {/*code to wash thingie*/
}

IWashable myCar = new Car();
Washer w = new Washer();
w.Wash(myCar);

From a C++ perspective Washer is nice because you don't need to change Car to add washing capabilities. This is solid pragmatic reason, though I think a more philisophical discussion was desired.

Given all the objects that would be needed to wash a car, what makes Car the right place to know how car washing works? Should a car know about how to find the nearest and cheapest car wash, for example? To answer this question you have to somehow be able to decide which objects are natural for Car to know about.

In this case, Washer is really a relation between all objects involved in washing a car. Washer is an algorithm abstraction represented by a class. This could easily be a function in a functional language, though in my mind classes and functions are pretty much the same thing.

You could make the wash method abstract in which case you would derive a class like CarWasher from Car that contained the same relation as Washer, using ISA instead of HASA. I might argue this approach has advantages from a program discovery perspective. By looking at Car I immediately know Car has the ability to wash itself, yet the implementation is left to others. This would set me looking for classes who have implemented wash. This makes the intent clear in the code. From looking a Car you have no idea that Washer exists.

The downside of this approach, in C++ at least, is that extension always requires changing the base Car class, which isn't always possible or desireable. So you often end up with a mish mash of ways of extening Car. Some extensions are made through the base class and some are made by adding new classes.

Again, these are largely pragmatic concerns. The real question is: should a Car know how to wash itself?

One answer is no because an object is characterized by identity whose behaviours arise from relations with other objects.

This is the answer that is correct for me in the abstract. But classes don't exist in the abstract. Classes exist inside a program which is built to serve a purpose.

It is the program's purpose that defines the domain/relation/frame in which Car should be understood. We can't just say we all know what a car is and make decisions based on our folk understandings of car-ness. The program defines the idea of car-ness within its borders of reality.

Anything consistent with a program's purpose should be in Car. Anything else not strongly cohesive with that purpose should be put elsewhere. If this program were about car washing the wash code should be in Car because then that would be essential to the nature of carness within in the application.

If car washing was being added to a program where a Car already has a strong meaning then adding wash to Car would make Car confusing and more difficult to test. We often find as programs grow and change that our simple cohesive objects take on multiple personalities and the only way to keep those identities straight is to move each of them into their own class. The Car object itself at that point is mostly about identity, it is the relation that ties everything together, but may not have much state or behaviour itself.

But here the pragmatist in me escapes and I must say that when I look at the Car class source code I really want to be able to see what it can do. I don't want to have to guess or look at 1000 different libraries to know all the things I can do with a Car. This is where modern documentation tools come in handy. They will tell you all the classes that use Car so you have the ability to know what Car does without including it directly in the source.

So, in a more complex program where Car has several natures I would go with the Washer approach. In a program where Car has a single nature I would make wash part of the Car class.

This is why no two designs are ever the same. People look at all these issues and can honestly come to very different conclusions about the answers.





comment[]

1:11:05 PM    



Click here to visit the Radio UserLand website. © Copyright 2006 todd hoff.
Last update: 7/11/2006; 12:37:20 PM.
January 2006
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Dec   Feb