The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Does Primitive Obsession influence your design?

I was reading..
http://www.jamesshore.com/Blog/PrimitiveObsession.html

Do you make lot of small classes? How does it help?
Jam
Friday, September 01, 2006
 
 
It's going a bit overboard. In this guy's example, I would use an int, and make a class if and when the need arises (it probably never will).

And, did you notice how he used a primitive type *inside* the age class? Why not go all the way and have the Age class encapsulate a CInt class; something like

public class Age {
  private CInt age;

  public Age( CInt age) { this.age.set( age ); }
  public int toInt() { return age.toInt(); }
}

etc, etc, ad nauseam

How about "Useless Micro Class Obsession" ? But, I guess I'm old fashioned...
Parisian developer seeking CGG
Friday, September 01, 2006
 
 
Yes, I make lots of small classes and it does help.

Age is a terrible example (who would ever keep an age object - it's always changing!) but I certainly would make a DateOfBirth class instead of using a primitive Date.  Pretty soon, I'll want an Age method on that class, then a few months later, I'll want to display age in years if greater than 2 but in months if less than 2, and on and on it goes.
Mike S Send private email
Friday, September 01, 2006
 
 
Parisian,

The idea is to encapsulate a primitive inside a class that represents a domain entity.  That's why you don't create a "CInt" class.

But you probably knew that already.
Mike S Send private email
Friday, September 01, 2006
 
 
How does this mesh with the YAGNI (Ya Ain't Gonna Need It) philosophy? Why make something more complex than it has to be right now, cause chances are ... ya ain't gonna need it. Don't all these guys in the "agile" world work on the: do the minimum necessary/YAGNI philosophy?

If an int works, an int works. Anything else, YAGNI.
Sgt.Sausage
Friday, September 01, 2006
 
 
> How does this mesh with the YAGNI
> (Ya Ain't Gonna Need It) philosophy?

Because following simple rules blindly is stupid?

> If an int works, an int works. Anything else, YAGNI.

Age is an idea, it is not an int. And I have noticed that everytime I don't represent ideas as classes it bites me in the rear. So I do it from the start. I don't need to wait.

As mentioned in another post, representing an Age with an int is the exact wrong way. A date is better for a number of reasons. If I used an Age class then not a line of code would need to change if I switched representations. That's somethin g I have learned from experience. Experience trumps rules.
son of parnas
Friday, September 01, 2006
 
 
Sausage,

I agree, I wouldn't set up these little empty classes "just in case", but I wouldn't hesitate to create one as soon as I had the simplest method to put in it, which often comes only a few minutes later...
Mike S Send private email
Friday, September 01, 2006
 
 
I always thought it would be interesting to define a bunch of little classes to wrap primitives in order to support  compile time dimensional analysis.

For example:
int distance = 100;
int time = 20;

// Wrong!  But compiles and runs...
int velocity = time/distance;

As opposed to:

Distance d = 100;
Time t = 20;

// Wrong, but this time doesn't compile because
// Time doesn't implement a division operator that
// takes a Distance and returns Velocity...
Velocity v = t/d;
SomeBody Send private email
Saturday, September 02, 2006
 
 
Exactly.
Mike S. Send private email
Saturday, September 02, 2006
 
 
I think that this is one of those common sense things.  If you are writing physics software by all means create time and distance classes.  If you are making calendar software go with date and time and so forth.  However if you are just doing a couple of physics equations then just use primative types.  It all comes down to common sense and experience.
Tom C
Sunday, September 03, 2006
 
 
Also, additional classes increase the cognitive load on the person reading the code. For example, if I came across an Age class, I'd have to look up its definition to learn that it's an int wrapper. Plus, the code would get cluttered with new Age() and age.toInt() calls.

I'm all for creating new classes when doing so makes the code easier to read and maintain. Dividing up big classes is one of the most valuable refactorings. However, that's not the case here.

The additional type safety would be nice, but it's not worth the verbosity, at least in Java.
Julian
Sunday, September 03, 2006
 
 
> For example, if I came across an Age class, I'd
> have to look up its definition to learn that it's
>  an int wrapper

Exactly the opposite. You would never have to look at the Age class because the operation would be obvious from the method name and the class. It's whey you see random operations on age ints that you have to backtrack every calculation to see if it is being done to type.


> age.toInt()

Why do you need to go in an out if ints so often?
son of parnas
Sunday, September 03, 2006
 
 
Interesting replies.I have found that having little classes,which represents concepts in the domain,makes writing code much intuitive.I dont know,how it will help me down the road,as this is my first project with this kind of approach.
jam
Monday, September 04, 2006
 
 
Say,I have a Person class,and it has Age(value object) as a property.
Class Person
{
  private Age m_Age;
  Age PersonAge
  {
    set{m_Age=value;}
  }
}
So,when I create an object of Person,and initialize its Age,my code will look like this..
Person person=new Person();
Person.PersonAge=new Age(19);
So,whats the point? Eventually,I am exposing that Age is stored as integer.
jam
Tuesday, September 05, 2006
 
 
It is a question of the abstractions exposed on the outside of a well-defined interface, and of the implementation and representation hidden behind the interface.

Ask yourself: Is it a good idea to deal with both abstraction and representation in multiple parts of the code, or is it a better idea to do the dirty work in one place only (the object) and expose the rest of the application to the abstraction only?

Could you even call it an "abstraction" when you directly deal with the naked bits? What is abstracted when you are bound to this one single implementation/representation in any place you use it?
Secure
Tuesday, September 05, 2006
 
 
Hi Secure,
I am unable to understand,completely what you are implying.Could you please,elaborate in terms of example I presented in my previous post.
thks
jam
Tuesday, September 05, 2006
 
 
Jam,

as son of parnas said above, "Age" is an abstract idea. To work with this idea in software, you have to represent it in some way. This may be an integer value directly representing the age. The int is the representation used for an age. Working with it, e.g. subtracting two age ints to get an age difference, is the implementation. Representation and implementation cannot cleanly be separated most of the time, because representation implies implementation, thus both can safely be used as synonyms, at least in this case.

When you use the representation directly to work with the age abstraction, you must use the ints in any place in the whole application where you want to do any calculation with an age. And when you decide to change the representation, e.g. to a date, you have to change any single occurrence, because you change the abstraction, too -- a date value provides a significantly different abstraction of an age than an int value.

Using an object, on the other hand, you have your public methods as the interface. On the outside of this interface, if it is designed well, you work only with the abstraction itself, i.e. with the abstraction of your choice. The inside of the objects cares for the representation, converting it to the abstraction of the interface if necessary. Since you now directly use the abstraction itself in the whole application, you can safely change the representation from an int to a date when necessary. If this doesn't change the interface, too, the abstraction will be left intact, thus there is no need to change anything outside of the object.

Even if you provide methods for accessing the age as an int, they do not expose anything of the internal representation. They just convert it to an int value, the same way a toString-method converts the internal object data to a string. In the same way it is a bad idea to do external calculations with the strings, it may be a bad idea to do external calculations with the ints, instead of providing accordant calculation methods with the Age object.

But this is VERY basic software engineering stuff.
Secure
Tuesday, September 05, 2006
 
 
> But this is VERY basic software engineering stuff.

Yes, but well put none the less.
son of parnas
Tuesday, September 05, 2006
 
 
Yes,very well put.Could you guide me to some sources-online,as well as books,to understand these concepts better.
thks
jam
Thursday, September 07, 2006
 
 
Jam,

Martin Fowler's book _Refactoring_ is excellent and is the origin (I believe) of the term "Code Smell" as well as the Primitive Obsession smell.

Eric Evans book _Domain-Driven Design_ captures what everybody should know about object-oriented programming in a comprehensive, readable format.

Cheers,
Jim
James Shore Send private email
Thursday, September 07, 2006
 
 
Jam,

Also, I have an article titled "Quality With a Name" that talks about some core software engineering principles.  It's a fun read.  (You can ignore the section on measuring design quality.)

http://www.jamesshore.com/Articles/Quality-With-a-Name.html

Cheers,
Jim
James Shore Send private email
Thursday, September 07, 2006
 
 
Three comments here:

1)  Does it make sense to spend 3 weeks designing, testing, and implementing an Age class?

2)  If you have to change aspects of the way a value is represented in the system, then you're going to have to change something.  The idea is to limit the changes that you are likely to make to as few places as possible.  Personally, I think you should always start simply and then complicate as the problem gets more complicated.  So, if a primitive fits the bill for the problem, then use a primitive and don't complicate the design with generic "it could happen in the future" stuff.

3)  Age, as a concept, is not an integer.  It is the output of a function that takes two dates.  Therefor, it is generally poor design to store the calculated result as the property, rather then the elements that drive the calculation.  This would imply that Age, as a concept, is not an integer but rather two properties (DOB and NOW) and a method (CalcAge()).  Same goes with other calculated values like InventoryCount and AvgCost.  (And yes, sometimes, it's a good idea to precache the calculated amount)

4)  Making things "generic" is the mark of an amateur developer making the transition to a pro.  Instead of clarifying the requirements (or using a development methodology that handles requirement risks well like Agile), you make it Generic because the user keeps changing their damn mind.  What you end up with when the user finally makes up their mind and you understand the problem is a lot of inefficient, bloated, confusing, and hard to maintain code.  Build what you need first, then optimise it later if you need to (and that goes for clarity too).

