• Search in book...
• Toggle Font Controls

# Chapter 3. Portfolio

Penny wise and Dollar foolish 1

Tired Proverb

We can multiply and divide amounts in any one currency by numbers. Now we need to add amounts in multiple currencies.

 5 USD x 2 = 10 USD 10 EUR x 2 = 20 EUR 4002 KRW / 4 = 1000.5 KRW 5 USD + 10 EUR = 17 USD 1 USD + 1100 KRW = 2200 KRW Remove redundant `Money` multiplication tests

In this chapter, we’ll deal with the mixed-mode addition of currencies.

# Designing our next test

To test drive the next feature — 5 USD + 10 EUR = 17 USD — it’s enlightening to first sketch out how our program will evolve. TDD plays nicely with software design, contrary to prevailing myths!

The feature, as described in our feature list, says that 5 Dollars and 10 Euros should add up to 17 Dollars, assuming we get 1.2 Dollars for exchanging one Euro.

However, it’s equally true that:

`1 EUR + 1 EUR = 2.4 USD`

Or, rather obviously:

`1 EUR + 1 EUR = 2 EUR`

Ah: an epiphany! When we add two (or more) `Money` s, the result can be expressed in any currency, as long as we know the exchange rate between all currencies involved (i.e. from currency of each `Money` into the currency in which we want to express the result). This is true even if all the currencies involved are the same, as in the last example — which is just one particular case out of many.

###### Tip

Test-driven development gives us an opportunity to pause after each RGR cycle and design our code intentionally.

We realize that “Adding Dollars to Dollars results in Dollars” is an oversimplification. The general principle is that adding Money in different currencies gives us a Portfolio; which we can then express in any one currency (given the necessary Exchange Rates between currencies).

Did we just introduce a new entity: Portfolio? You bet! It’s vital to let our code reflect the realities of our domain. We’re writing code to represent a collection of stock holdings; for which the correct term is a Portfolio. 2

When we add two or more `Money` s, we should get a `Portfolio`. We can extend this domain model by saying that we should be able to `evaluate` a Portfolio in any specific `currency`. These nouns and verbs give us an idea about the new abstractions in our code which we’ll drive out through tests.

###### Tip

Analysis of the problem domain is an effective way to discover new entities, relationships, functions, and methods.

Given this new realization, let’s add the simpler case of adding two `Money` s in the same currency first, deferring the case of multiple currencies until later:

 5 USD x 2 = 10 USD 10 EUR x 2 = 20 EUR 4002 KRW / 4 = 1000.5 KRW 5 USD + 10 USD = 15 USD 5 USD + 10 EUR = 17 USD 1 USD + 1100 KRW = 2200 KRW Remove redundant `Money` multiplication tests

Let’s build this feature: adding `Money` s together. We’ll start with a test to add two `Money` s in the same currency, using the `Portfolio` as a new entity.

## Go

Here’s our new test, `TestAddition`, which we add after the existing tests in `money_test.go`:

````func` `TestAddition``(``t` `*``testing``.``T``)` `{`
`var` `portfolio` `Portfolio`
`var` `portfolioInDollars` `Money`

`fiveDollars` `:=` `Money``{``amount``:` `5``,` `currency``:` `"USD"``}`
`tenDollars` `:=` `Money``{``amount``:` `10``,` `currency``:` `"USD"``}`
`fifteenDollars` `:=` `Money``{``amount``:` `15``,` `currency``:` `"USD"``}`

`portfolio` `=` `portfolio``.``Add``(``fiveDollars``)`
`portfolio` `=` `portfolio``.``Add``(``tenDollars``)`
`portfolioInDollars` `=` `portfolio``.``Evaluate``(``"USD"``)`

`assertEqual``(``t``,` `fifteenDollars``,` `portfolioInDollars``)`
`}````

Notice that we have declared the `portfolio` and `portfolioInDollars` variables explicitly, to emphasize their types. The verbosity makes things clear to us as we proceed.

What our test says is:

1. We can start with an empty `Portfolio`.

2. We can then `Add` multiple `Money` structs to a `Portfolio`.

3. We can ask the `Portfolio` to `Evaluate` itself in a specific currency.

4. Finally, the result of the evaluation should be a `Money` with the correct amount and currency.

Of course, in our current simple case, the currency is always the same, so exchange rates don’t (yet) become a concern. Let’s walk before we run!

By now, we’re very accustomed to errors like `undefined: Portfolio`. Let’s speed ahead and implement the barest possible `type Portfolio` to get beyond these errors. Here’s what it looks like, added to the end of `money_test.go`:

