The last mile

I’ve been refactoring the ATM Service class in my last 2 posts of this blog post series. Although the class is more maintainable and readable as it is, I am still not there where I want to be. Two more things are in serious need of improvement for my solution to be maintainable. The if statements somehow need to be removed and the dispenser configuration should be dynamically configurable. As mentioned in my last post I am going to try to achieve this using the “Chain of responsibility” design pattern.

Before

This is what we ended up with at the end of the previous post

public class ATMService {
    private Map<Denomination, Integer> wallet = new HashMap<>();

    public Map<Denomination, Integer> withdraw(int amount){
        validate(amount);

        if(amount >= FIFTY_EURO.getValue()){
            amount = processWallet(amount, FIFTY_EURO);
        }

        if(amount >= TWENTY_EURO.getValue()){
            amount = processWallet(amount, TWENTY_EURO);
        }

        if(amount >= TEN_EURO.getValue()) {
            processWallet(amount, TEN_EURO);
        }

        return wallet;

    }

    private void validate(int amount) {
        if(amount % Denomination.smallest() != 0) {
            throw new IllegalArgumentException("ATM cannot dispense the requested amount!");
        }
    }

    private int processWallet(int amount, Denomination denomination) {
        int quantity = amount / denomination.getValue();
        if (quantity != 0) {
            wallet.put(denomination, quantity);
        }
        return amount - (quantity * denomination.getValue());
    }
}

Chain of responsibilty

What does this design pattern helps us solve? Coupling the sender (object) of a request to its receivers should be avoided. And that’s exactly the problem that this pattern solves. A characteristic of this pattern is that it should be possible that more than one receiver (object) can handle a request. Otherwise it wouldn’t be a chain I guess.

The above implies that there should be at least two receiver objects. Examining the code above we see that the wallet object is processed multiple times in a certain order. So there we have our receiver objects.

Chain of responsibility ATM as a maintainable chain of responsibilities

Quick recap. For this pattern we need a sending object. The ATM service class. Check. A request. Adding money to the wallet object. Check. And more than one receiver object (which should have a chance of handling the request). The dispensers. Check. So, choosing the chain of responsibility pattern might turn out to be a good call.

Sending object

Looking at the code below, the processing of the three if statements have been removed and delegated to the “Dispenser” object. Or actually, three Dispenser objects.

The ATM checks if the requested amount can be dispensed. Then the service creates the chain of dispensers (50 EUR dispenser -> 20 EUR dispenser -> 10 EUR dispenser). The ATM Service holds only one reference to the first dispenser. The service basically “fires-and-forgets” the request into the Dispenser chain and let the chain handle the process of adding the requested amount of money to the wallet. And finally, when the dispenser chain has finished processing, the service returns the wallet object to the calling client.

public class ATMService {
    private Map<Denomination, Integer> wallet = new HashMap<>();
    private Dispenser dispenser;


    public Map<Denomination, Integer> withdraw(int amount){
        validate(amount);

        dispenser = Dispenser.create( new Dispenser(FIFTY_EURO),
                                      new Dispenser(TWENTY_EURO),
                                      new Dispenser(TEN_EURO)
                );

        dispenser.execute(amount, wallet);

        return wallet;

    }

    private void validate(int amount) {
        if(amount % Denomination.smallest() != 0) {
            throw new IllegalArgumentException("ATM cannot dispense the requested amount!");
        }
    }

}

Receiving object(s)

The Receiver object is responsible for the following things:

  • creation of the chain of dispensers (not for the creation of the dispensers itself!!)
  • holding a pointer to the next dispenser in the chain
  • adding the amount of money to the wallet in the right combination of denominations
  • passing the request to the next dispenser in the chain (if there is a next dispenser)

The wallet is passed on to the chain. The wallet visits every Dispenser defined in the chain. When the end of the chain is reached the processing of the wallet terminates and the ATM Service can return the manipulated wallet object to the client.

public class Dispenser {
    private Dispenser nextDispenser;
    private Denomination denomination;

    public Dispenser(Denomination denomination) {
        this.denomination = denomination;
    }

    public static Dispenser create(Dispenser... dispensers) {
        for (int i = 0; i < dispensers.length; i++) {
            if (i == dispensers.length - 1)
                return dispensers[0];

            dispensers[i].setNextDispenser(dispensers[i + 1]);
        }
        throw new IllegalStateException("Chain error!");
    }

    public void setNextDispenser(Dispenser nextDispenser) {
        this.nextDispenser = nextDispenser;
    }

    public void execute(int amount, Map<Denomination, Integer> wallet) {
        int remainder = addToWallet(amount, wallet);

        if(nextDispenser != null)
            nextDispenser.execute(remainder, wallet);
    }

    private int addToWallet(int amount, Map<Denomination, Integer> wallet) {
        if (numberOfDenominations(amount) != 0) {
            wallet.put(denomination, numberOfDenominations(amount));
        }

        return amount - (numberOfDenominations(amount) * denomination.getValue());
    }

    private int numberOfDenominations(int amount) {
        return amount / denomination.getValue();
    }
}

The extra mile

