Now that I’ve selected the riverboat velocity problems as the problem domain, and understood what is involved with solving the problem, I need to come up with a way to abstract the solving of this problem. As it turns out, the concept of solving a mathematically-based problem is inherently the same as what a computer procedure or function does. You have a set of inputs, which you run through some kind of algorithm, and produce a set of desired outputs.

Accordingly, I could just map a riverboat velocity problem onto this directly, such as a programming procedure akin to:
Vector CalculateResultantVelocity(Vector boatVelocity,
Vector currentVelocity);
The strength of this approach is that it’s simple, straightforward, and easy to code. But there are several problems with this approach. The biggest weakness is that it’s just too simple. The reality is that many problems in physics (and other domains) are actually composed of other smaller problems. You often find that solving a problem involves breaking down a large problem into several steps or components and solving them. For example, here is a diagram of a problem which is actually done in two steps.

Each step has its associated input and output — and notice that the output of the first subproblem becomes the input of the second subproblem. Even a simple equation like
a = 2 * ( 3 + 2 ) – 4 / 2
can be broken down to individual arithmetic operations, arranged in particular orders. The point is that for a complex problem, part of the solution is breaking down that problem into appropriate components, each of which are simpler to solve.
So what does this look like for a riverboat velocity problem? Here’s a diagram for calculating the resultant velocity of the boat and river current:

There are several inputs — the speed and direction of both the boat and the river current. The output we are aiming for is the resultant velocity, both speed and direction. As it turns out, this problem can be broken into two parallel components — calculating the resultant magnitude of the velocity (or speed), and calculating the resultant direction. The first problem only needs the magnitudes to solve, while the 2nd requires all four inputs.
The two respective problems are actually are just geometry/trigonometric calculations applied to a real world situation. You could argue that translating from “Calculate Resultant Magnitude” to “Use Pythagorean Theorem” isn’t a problem — that both algorithms are the same thing mathematically. But you could also argue that part of the problem in solving the real world problem is knowing which formula to apply, and which parameters apply to which information you have. In fact, some of the other riverboat problems require only arithmetic — what students get wrong is which values to plug in.
What consequence does this have for programming these problems? One of the patterns that emerge immediately from looking at the last two diagrams is that the problem and its subproblems are recursive in nature. That is, there really is nothing to distinguish a subproblem vs. its parent problem. They are all just problems, really — they have inputs and outputs, and an associated algorithm that is operated on to convert between the two.
This means, in programming speak, that we’ll probably want to model the “idea” of a problem as a class (actually an interface) that both subproblems and problems derive or implement. And, that a problem can have child problems (the subproblems). I’m not all too sure right now whether a problem also needs to have a reference back to its parent problem, but the answer will probably emerge later as coding begins.
The point of this exercise was to do a little thought before coding, but not so much that we fall into an analysis paralysis funk. It will be interesting what kind of class design eventually emerges after a few iterations on the problem domain, and how flexible it will ultimately be, when trying to add support for other algorithms and problems that are very different.