views:

235

answers:

7

How could I possibly implement a unit converter in Java??? I was thinking of having a abstract base class:

public abstract class Unit {
    ...
    public void convertTo(Unit unit);
}

Then having each class like Meter Kilometer Inch Centimeter Millimeter ... derive from that base Unit class. All the units of length would be in a package called com.unitconverter.distance, then a package, com.unitconverter.energy, for energy etc. etc. So is this the best way to implement a unit converter? Or is there a better or more easier way?

+11  A: 

There's been a fair amount of work on this, including JSR 108 (withdrawn) and JSR-275 (rejected). See JScience and UnitsOfMeasure implementations of the latter

Matthew Flaschen
+1 for JScience. It's pretty nice.
spong
+4  A: 

You should check how java.util.concurrent.TimeUnit is implemented, just as a reference. This is an enum that supports the convert(..) operation.

Eyal Schneider
A: 

The use of abstract classes is good when there is some default behavior that you want to implement, as well as some methods that you want to ensure implement some non-default behavior. If you only have a single method that you want to ensure your Unit inheritors implement, you should use an Interface.

akf
+1  A: 

To implement a Unit converter, you are converting from one Unit to another. So I would have a method called convertTo() that would take one Unit object and return another Unit object.

Each subclass of the Unit class would have its own definition for how it converts itself to some intermediary Unit, and how it converts a value from some intermediary Unit to its own Unit.

To convert it, then, your convertTo() method would call its own method to convert itself to some alternative Unit, then call another method to convert that intermediary Unit to the type of the one that was passed as a parameter (because the one that was passed has its own convertFrom() method).

chustar
A: 

You can implement unit conversion using a sparse graph. Create an edge between two units if there is a conversion between them. Ergotic sets in the transitive closure are units of the same species (length, time, etc).

Justin
A: 

Don't forget, although you'll need to model the Unit concept, it's actually a "unit + quantity" that you'll convert. Also, the concept of dimensionality is important, in that it restricts which conversions are meaningful (i.e. metre to yard is as both are lengths; Celsius to Watts would not be).

pdbartlett
+1  A: 

As a converter, units are data and do not really belong in code.

You can do some really awesome stuff, however, if you break your units down to "base" components (Length, temperature, Time, ...)

So you might have a database that looks like this:

minute = 1 x Time (where Time is the basic unit of measure for time)

second = time / 60

hour = time * 60

meter = 1 x distance

Klometer = 1000 x distance

...

The nice thing about this is you start to get real flexibilities with formulas. For instance, if I had some info around like the fact that a snail I was watching moved 20 cm in 3 minutes, I could easily feed that to a speed equation (velocity=dist/time) to get a "Root" velocity figure, then request the results in terms of different units (say, miles and hours) so that I instantly get a result in miles per hour.

You even know what info you are missing--like if you give a distance and ask for a velocity, it could respond "You still need a time". Or if you give a distance and velocity it could calculate a time in units of your choice.

Anyway, it's all data. You should probably not even have different classes for distance and time.

I suppose I'd use a fixed set of measurement objects as described above that could be used to look up a conversion (unit name, unit type, conversion factor) like ("Minutes", TIME, 1)

When a user entered a value with a unit (Let's say user enters 3 hours), I'd simply look up the unit ("Hours"), figure out the conversion ( 180, TIME ) and store (180, TIME) somewhere.

When they asked for a readout in minutes, I'd look up the conversion for minutes ("Minutes", TIME, 1) and use the scaling factor (1) to determine that you need to print 180 minutes.

If the user put in a value like 3 hours and asked for a speed in meters/second, you can prompt for a distance (that the user can specify using any distance type) and easily convert to their required output. Converting to the base units and using scaling factors makes nearly all the difficulty go away.

Justification/reasoning:

My assertion that the units and conversion factors shouldn't be specified in code is probably going to be questioned...

My reasoning is that you should NEVER have objects that do not have UNIQUE business logic.

The only difference anywhere between minutes and seconds will be a formula that will only vary in a constant (anywhere minute is used, you could also use second x 60 or hour * (1/60), so that constant (1, 60, 1/60) cannot cause a new class to be created in itself--this would be absolutely criminal.

This is true of all the basic measurement types, none of them have unique business logic.

Bill K