“We are done”, you might think. Nope… almost. We got rid of the three if statements but we still have this issue of not being able to dynamically set the available dispensers. The chain is created within the ATM service and will therefore always consist of the three dispensers added at compile-time.

Fortunately, this is a relatively small change. All we have to do is remove the creation of the dispenser part and create a constructor which receives a dispenser (chain) object;

Once we do that we end up with an ATM Service class that looks something like this:

public class ATMService {
    private Map<Denomination, Integer> wallet = new HashMap<>();
    private Dispenser dispenser;

    public ATMService(Dispenser dispenser){
        this.dispenser = dispenser;
    }

    public Map<Denomination, Integer> withdraw(int amount){
        validate(amount);
        dispenseToWallet(amount);
        return wallet;
    }

    private void validate(int amount) {
        if(amount % Denomination.smallest() != 0) {
            throw new IllegalArgumentException("ATM cannot dispense the requested amount!");
        }
    }

    private void dispenseToWallet(int amount) {
        dispenser.execute(amount, wallet);
    }
}

What did all this refactoring got me? A compact and well-balanced ATM Service class. A more than well readable class. A more evenly distribution of responsibilities. And a very maintainable class. If e.g. a new dispenser has to be added to the ATM it only requires a modification in the enum and the addition of a new dispenser in the chain. The ATM Service and the dispenser object itself never have to be modified for a change like this!!! It adheres to the Open/Close principle

Well… There is maybe a small change needed. The “validate” method needs to be changed. But that’s because the implementation of the “validate” method isn’t quite right. But that has got nothing to do with the design pattern itself. I will leave it up to you as a form of exercise.

ATM in action

Below an example of the ATM Service in action. The chain is created outside the ATM Service class. The Service does it’s magic and a wallet is returned.

public class ATMClient {
    public static void main(String[] args) {
        Dispenser dispenser = Dispenser.create(
                new Dispenser(Denomination.FIFTY_EURO),
                new Dispenser(Denomination.TWENTY_EURO),
                new Dispenser(Denomination.TEN_EURO)
        );

        ATMService service = new ATMService(dispenser);
        Map<Denomination, Integer> wallet = service.withdraw(180);

        System.out.println("Wallet : " + wallet);
    }
}

Output:
“Wallet : {TWENTY_EURO=1, FIFTY_EURO=3, TEN_EURO=1}”

Going all out

I am finally done… This would be a really good time for me to stop. Really. I could stop right here, do a git-commit and be happy. But I still could do an extra thing or two for more readability. One of those things is creating a separate builder for the creation of the chain of dispensers. This seems like a bit of over-engineering. And maybe it actually is. But just for the fun of it, lets see what we end up with.

What I am trying to do is to move the responsibility of creating the chain of dispensers from the Dispenser class to another class. Here goes.

1. Remove the create method out of the Dispenser class.

public class Dispenser {
    private Dispenser nextDispenser;
    private Denomination denomination;

    public Dispenser(Denomination denomination) {
        this.denomination = denomination;
    }

    public void setNextDispenser(Dispenser nextDispenser) {
        this.nextDispenser = nextDispenser;
    }

    public void execute(int amount, Map wallet) {
        int remainder = addToWallet(amount, wallet);

        if(nextDispenser != null)
            nextDispenser.execute(remainder, wallet);
    }

    private int addToWallet(int amount, Map wallet) {
        if (numberOfDenominations(amount) != 0) {
            wallet.put(denomination, numberOfDenominations(amount));
        }

        return amount - (numberOfDenominations(amount) * denomination.getValue());
    }

    private int numberOfDenominations(int amount) {
        return amount / denomination.getValue();
    }
} 

2. Create the Builder and put the create method, which we removed previously from the Dispenser class, into the Builder class.

public class DispenserBuilder {
    private Denomination[] denomination;

    public static DispenserBuilder aDispenserchain() {
        return new DispenserBuilder();
    }

    public DispenserBuilder consistsOf(Denomination ... denominations) {
        this.denomination = denominations;
        return this;
    }

    public Dispenser build() {
        return createChain(new Dispenser[denomination.length]);
    }

    private Dispenser createChain(Dispenser[] dispenser) {
        assemble(dispenser);
        return link(dispenser);
    }


    private void assemble(Dispenser[] dispenser) {
        for (int i = 0; i < denomination.length; i++) {
            dispenser[i] = new Dispenser(denomination[i]);
        }
    }

    private Dispenser link(Dispenser[] dispenser) {
        for (int i = 0; i < dispenser.length; i++) {
            if (i == dispenser.length - 1)
                return dispenser[0];

            dispenser[i].setNextDispenser(dispenser[i + 1]);
        }
        throw new IllegalStateException("Chain error!");
    }
}

Now we can create the chain with the builder.

Dispenser dispenser = aDispenserchain()
                        .consistsOf(FIFTY_EURO, TWENTY_EURO, TEN_EURO)
                        .build();

I will say it again. Use with caution because this could be labeled as over-engineering, syntactic sugar or whatever you would like to call it. It's really a matter of taste! But hey, it looks cool anyway.

The full solution can be cloned or downloaded at GitHub