Skip to main content
3 of 7
Showed a secondary way of doing the formatting in the Expressions. My original code used a custom class, but more well-known Formatters can be used

Developed Fully Working Alternative to String.format(...) | Has Reusability Issues

Goal

Now, we get taught that reusability is very important in university; however, when I applied what was taught before, I found out by SO users that my code was not reusable - well part of it was at least.

So my main goal here is to create a reusable part of my application. Secondary goal would be to make sure most (all important parts) are reusable.

Here is my entire project on GitHub. This is my first project on GitHub, so I hope everything is right!


Background Info (skippable)

I have been working on an expanding Android program that I started back a little while ago. This was a project to get back into programming after a longer-than-expected hiatus.

The program I have is contrived, and over engineered, but that was part of the idea behind it.

My goals throughout the project were:

  • Abstraction was key.

  • Readability was very important.

  • Yet the simplest way to the above was preferred.

Also...

  • DRY was very important.
  • Trying new methodologies/patterns was a good idea
  • Reusability was important. (I thought I had it, but I guess not)

Here is the SO question I had dealing with this code before


Alternative

I have been working on an alternative to the use of String.format, String.printf (and alike function). I have disliked the use of printf-like functions for a while, andI have been working on a solution that is more readable and encapsulates the formatted string into a separate entity. While the alternative isn't the best alternative, I have simplified it a lot, and actually like it for more compared to complicated formatted strings.

Code

I will layout the code to follow the sequence of button press to display

UI (MainActicity classes)

Here is the code dealing with the listener! The code that will cause the above code to execute.

First in the UI...

    btnAdd.setOnClickListener(new OperationClick(Add).listenerOn(this)); // Add is an adnanced Enum
    btnAdd.setOnClickListener(new OperationClick(Subtract).listenerOn(this));
    // and so on...

OperationClick (Listener + Logic)

Then OperationClick which has the listener logic, and does the calculations. This code is activated by clicking a button (Add, Substract, ...) in the UI

// Method listenerOn(...)
public final View.OnClickListener listenerOn(final ICalculatorAbstraction UI) {

    if (listener != null) return listener;

    return listener = new View.OnClickListener() {

        @Override
        public void onClick(final View v) {

            // Gets num1 and num2 from the UI. Calculates total via Strategy pattern. operator is retrieved via Enum that was passed in

            final ExpressionState expression = new ExpressionState.Builder()
                    .setFirstNumber(num1)
                    .setSecondNumber(num2)
                    .setTotal(total)
                    .setOperator(operator)
                    .buildFor(UI);

            UI.updateResult(expression); // 

        }
    };

}

UI Again

The UI has no logic at all (no listener main code or other logic). It is backed by an interface. I.e. Getting first and second number, and updating result. Here is the code I have now...

@Override
public void updateResult(final ExpressionState result) {

    // Note: The Publish class is not needed, it is here for readability only
    this.txtResult.setText(new Publish(result).as(Expression.class)); // .as(ExpressionDoge.class);  or .as(ExpressionStatic.class);

    // Could be... setText(IExpression.Display.getFullExpression(Expression.class, state));
    // Verses ...  setText(String.format("%s %s %s = %s", result.getFirstNumber(), ... , result.getTotal()));

}

Take note that this is very readable and clean. The UI does not care how it is displayed - and, really, why should it? The UI here is just to map out what to do with user interactions. How to display and format the results is a different goal, and could be abstracted out.

BaseActivity

This class abstracts out all the ugly, boilerplate code that I don't care about and don't want to keep on typing (DRY). In it there is the Publish class from the above code. I was thinking this could be a seperate class altogether, but right now it is part of the abstract base class.

public abstract class BaseCalculatorActivity extends ... implements ... {

    // ...

    // This inner class isn't needed. Just used for readability in the UI. Can replace call to new Publish with the return statement below (slightly modified)
    protected class Publish {

        private final ExpressionState state;

        public Publish(final ExpressionState state) { this.state = state; }

        public CharSequence as(final Class<? extends IExpression> expression) {

            return IExpression.Display.getFullExpression(expression, state);

        }

    }

}

Now comes my alternative to String.format...

IExpression

public interface IExpression {

    String getExpression();
    String formatRealExpression();

    // Inner class is a "hack" for Java 7. Java 8 might be cleaner?? but Android does not support it!!
    class Display {

        protected static ExpressionState state;

        public static CharSequence getFullExpression(final Class<? extends IExpression> type, final ExpressionState state) {

            String replacedString = "";

            Display.state = state;

            // Here is the key code segment! 
            for (final IExpression expression : type.getEnumConstants()) {

                replacedString += expression.getExpression();

            }

            return replacedString;

        }

   }

}

I was thinking that this class could be a library that others could develop with, but right now that cannot happen.

Anyways, here is the real deal...

Expression Class

Here is a code snippet first! This is to show one aspect of the code.

public enum Expression implements IExpression {

    Number1 ("%s"),
    Operator(" %s "),
    Number2 ("%s"),
    Result  (" = %s");

    // Printing out the enum would guarantee this unformatted expression "%s %s %s = %s" ... Just like we want

}

but here is the full class...

public enum Expression implements IExpression {

    Number1 ("%s") {

        @Override
        public String formatRealExpression() {

            // Note Number is a custom class, but I could have used DecimalFormat (Number does that internally!)
            return Number.parse(Display.state.getFirstNumber())  // IExpression.Display.state.getFirstNumber()
                    .setMaximumDecimalPlace(2)
                    .toString();

            // Or more simply
            return new DecimalFormat("0.##").format(Display.state.getFirstNumber());  // This would be a simpler approach

        }
    },
    Operator(" %s ") {  // " + " or " - ", etc...

        @Override
        public String formatRealExpression() {

            return Display.state.getOperator().toString();

        }

    },
    Number2 ("%s") {

        @Override
        public String formatRealExpression() {

            return Number.parse(Display.state.getSecondNumber())
                    .setMaximumDecimalPlace(2)
                    .toString();

        }
    },
    Result  (" = %s") {

        @Override
        public String formatRealExpression() {

            return Number.parse(Display.state.getTotal())
                    .setMaximumDecimalPlace(4)
                    .toString();

        }
    };

    private final String defaultFormat;

    Expression(final String format) { this.defaultFormat = format; }

    // Because every expression has 1 "%s" and no more, or not something else...this method can be done here!
    @Override
    public String getExpression() { return defaultFormat.replace("%s", formatRealExpression()); }

}

ExpressionDoge Class

Here is just a short, second example...

public enum ExpressionDoge implements IExpression {

    Phrase  ("Wow. Such %s. ") {   // "Wow. Such Addition. " or "Wow. Such Add. "

        @Override
        public String formatRealExpression() {

            return Display.state.getOperator().toString();

        }
    },
    Total   ("%s") {

        @Override
        public String formatRealExpression() {

            return new DecimalFormat("0.##").format(Display.state.getTotal());

        }
    };

That is my code, and I am wondering how to make it more reusable while still following my goals. Second goal is how to make the other parts of my project reusable

NOTES

List of classes above

  • MainActivity.java (not shown - MainActivityDoge.java, and MainActivityStatic.java)
  • OperationClick.java
  • BaseCalculatorActivity.java (plus Publisher class)
  • IExpression.java
  • Expression.java (not shown - ExpressionDoge.java)