````type` `Portfolio` `[]``Money`

`func` `(``p` `Portfolio``)` `Add``(``money` `Money``)` `Portfolio` `{`
`return` `p`
`}`

`func` `(``p` `Portfolio``)` `Evaluate``(``currency` `string``)` `Money` `{`
`return` `Money``{``amount``:` `15``,` `currency``:` `"USD"``}`
`}````

We declare a new type `Portfolio` as an alias for a slice of `Money` s. We then define the two missing methods: `Add` and `Evaluate`. The signatures of these methods are suggested by the failing test we wrote. The implementation is the least possible code to get the test to pass — including the “silly” hard-coded `Money` that `Evaluate` returns.

In an earlier round of RED-GREEN-REFACTOR, we recognized the subtle duplication in the test and production code and used it change the “silly” implementation to a more correct one. Where is the duplication in this case? Yep: it’s the “15” that’s in both the test and production code.

We should replace the hard-coded `15` in `Evaluate` method with code that actually sums up the `amount` in the `Money` s:

````func` `(``p` `Portfolio``)` `Evaluate``(``currency` `string``)` `Money` `{`
`total` `:=` `0.0`
`for` `_``,` `m` `:=` `range` `p` `{`
`total` `=` `total` `+` `m``.``amount`
`}`
`return` `Money``{``amount``:` `total``,` `currency``:` `currency``}`
`}````

Hmm… our `TestAddition` fails with an assertion failure:

``...` `Expected`  `[{``amount``:``15` `currency``:``USD``}]` `Got``:` `[{``amount``:``0` `currency``:``USD``}]``

Ah! We are iterating over an empty slice. We made the correct change to `Evaluate`, however, our `Add` method still has a trivial (“silly”) implementation. Let’s fix that, too:

````func` `(``p` `Portfolio``)` `Add``(``money` `Money``)` `Portfolio` `{`
`p` `=` `append``(``p``,` `money``)`
`return` `p`
`}````

The test is now green.

We know that the value of `currency` in the `Money` struct returned by `Evaluate` is whatever was passed in as the first (and only) parameter to that method. This is obviously not the right implementation: it only works because our test uses two `Money` s that both have the same currency, and then calls `Evaluate` also with the same currency.

Should we test-drive our way to removing this “silly” behavior of our code, or use our “refactoring budget” (now that we have green test) to do it?

There is no one-size-fits-all answer. TDD allows us to define for ourselves how fast we want to go. In our case, we have good reason to defer fixing the “silly” behavior of our code.

We know that when we `Evaluate` a `Portfolio` containing `Money` s with different currencies, we’ll have to use exchange rates — a concept we haven’t defined yet. We also know that we have an item on our to-do list — 5 USD + 10 EUR = 17 USD — that will compel us to test-drive this mixed-currency feature. Therefore, we can defer the change for a bit: the “silly” implementation survives to see another day. Or maybe ten more minutes.

## JavaScript

Here’s our new test for adding two `Money` s, which we add to the very end of `test_money.js`:

````fifteenDollars` `=` `new` `Money``(``15``,` `"USD"``);`
`portfolio` `=` `new` `Portfolio``();`
`portfolio``.``add``(``fiveDollars``,` `tenDollars``);`
`assert``.``deepStrictEqual``(``portfolio``.``evaluate``(``"USD"``),` `fifteenDollars``);````

What our test says is:

1. We start with an empty `Portfolio` object.

2. We then `Add` multiple `Money` objects to this `Portfolio`. We use our pre-existing `fiveDollars` and `tenDollars` objects.

3. We ask the `Portfolio` to `evaluate` itself in a specific currency.

4. Finally, the result of the evaluation should be a `Money` object with the correct amount and currency.

In this test case, the currency is the same throughout, so exchange rates don’t yet become a concern.

By now, we’re very accustomed to errors like `ReferenceError: Portfolio is not defined`. Let’s speed ahead and implement the barest possible `class Portfolio` to get beyond the errors and a quick passing test.

````class` `Portfolio` `{`
`add``(``money``)` `{`
`}`

`evaluate``(``currency``)` `{`
`return` `new` `Money``(``15``,` `"USD"``)`
`}`
`}````

We define a new `Portfolio` class below the pre-existing `Money` class in `test_money.js`. We give it the two methods our test demands: `add` and `evaluate`. The signatures of these methods are also evident from our test. In `evaluate`, we implement the quickfire solution that gets our test to pass: always return a `Money` object representing “15 USD”.

