2
\$\begingroup\$

Context

I m studying clean code and I m actually working on the banker account Kata. Taken from Sandro Mancuo's github account, here's the job I have to do :

Deposit and Withdrawal
Transfer
Account statement (date, amount, balance)
Statement printing

And here's the Kata rules :

One level of indentation per method
Don’t use the ELSE keyword
Wrap all primitives and Strings
First class collections
One dot per line
Don’t abbreviate
Keep all entities small (50 lines)
No classes with more than two instance variables
No getters/setters/properties

My implementation

Account class :

package com;

public class Account {

    private Operations operations;

    public Account(Euro amountOfInitialOperation) {
        DateOfOperation dateOfInitialOperation = new DateOfOperation();
        Operation defaultOperation = new Operation(amountOfInitialOperation, dateOfInitialOperation);
        operations = new Operations();
        operations.create(defaultOperation);
    }

    public void depose(Euro amount) {
        recordOperation(amount);
    }

    public void withdraw(Euro amount) {
        Euro negativeAmount = amount.negative();
        recordOperation(negativeAmount);
    }

    private void recordOperation(Euro amount) {
        DateOfOperation dateOfOperation = new DateOfOperation();
        Operation newOperation = new Operation(amount, dateOfOperation);
        operations.create(newOperation);
    }

    public String print() {
        return operations.formatBankOperations();
    }
}

DateOfOperation class :

package com;

import java.util.Date;

public class DateOfOperation {
    private Date date;

    public DateOfOperation() {
        date = new Date();
    }

    public String formatInFrench() {
        return "Formated Date";
    }
}

Euro class :

package com;


public class Euro {

    private long value;

    public Euro(long theValue) {
        value = theValue;
    }

    public String moneyRepresentation() {
        return  value + " euros";
    }

    public Euro negative() {
        return new Euro(-value);
    }

    @Override
    public boolean equals(Object obj) {
        Euro other = (Euro)obj;
        return value == other.value;
    }
}

Operation class :

package com;

public class Operation {

    private Euro amount;
    private DateOfOperation date;

    public Operation(Euro theAmount, DateOfOperation theDate) {
        amount = theAmount;
        date = theDate;
    }

    public String formatDetails() {
        String stringDate = date.formatInFrench();
        String moneyRepresentation = amount.moneyRepresentation();
        StringBuffer formatedDate = new StringBuffer();
        formatedDate
                .append(stringDate)
                .append(" - ")
                .append(moneyRepresentation)
                .append("\n");
        return formatedDate.toString();
    }
}

Operations class :

package com;

import java.util.ArrayList;
import java.util.List;

public class Operations {

    private List<Operation> operations;

    public Operations() {
        operations = new ArrayList<Operation>();
    }

    public void create(Operation operation) {
        operations.add(operation);
    }

    public String formatBankOperations() {
        StringBuffer details = new StringBuffer();

        for(Operation operation : operations) {
            details.append(operation.formatDetails());
        }

        return details.toString();
    }
}

Where do my problems come from


On the Account class

When working on the Account class, I was facing some problems while recording transaction. In fact, I wanted to test the deposit function and I had no way to mock the DateOfOperation (not injected, but created from a hidden way).

From a technical point of view, this method is private, and its creating a new DateOfOperation to create a new Operation. But the fact of creating this date internally seems to break some SOLID principles.

From a business point of view, I don't want people to provide me some DateOfOperation as argument, because they could have modified that date while passing it as argument. And my operation could now be false.

So, I don't know how to test this result, and I m lost between the two possibility : matching absolutely SOLID, or accepting that I'm completely hiding the date creation of the operation. One step further to blow my mind, Uncle Bob, in Clean Code proposes to have the fewest parameters possible while creating methods. If we have too much parameters, we should split it in multiple classes / functions (I think I m in this case)


On the DateOfOperation class

The DateOfOperation exists only to match the rule :

Wrap all primitives and Strings

which, in an Calisthenic objects context, means that we have to wrap primitives (even if Date is a class definition... same for List, String etc...)

This way, I m completely hiding my Date (at a Java sens) creation inside of the DateOfOperation constructor.

The problem here is that I can't mock my Date, and my expectation in unit test can't be true...

What I m looking for

I m looking for some explanations, or code modifications that could be applied in a calisthenic objects context, that would help me correcting the previous problems, or understanding the limit of that kind of stuff.

\$\endgroup\$
1
\$\begingroup\$

The main question here is in the understanding of the business context: do you want the date of operation as an external parameter (I tell you now that I will have a withdrawal next Tuesday) or always as "now"?

If it is always "now", create it internally like you do right now. If you want to test the result of "new Date()" to equal a given predefined value, you could either use Powermockito to mock the new operator on Date, or inject a date factory which you can mock.

Apart from that, there are two problems with your naming which make understanding the code quite hard:

  • Operation is de facto a "Deposit"
  • Operations.create() is add
\$\endgroup\$
  • \$\begingroup\$ Thanks for this information. I really want the operation to exist now, so I'm going to learn how to mock the new Date(). I m going to refactor the informations you provided, and give a new representation of what I've done in the day :). Thanks again for your reply \$\endgroup\$ – mfrachet Jan 25 '17 at 9:59
  • 1
    \$\begingroup\$ @Margin If you need a starting point for the mocking part, have a look at stackoverflow.com/questions/5920153/… Good luck! :) \$\endgroup\$ – mtj Jan 25 '17 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.