Creating a Unit Converter App – Part 1

In this post (split into two), we’re going to build a unit converter app. We’ll be learning many things along the way like the Model-View-Controller (MVC) pattern, Java Enums, and Spinners. By the end of this post, we’ll have a working app that will allow us to convert to and from various units of distance like miles, centimeters, kilometers, and other units. Feel free to append more types of units as well!

As a side note, it’ll be helpful if you’ve read over how to setup Android Studio, create projects, and some of the basic Android UI elements. You can get up to speed very quickly by following the link here.

You can pull the source code for this project here.

Model-View-Controller (MVC) Pattern

When dealing with a system that has many moving parts it’s helpful to have some form of system design that helps us keep everything distinct. Imagine the mess our app would be if we had XML views, Java source code, and other components wildly about in our Android project! To prevent this disaster from occurring, we have design patterns we follow to try to keep our code as segmented as possible. Similar to the concept of encapsulation in Java, we want to make sure our components are separated from each other. For this, we use the Model-View-Controller (MVC) pattern. The MVC Pattern is now ubiquitously used in both mobile and desktop development.

Unit Converter – 1

The above figure is an illustration of the components of the MVC pattern. There’s the model, view, and controller. The model can be any kind of backend such as a database or web server. It can also be any kind of structure that holds or stores data somewhere, like a local key-value pairing system. The view is pretty self-explanatory: it’s the UI that’s presented to the user. The controller is the most crucial component that allows for communication between the model and the view. However, the view and the model don’t even know the other exist! This is so we can make changes to the model or the view and we only need to update the controller instead of all three components! (The view in most case is the component that is updated the most frequently.) The MVC Pattern is now ubiquitously used in both mobile and desktop development.

Specifically, Android supports the MVC pattern and we’re going to be using it throughout this app and all other apps. In fact, Android mandates the view and the model are all separate because the view is in written in an XML layout file (although it is possible to manually instantiate views in Java source code). In Java, Activities act as the controller and mediate between the XML views whatever model backend we decide to use. In our case, our model will just be a utility class that we’ll store the constants needed for converting from one kind of unit to another.

Creating the UnitConverter Project

Let’s get started! First of all, we’ll want to create an Android project called UnitConverter with any package name we want. We’ll want to keep this to Android 6.0 and creating a blank activity named MainActivity will suffice for us. The first component we’ll create is the model. We’ll need to right-click on the main package (not the one ending with (androidTest)!) and create a new class called Converter. This will be used to convert the different units. However, we’ll need to keep some way to represent units. There are several ways we can do this: String constants with the name of the units, distinct integer constants that represent those values, or we can use a language feature of Java called enums.

Java Enums

Enums in Java were introduced to the language after it was created and was not a part of it’s original language features. There’re really usefully however! Essentially, we can use them to create groups of related constants. An enum is also a kind of class in the sense that we can declare variables and methods in an enum. One of the greatest benefits of using enums is the fact that they’re type-safe! Meaning that we can’t assign an enum variable to just any value, it must be to a valid constant in the variable’s enum. It also prevents us from performing any arithmetic operations. It doesn’t make any sense to add constants representing something like units! If we used String or integer constants, we could do something KILOMETER + MILE and that wouldn’t make any sense! Once we declare an enum, we can use it as a type like we would with a class or interface. However, as I mentioned before, the value of the enum variable must be in the enum class that defines that variable. Under the hood, Java essentially creates objects with static memory addresses so when we refer to an enum constant, there’s only one! This is great because we can do equality checks with == since the only way the two enum pointers are the same is if they point to the same object in memory, of which there is only one! Similarly, we can use the assignment operator since that will also allow us to set the pointers to the same static object in memory. To sum up, enums are a kind of class that allow us to group related constants in a type-safe way much better than integer or String constants.

We’re going to nest our enum inside our Converter class, but to do that, we need to create our Converter class! Right-click on our main package (not the green highlighted one that ends with “(androidTest)” since that’s the test suite) and select New Java Class. We’ll call it Converter. Inside it, let’s declare the following enum:

public enum Unit {
    INCH,
    CENTIMETER,
    FOOT,
    YARD,
    METER,
    MILE,
    KILOMETER;

    // Helper method to convert text to one of the above constants
    public static Unit fromString(String text) {
        if (text != null) {
            for (Unit unit : Unit.values()) {
                if (text.equalsIgnoreCase(unit.toString())) {
                    return unit;
                }
            }
        }

        throw new IllegalArgumentException("Cannot find a value for " + text);
    }
}

We can see that an enum can be declared in a similar fashion to a class. The first thing in an enum is the comma-delimited list of constants. If we wanted to write methods, we have to terminate our list of constants with a semicolon. Then we’re free to write any methods. In this case, we’re writing a small utility method that will return us a enum value given a String. We’re doing our null check as the first step. Then we go through each value of the enum and if our input equals the name of the enum constant, then return that enum constant. At the end of the method, we’re going to throw an exception saying that the user didn’t input a valid unit String. Enum checking is exhaustive, meaning that if we get an enum of type Unit, we know that its value MUST be one of the constants. We can access those constants by Unit.MILE for example. You’ll see in the next post that Spinners only allow the user to input a finite set of options. This, along with the fact that enums are exhaustive, means that we’ll never reach that exception and it’s just there for our debugging purposes. Now we can start using our enums in our model.