In an earlier round of RED-GREEN-REFACTOR, we recognized the subtle duplication in the test and production code and used it change the trivial (“silly”) implementation to a more correct one. Where is the duplication in this case? Yep: it’s the “15” that’s in both the test and production code.

Now that our tests pass, we should replace the hard-coded `15` in `evaluate` method with code that actually sums up the `amount` in the `Money` s:

```    `evaluate``(``currency``)` `{`
`let` `total` `=` `this``.``moneys``.``reduce``(` `(``sum``,` `money``)` `=>` `{`
`return` `sum` `+` `money``.``amount``;`
`},` `0``);`
`return` `new` `Money``(``total``,` `currency``);`
`}````

We use the `reduce` function for an array. We declare an anonymous function that adds up the `amount` of each `Money` object, thereby reducing the array `this.moneys` to a single scalar value. We then create a new `Money` object with this `total` and the given `currency` and return it.

###### Tip

ES6 Arrays are list-like objects whose prototype defines methods like `map`, `reduce`, and `filter` to facilitate a functional programming style.

The `evaluate` function, predictably, results in an error:

```        `let` `total` `=` `this``.``moneys``.``reduce``(` `(``sum``,` `money``)` `=>` `{`
`^`
`TypeError``:` `Cannot` `read` `property` `'reduce'` `of` `undefined````

Let’s define the missing `this.moneys` array in a new `constructor` in the `Portfolio` class:

```     `constructor``()` `{`
`this``.``moneys` `=` `[];`
`}````

After adding the constructor, we get an interesting assertion error:

````AssertionError` `[``ERR_ASSERTION``]``:` `Expected` `values` `to` `be` `strictly` `deep``-``equal``:`
`+` `actual` `-` `expected`

`Money` `{`
`+`   `amount``:` `0``,`
`-`   `amount``:` `15``,`
`currency``:` `'USD'`
`}````

Ah! We are iterating over an empty array. Our `evaluate` method and `constructor` are correct, however, our `add` method is still empty. Let’s rectify this shortcoming. We’ll use the implicit `arguments` object to allow multiple `Money` s to be added simultaneously:

```    `add``()` `{`
`this``.``moneys` `=` `this``.``moneys``.``concat``(``Array``.``prototype``.``slice``.``call``(``arguments``));`
`}````

The test is now green.

## Python

Here’s our new test for the addition of two `Money` objects, which we append to our growing list of tests in `TestMoney` class:

```  `def` `testAddition``(``self``):`
`fiveDollars` `=` `Money``(``5``,` `"USD"``)`
`tenDollars` `=` `Money``(``10``,` `"USD"``)`
`fifteenDollars` `=` `Money``(``15``,` `"USD"``)`
`portfolio` `=` `Portfolio``()`
`portfolio``.``add``(``fiveDollars``,` `tenDollars``)`
`self``.``assertEqual``(``fifteenDollars``,` `portfolio``.``evaluate``(``"USD"``))````

What our test says is:

1. We start with an empty `Portfolio` object.

2. We then `Add` multiple `Money` objects to this `Portfolio`.

3. We ask the `Portfolio` to `evaluate` itself in a specific currency.

4. Finally, the result of the evaluation should be a `Money` object with the correct amount and currency.

In this test case, the currency is always the same, so exchange rates don’t yet become a concern.

We’re now quite accustomed to errors like `NameError: name 'Portfolio' is not defined`. Let’s speed ahead and implement the smallest possible `class Portfolio` to get beyond these errors and a passing test. We add the new class after the `Money` class definition in `test_money.py`.

````class` `Portfolio``:`
`def` `add``(``self``,` `*``moneys``):`
`pass`

`def` `evaluate``(``self``,` `currency``):`
`return` `Money``(``15``,` `"USD"``)````

The `Portfolio` class has a no-op `add` method, and an `evaluate` method with a “silly” implementation that always returns a `Money` object that’s worth “15 USD”. Just enough code to get a passing test.

In an earlier round of RED-GREEN-REFACTOR, we recognized the subtle duplication in the test and production code and used it change the trivial (“silly”) implementation to a more correct one. Where is the duplication here? Yep: it’s the “15” that’s in both the test and production code.

We can replace the hard-coded `15` in `evaluate` method with code that actually sums up the `amount` in the `Money` s:

````import`` ``functools`` ````
````import`` ``operator`` ````
````.``.``.````
````class`` ``Portfolio``:````
````.``.``.````
````    ``def`` ``evaluate``(``self``,`` ``currency``)``:````
````        ``total`` ``=`` ``functools``.``reduce``(````
````            ``operator``.``add``,`` ``map``(``lambda`` ``m``:`` ``m``.``amount``,`` ``self``.``moneys``)``)````
````        ``return`` ``Money``(``total``,`` ``currency``)````

The `functools` package gives us the `reduce` function

The `operator` package gives us the `add` function

This code uses Python’s functional programming idioms. The best way to understand how `total` is derived is to unravel the expression from the inside out:

1. We import the packages we need: `functools` and `operator`.

2. Using a `lambda` expression, we `map` the `self.moneys` array to a map of only the `amount` s in each `Money` object.

3. We then `reduce` this `map` to a single scalar value, using the `operator.add` operation.

4. We assign this scalar value to the variable named `total`.

5. We finally create a new `Money` object using this `total` and the `currency` passed in the first (and only) parameter to the `evaluate` method.

Phew: that one line of functional code sure packs a lot of punch!

###### Tip

Python has rich support for functional programming, including `map`, `reduce`, and `filter` in the `functools` package; and custom-written `lambda` functions.

We’re not done yet: when we run our test, the error message `AttributeError: 'Portfolio' object has no attribute 'moneys'` reminds us of that. Let’s add an `__init__` method that initializes this missing attribute in `Portfolio`:

```    `def` `__init__``(``self``):`
`self``.``moneys` `=` `[]````

Ah: this gives us a new error! `TypeError: reduce() of empty sequence with no initial value`. We realize two things:

1. The `add` method in `Portfolio` is still a no-op. That’s why our `self.moneys` is an empty array; and

2. Notwithstanding the above problem, our code should still work with an empty array.

We fix these two shortcomings by the following code changes in `Portfolio`:

````    ``def`` ``add``(``self``,`` ``*``moneys``)``:````
````        ``self``.``moneys``.``extend``(``moneys``)````
``````
````    ``def`` ``evaluate``(``self``,`` ``currency``)``:````
````        ``total`` ``=`` ``functools``.``reduce``(````
````            ``operator``.``add``,`` ``map``(``lambda`` ``m``:`` ``m``.``amount``,`` ``self``.``moneys``)``,`` ``0``)`` ````
````        ``return`` ``Money``(``total``,`` ``currency``)````

The last parameter to `reduce` (0 in our case) is the initial value of the accumulated result

We give the `add` method its correct implementation: it accumulates any given `Money` s in the `self.moneys` array. And we add an initial value of `0` to our call to `functools.reduce`. This ensures that the code works even when there is an empty array.

All tests are now green.

# Committing our changes

We have the addition feature implemented for `Money` s in the same currency. This suggests the appropriate message for our next commit to our local Git repository:

```git add .
git commit -m `"feat: addition feature for Moneys in the same currency done"````

We now have three commits in our Git repository.

# Where We Are

We started to tackle the problem of adding different representations of `Money`. This new feature requires us to introduce a new entity to our code, which we named `Portfolio`. Addition of `Money` s also requires introduction of exchange rates. Since that is too much to take on all at once, we used a divide-and-conquer strategy to first add two `Money` s and evaluate the value of the `Portfolio` all in the same currency. This allows us to gently introduce the concepts of Portfolio and addition of `Money` s.

This divide-and-conquer strategy means our `Portfolio` is far from finished. It needs to be enhanced to `evaluate` correctly when the `Money` s in it have different currencies, as well as when the currency of evaluation is different.

Also, we can’t help noticing that our source code is growing as we accrete tests and features. No surprises there! However, it’s getting a bit too long to all be in one file. We need to restructure our code: separating the test code from the production code would be a good start.

For now, let’s take a deep breath and celebrate crossing one more item from our feature list, before we pick up the next item.

 5 USD x 2 = 10 USD 10 EUR x 2 = 20 EUR 4002 KRW / 4 = 1000.5 KRW 5 USD + 10 USD = 15 USD 5 USD + 10 EUR = 17 USD 1 USD + 1100 KRW = 2200 KRW Remove redundant `Money` multiplication tests

1 Or, to unfurl the brows of my many and dear British friends, “Penny wise and Pound foolish”!

2 Are there other entities that we should also have in addition to “Money”? Possibly. However, the “Money” abstraction meets our current needs. We’ll add one more entity later in Chapter 11, when its time comes.

• No Comment
..................Content has been hidden....................