Hope this helps.
James Birchall Send private email
Thursday, September 07, 2006
 
 
Hmmm, probably should have refactored that post a bit.  Should be *4* comments.  *grin*
James Birchall Send private email
Thursday, September 07, 2006
 
 
Hi james,

Do you go to the extreme of using value objects,and no primitives?
jam
Saturday, September 09, 2006
 
 
Jam,

Nope.  I think it's excessive.

- James.

Saturday, September 09, 2006
 
 
jam, a distinction to make here, as is implied by the thread, is that classes are useful to represent ideas, the meaning of entities in the domain being modeled by the system.  Age is an idea, the idea of how old a person is.  So, clearly then Person is an idea too.

So, now, think about this in terms of a wrapper for a value type. What is the idea of int?  I would argue there is no idea, in the sense of having meaning in the domain being modeled.  Int means something in mathematics, it means something to the JVM/compiler, the OS, etc.  None of these are part of what you are modeling in your system.  So you do not need to model them in your design.

Another example, to beat a dead horse.  A program modeling a baseball player might have a class for BattingAverage, which might store the actual average in a float or double.  It wouldn't need a class for Float or Double.
Mark S. Weiss Send private email
Saturday, September 09, 2006
 
 
OK, How will you represent the following idea,a Hotel Rate includes Breakfast and Lunch.
jam
Monday, September 11, 2006
 
 
Jam,