Model: Converter class

In the same Converter class, we’re going to need to design a way to convert from one unit to another. Essentially, a user would input a starting unit, an ending unit, and a value to convert. There are several design approaches we could take. For example, we could have a static method that takes in two Unit objects and a double and converts between the two. However, to demonstrate OOP even further, we’ll have the Converter class take the to and from Units as part of the constructor and have an instance method called convert(double)  that takes in a double and returns an output. We can convert from any unit to another valid unit by multiplying it by some constant. Since we’re following the constructor approach, we can set that constant in the constructor to make our method very simple. To set the constant, we need to check the value of the the to and from units and set the constant equal to some value that will properly convert from one to the other. We can initially set the constant to 1 in the case that the to and from Units are equal to help reduce a case. Here’s what this would look like: (put this code after the enum declaration)

// What can I multiply by to get me from my fromUnit to my toUnit?
private final double multiplier;

public Converter(Unit from, Unit to) {
    double constant = 1;
    // Set the multiplier, else if fromUnit = toUnit, then it is 1
    switch (from) {
        case INCH:
            if (to == Unit.CENTIMETER) {
                constant = 2.54;
            } else if (to == Unit.FOOT) {
                constant = 0.0833333;
            } else if (to == Unit.YARD) {
                constant = 0.0277778;
            } else if (to == Unit.METER) {
                constant = 0.0254;
            } else if (to == Unit.MILE) {
                constant = 1.5783e-5;
            } else if (to == Unit.KILOMETER) {
                constant = 2.54e-5;
            }
            break;
        case CENTIMETER:
            if (to == Unit.INCH) {
                constant = 0.393701;
            } else if (to == Unit.FOOT) {
                constant = 0.0328084;
            } else if (to == Unit.YARD) {
                constant = 0.0109361;
            } else if (to == Unit.METER) {
                constant = 0.01;
            } else if (to == Unit.MILE) {
                constant = 6.2137e-6;
            } else if (to == Unit.KILOMETER) {
                constant = 1e-5;
            }
            break;
        case FOOT:
            if (to == Unit.INCH) {
                constant = 12;
            } else if (to == Unit.CENTIMETER) {
                constant = 30.48;
            } else if (to == Unit.YARD) {
                constant = 0.333333;
            } else if (to == Unit.METER) {
                constant = 0.3048;
            } else if (to == Unit.MILE) {
                constant = 0.000189394;
            } else if (to == Unit.KILOMETER) {
                constant = 0.0003048;
            }
            break;
        case YARD:
            if (to == Unit.INCH) {
                constant = 36;
            } else if (to == Unit.CENTIMETER) {
                constant = 91.44;
            } else if (to == Unit.FOOT) {
                constant = 3;
            } else if (to == Unit.METER) {
                constant = 0.9144;
            } else if (to == Unit.MILE) {
                constant = 0.000568182;
            } else if (to == Unit.KILOMETER) {
                constant = 0.0009144;
            }
            break;
        case METER:
            if (to == Unit.INCH) {
                constant = 39.3701;
            } else if (to == Unit.CENTIMETER) {
                constant = 100;
            } else if (to == Unit.FOOT) {
                constant = 3.28084;
            } else if (to == Unit.YARD) {
                constant = 1.09361;
            } else if (to == Unit.MILE) {
                constant = 0.000621371;
            } else if (to == Unit.KILOMETER) {
                constant = 0.001;
            }
            break;
        case MILE:
            if (to == Unit.INCH) {
                constant = 63360;
            } else if (to == Unit.CENTIMETER) {
                constant = 160934;
            } else if (to == Unit.FOOT) {
                constant = 5280;
            } else if (to == Unit.YARD) {
                constant = 1760;
            } else if (to == Unit.METER) {
                constant = 1609.34;
            } else if (to == Unit.KILOMETER) {
                constant = 1.60934;
            }
            break;
        case KILOMETER:
            if (to == Unit.INCH) {
                constant = 39370.1;
            } else if (to == Unit.CENTIMETER) {
                constant = 100000;
            } else if (to == Unit.FOOT) {
                constant = 3280.84;
            } else if (to == Unit.YARD) {
                constant = 1093.61;
            } else if (to == Unit.METER) {
                constant = 1000;
            } else if (to == Unit.MILE) {
                constant = 0.621371;
            }
            break;
    }

    multiplier = constant;
}

// Convert the unit!
public double convert(double input) {
    return input * multiplier;
}

Now the convert method is really simple. We just return the constant times the input. One downside to this approach is if we wanted to convert another set of units, we’d have to create a new Converter object. However, in our case, we’ll only initialize the Converter object after the user has clicked the “Convert” Button. But now our model is ready and we can use it to convert units!

In this post, we covered the MVC pattern and how we’ll be using it in our apps. We also completely created the model of our Unit Converter app and learned about Java enums and how we can use them to label constants in a type-safe manner. In the follow post, we’ll be building the View and implementing the Controller to interact between the Model and View.