On paperboys, newsagents and exceptions

A few days ago I asked whether an exception should be thrown when the customer cannot pay the paper-boy. The responses (thank you!) ranged widely across the spectrum between Yes and various shades of No, and are well worth taking a few minutes to read before we go any further. What follows is my take on the code…

TL;DR — replace the exception by giving the paper-boy a coping strategy

Let’s think about the real-world (domain) model for a minute:

Firstly, I note that I have been that customer on many occasions. I’m frequently not at home when the paper-boy or the Avon lady or the window-cleaner comes around (honest!). I would expect that non-payment is statistically likely to occur on pretty much every collection round.

Now, presumably the paper-boy is on his round, calling on the newsagent’s customers. He may be only collecting money, or he may also be delivering papers. As he goes from house to house everything runs smoothly, in that every customer so far has paid their bill; he has a bulging bag of money, and has marked ‘Paid’ against every customer on the first page of his little Accounts book. But the next customer doesn’t have the funds to pay! The paper-boy hasn’t been told what to do, and wasn’t expecting this eventuality at all. He panics and runs screaming back to the newsagent.

The paper-boy runs into the arms of the newsagent, who sees that someone couldn’t pay. Thankfully the boy still has the money he has collected thus far. But he also has hold of the last customer’s wallet. And in his panic he is so incoherent that he can’t say which customer couldn’t pay [note that the exception has no constructor parameters]. At this point we have to make some assumptions about where the newsagent might be standing:

If he is back at the shop, then the paper-boy has run so far that his round must now be assumed to be abandoned; the newspapers (and the bills) of any remaining customers will need to be dealt with on another round. Alternatively, the newsagent might be accompanying the boy on his round, telling him which house to visit next; in this case he can simply calm the boy down, deal with the matter himself, and the pair can then proceed with the round.

Neither of these alternatives seems entirely satisfactory. In the first case, it seems unreasonable that neither the boy nor the newsagent would learn how to cope with non-payment (which must surely happen regularly). Even if the boy wasn’t told about the possibility before his first ever round, he wouldn’t continue behaving that way forever. And no sensible newsagent would be happy to have to re-start the boy’s rounds after each non-payment.

But in the second case, why have a dog and bark yourself? Why is the newsagent accompanying the boy, unless this is a training exercise? Again, not a scenario that is likely to be oft repeated.

I assume therefore that, in the real world, the boy is sent on the round because the newsagent wants to stay and mind the store. The newsagent wants to boy to collect his customers’ dues (and perhaps deliver papers too), without the newsagent having to break off his work (and potentially finish the round himself, or send another boy to do that later). It’s likely therefore that the boy will have been given instructions such as “If anyone isn’t at home, or can’t pay, just make a note in the book. We’ll see them next week, or send an invoice, but don’t you worry about that for now.” That is, the newsagent gives the boy a coping strategy. The boy’s responsibility is to mark ‘Paid’ or ‘Not paid’ against each customer, and to collect money where possible. The newsagent will reconcile these notes against his accounts later in the day and decide what to do, both with the money and with the non-payers.

So, back to the code. Throwing this exception forces some other part of our code (presumably the Newsagent) to cope with it; the catch clause will be an example of a “type 7” conditional in my little catalogue. I don’t believe it models the domain at all faithfully. Alternatively, we could avoid the exception if the paper-boy returned a status code. But that seems to me to be pretty much the same as the scenario in which the newsagent accompanies the boy on his round (and it leaves the Newsagent having to implement a “type 1” conditional too).

Instead, I agree with Paul D’Ambra: I think we should give the Paperboy a coping strategy: Equip him with code to call when the customer cannot pay. And in the interests of symmetry we might go further, having the boy update the accounts directly as he goes.

There are numerous ways to express this in code. For example, we could implement this using the Observer pattern, either with a directly coupled listener:

class Paperboy {
  private int collected_amount;
  private Newsagent newsagent;

  public void collect_money(Customer customer, int due_amount) {
    int amount_received = customer.payWhatYouCanAfford(due_amount);
    collected_amount += amount_received;
    newsagent.customerPaid(amount_received, customer);
  }
}

Or with a decoupled event notification mechanism:

class Paperboy
  def collect_money(customer, due_amount)
    amount_received = customer.pay_what_you_can_afford(due_amount)
    @collected_amount += amount_received
    EventBus.announce :customerPaid, amount: amount_received, customer: customer
  end
end

Another conditional avoided. (And a domain more faithfully modelled, in my opinion.)

Advertisements

4 thoughts on “On paperboys, newsagents and exceptions

  1. I quite agree that this isn’t an exception, it is entirely expected! Two thoughts spring to mind:
    1) I always thought the job of the paper-child was to leave the paper before you got out of bed and every so often one settled up with the newsagent directly. Getting me out of bed to ask for money would lead to cancellation of service!
    Even so…
    2) Somewhere along the line you need a conditional. This just (correctly) shifts it from the money collector. But eventually, if I don’t pay, someone has to take the decision to stop giving the paperboy my paper to deliver!

  2. Thank you, this has been good food for thought. I do have a quibble about context. The caller of collect_money() is surely the paperboy himself, not the newsagent: otherwise we have a loop where the boy returns to the newsagent after every customer to find out who the next customer is. Likewise, the boy would not call the newsagent after every customer to tell whether they paid or not. I think it would make sense to return a list of results (and that corresponds closely to the little Accounts book).

  3. I agree with the revision, but it does raise another thought experiment in my mind: what should happen when the EventBus/newsagent is (for some reason) unreachable, or the customer has already paid the newsagent while the paperboy was doing the rounds (a transactionality failure)? Is there any point at which we should throw an exception in this small domain?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s