The simplest, is to make Room Rate a double.  Why?  Well, having experience in the hotel industry, rates are often ad hoc, depending upon the house count, the client and other factors like discounts, promotions, group packages, etc...  So the easiest is to make the user put a value in and leave it.

But take a close look at what a Room Rate is...

In the generic case, a room rate is either a simple value or the RESULT of a CALCULATION.  It takes as input, various discounts and a base rate.  Base rate is either a simple value or an output calculated using house count, day, time and room class.  Discount could be a negative simple value and a calculation (10% off, for example, isn't a simple value).  So, there is an object hierarchy between room rate, discount and base rate (and really, any other kind of rate).

Now, you could implement Rate as a class with a double value.  You could implement a Room Rate class as containing a collection of rate objects which represent discounts and the "base rate".  You would probably pull the various base rates and discounts from a DB to Calculate the Room Rate and then load them into Rate objects so you could pass them around.  Of course, you'd need methods which operated on the rates to calculate the final Room Rate.

Look at the USAGE of Room Rate.  How is it used by the user?  It is a subtotal on a bill and drives the double value that is passed to the credit card company (or the cash drawer or whatever) to be charged for the service.  The USER thinks of this as a dollar value and doesn't care about all the components that are used to calculate it.  Therefor, a Room Rate in this mental map is a two decimal point precision DOUBLE.

The communications interfaces to the various credit card companies are not likely to use the Object representation, but rather the double representation, so implementing an object is only for your local convenience. 

However, if the user wants an AUDIT of the INPUTS to the CALCULATION, Room Rate becomes both a value and a list of other values.  If the user requirement is framed in this way, Room Rate becomes an Object.  Is it framed in this way?

Your input interfaces are all doubles.  Your output interfaces are all doubles.  Your calculations are all doubles so you'll have to implement a ToDouble() interface anyways. The question is: is a Rate a double or an object?

A couple of other questions...

1)  Is Rate to be displayed seperately?
2)  Do you serialise Rate?
3)  How does Rate interact with other interfaces?

One of the fundamental mistakes I see with designs is the tendancy to design for every possibility.  I like You Ain't Gonna Need It (YAGNI) philosophy because SIMPLICITY is the goal of any good design.  Which is SIMPLER: 

1)  Rate as an Object
2)  Rate as a double

When you answer that question, your design will fall naturally.  What I don't like about the "Primitive Obsession" is the idea that ALL primitives MUST be objects.  ALL is a pretty heavy requirement.

A final word and that is on IMPORTANCE.  How important to your system is Rate?  The more importance, the more effort spent on design.  The less important, the less effort.  If Rate is not important, just implement it as a double and carry on with more important elements of the system.  If it is a core element, implement it as an object.

- James
James Birchall Send private email
Monday, September 11, 2006
 
 
Hi, James,

"What I don't like about the "Primitive Obsession" is the idea that ALL primitives MUST be objects.  ALL is a pretty heavy requirement."

Primitive Obsession doesn't say that ALL primitives are bad.  It says that using a primitive to represent a single concept in multiple places in your system is a SIGN that you MAY have a design problem.

Primitive Obsession is a "code smell"--something that tells you that there MAY be a problem with your design.  One smell does not a stinky design make.  For more, see Martin Fowler's _Refactoring_, chapter 3.

Cheers,
Jim
James Shore Send private email
Monday, September 11, 2006
 
 
James,

Oh!

I thought it was more radical then that and advocated encapsulating EVERY instance of a primitive with a class.  That seemed really excessive to me.

This only applies to discrete concepts in the system, right?  And not things like object properties and method return signatures?

- James
James Birchall Send private email
Monday, September 11, 2006
 
 
I thought too,that every instantce of primitive must be converted into an object.
jam
Tuesday, September 12, 2006
 
 
Hi, James,

"This only applies to discrete concepts in the system, right?  And not things like object properties and method return signatures?"

That's right.  Sorry for the confusion--when I posted that blog entry, I assumed my readers had heard of Primitive Obsession already (it's not my term).  Looking back at it, I can see how my brief summary ("Primitive Obsession is when you use basic types instead of creating a class") would be misleading.

Cheers,
Jim
James Shore Send private email
Wednesday, September 13, 2006
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz