WebCalc iteration 7 - billing

Welcome to the 7th iteration of WebCalc. Previously I scratched a surface of users and security in order to start working on billing. In retrospective, I don’t think I needed any user and Spring Security at all for billing to work, at least not in the first phase. Luckily, their introduction didn’t require much and should not actually hurt. The plan for this iteration is to get the billing as far as possible in terms of functionality. To keep iterations shorter, I’ll restrict myself from extracting billing module this time, that’s the current plan for 8th iteration.

Last time I only managed to get one simple test working, charging one unit of something for calculating a sum. I didn’t specify what kind of unit is it, but it’s of less importance. What I’m currently thinking about is that I should not have added anything to the balance just yet, just count how many times sum was called and only when the balance is checked, calculate something. This would work better if my billing plans were a bit sophisticated and said: “I charge 1 Eurocent per 1000 additions”. I will probably not go in there, I think that there are no interesting problems to solve, it wouldn’t contribute to the idea I have for this series. And if I’m wrong, I can always go and fix. After all my tests are checking the balance after executing some calculations, not that I have some intermediate data or how am I doing things. That’s important in the tests, the should verify the final outcome, not how we do things.

Enough talking, time to start coding. For now, I have a very simple test with just one operation. Let’s add one with two operations and verify, that it still works, like so:

It works. What about multiplication?

That doesn’t work. Fix requires a few changes because I was very minimalistic about my implementation last time:

OK, multiplication works. Do I need a test for two multiplications? I don’t think so, it would work. I can check some crazy combinations later on after I implement all basic operations. Time for subtraction:

I could go on with multiple if ... else statements, but switch is probably a bit more readable.

An issue might be growing in the code. I’m doing a switch over the same input for the second time already, first time was in the Calculator. In general, the switch statement in object-oriented programming suggests, that there’s a hierarchy of objects missing. In general, I agree, but I don’t see it in here yet. This duplication happens in two different modules, once I create a hierarchy of objects it will couple those modules even stronger. I’m also not sure if creating objects with just one operation makes sense, especially that Java already provides BinaryOperator interface. Even more, if I introduce this hierarchy I’d still need to either:

  • add some billing responsibilities to those objects completely breaking single responsibility principle,
  • switch over specific classes and put the logic in the switch or
  • implement a second hierarchy of visitors probably to take care of the billing.

All of those solutions seem either plain wrong or over the top, so I’m not going to do anything about those switches. Maybe in the future I’d see another solution, or maybe it’s not such a big problem after all.

In that case, the only operation missing is division. It is even more complex from a computation point of view, so the price will be even higher.

Now I have all tests green, but those were quite simple. Let’s add something more complex. This time I will parameterize the test, similar to CalculatorShould tests:

OK, single eval calls, even with multiple operations inside, are working. Just in case I’m also adding balance calculations over multiple eval calls:

Surprisingly, it doesn’t work. It has nothing to do with an error in Calculator or Billing, but apparently, I never sanitized my input! That’s a huge security hole and I might need to revisit either Calculator or CalculatorController later on for some more thorough checks. For now a simple trim will suffice:

As for now I only support 4 operations, calculating a balance is complete. Those two switch statements I was talking about previously will become a problem once I decide to add more functions. I would then need to remember to change both places, so I’m making now a mental note to myself to go there some other time and see if I can somehow make it more robust. Calculator will complain about not knowing the operation I want to have evaluated, but billing will just ignore it an not charge anything, I certainly don’t want to do that!

From a billing perspective preparing a more complex statement about what all those charges were for would probably be required. The current implementation is not supporting that. I have also hardcoded all prices in the code, certainly not a sustainable decision. But as promised last time, I’ll keep the iterations shorter, so I’ll finish this one here. I’ll go back to business logic next time after I extract the billing module from app. See you there!