tags:

views:

247

answers:

3

I am trying to get started with OOAD, this problem came to my mind and I am not sure I can find a good solution: (it is a supersimplfied version of a real world case).

A fisherman fishes at a pond with a fishing rod. At every launch of the fishing line there is a likelihood of catching a fish equals to 1/10 when there is sunlight and 1/20 at night.

Which classes to define? I would answer: Fisherman, FishingRod, Pond, Day(for modeling night and day).

Which methods? I would answer: Fisherman.Launch(FishingRod), FIshingRod.TryToFish(Pond) returns boolean

How to model the likelihood? Who has the responsabiloity of the likelihood? It doesn't belong to the fisherman, or to the pond. In this example there is a relationship only to daylight, in real world it is probably linked also to fisherman, fishingrod and pond.

How to model the external factor (daylight)?

Any comment is welcome. Also code samples.

UPDATE: The first comment to the question and tdammers's answer force me to be more specific. As I wrote above "it is a supersimplfied version of a real world case", anyway let's say I want to increase the complexity later, not super increase it, let's say increase it enough so that it is good to have all the classes I listed above (for example becuase I keep track of how many fishes are there in the pond, how tired is the fisherman, ...). Anyway the most interesting questions for me are "how to model the likelihood" and "external factors". Treat this as a newbie question for people that has not many skills in OOAD.

+2  A: 

No classes. No methods. One function with three arguments.

bool launch_rod(bool daytime, float chance_daytime, float chance_night) {
    float chance = daytime ? chance_daytime : chance_night;
    float cast = random_float();
    return cast < chance;
}

That's all there is to it. Maybe, just maybe, wrap the whole thing in a class, and make chance_daytime and chance_night properties of that class. Anything else, given the specs, is over-engineering.

tdammers
Lol :) the OP said "it is a supersimplfied version of a real world case". So of course it's over-engineered. The questions still stands though.
Drew Gibson
KeithS
+6  A: 

I would have Fisherman, FishingRod, FishingLine, Pond, Fish and "Sky" (or "Environment").

In object-oriented land, objects usually end up being smarter than you think. Fisherman "has a" (contains) FishingRod. He casts the FishingLine (a component of FishingRod) into Pond. The Pond "looks" at the Sky to determine if it's day or night, then rolls the dice to determine if it should put a Fish on the Line.

The object hierarchy that shakes out is that a FishingLine can optionally contain a Fish, and is owned by a FishingRod, which is owned by a Fisherman. The Pond contains Fish, receives FishingLines but does not "own" them, and also knows about but doesn't own the Sky.

The methods that follow would be something like the following:

Fisherman.FishingRod - an initialization property (or pair of getter/setter methods) used to give the Fisherman a FishingRod to FishAt() a Pond with. This is optional; a Fisherman can create his own FishingRod, or he himself can select it from a collection of FishingRods, instead of the FishingRod being given to him.

Fisherman.FishAt(Pond) - tell the Fisherman to use his FishingRod to Launch() the FishingLine into the Pond, then Retrieve() it to possibly get a Fish.

FishingRod.Launch(Pond) - Releases the FishingRod's FishingLine into the Pond.

FishingRod.Retrieve() - Retrieves the FishingLine from the Pond, returning a Fish, which could also be nothing.

Pond.StockWith(Fish[]) - Gives the Pond Fish for the Fisherman to catch with the FishingRod. Remember that in OO-land, everything has to either be given what it wants or know how to make it; the Pond can just as easily create Fish if that's the model you want to follow, but the user story here didn't say how this happens (usually meaning it's outside the scope of the story).

Pond.SetFishingLine(FishingLine) - used by FishingRod to put its FishingLine in the Pond. This is the "driving function" that incorporates the business logic. When this is called, the Pond should ask the Sky if it's day, and possibly put a Fish on the FishingLine based on the chances given the time of day.

Sky.IsDay() - A method that returns true if it's day and false if it's night.

If you think that a Pond shouldn't directly know the exact rules under which a Fish gets put on a FishingLine, it could give the FishingLine and its Fish[] to what's called a "pure fabrication". This fabrication, "FishingLogic", would be the one to examine the Sky and apply the rules. In development, this is often a good thing to do, because it means that FishingLogic can change without changing Pond, unless FishingLogic needs more from Pond (like water temperature).

The various objects represent various basic "patterns" in real-life programming:

  • Fisherman is an "actor", the closest analog to our user. The user of a system like this basically stands over the actor's shoulder and tells him what to do.
  • FishingRod is a "helper" or "utility". It is a real-world analog to a "tool", and contains a mixture of state and business logic that helps it to perform a very specific task.
  • FishingLine in this model is similar to a "request" or "command". Its sole purpose is to be given from one object to another and, when this happens, it signals that a specific action should be taken by the recipient.
  • Fish is a "response"; the answer to a request. There may be one, maybe not.
  • Pond is a "repository"; it contains things, and handles requests by external objects for those things according to a set of logic.
  • Sky is a "state bucket". It has data, and provides access to that data through its interface.
  • FishingLogic is a "pure fabrication"; it has no analog to a "noun" (object) in the real world we are modeling, and exists to contain environmental rules, or things that happen without the model objects having to know how (how does a fish decide to go on a hook?)
KeithS
Thanks a lot, this is exactly the kind of answer I was looking for. The discussion on FishingLogic is very interesting. Basiaclly all things that in real world things don't do (a Pond doesn't ask the sky) can (it is not mandatory, of course, it depends on how complex is the scenario) be delegated to "pure fabrication". I will keep this answer and review it again in a few months when (I hope) I used OOAD in real world. Thanks.
Very good answer! Just one irk: why does the pond get to decide whether to put a fish on the line? Couldn't the fish itself look at the sky and decide to go for a bite or not? You could then ultimately also have "hunger", "bite happiness", "aggressiveness" etc of a fish determine whether any one fish or more decide to make a run on the hook.
Marjan Venema
@Marjan and Keith: is it so ok to say that until the model is without fishes one class must be responsible for what in fact is a fish decision (go or not at the hook), in this case the FishingLogic class makes sense? Of course if one starts modeling the fishes, the pond becomes more similar to what it is in real life: it is not a mixture of water and fishes, but just a water conatiner for fishes. So if I udnerstand well the more the classes in a domain match the complexity of the "real world", the more the objects will behave "exactly" as in the real world.
@user193655: If you do not have any fish class in your model, then yes of course you need to model elsewhere whether the fishingline cast produces a result or not. And in that case I would prefer a separate class such as FishingLogic instead of adding the logic to another class (such as the Pond). It keeps the design cleaner and more easily adapted/extended.
Marjan Venema
I attached the logic to Pond (either directly or through FishingLogic) because of the nature of the logic. The requirements state that a cast has a certain chance of catching ONE Fish. That means that Pond or FishingLogic must have at least some control over Fish going on the line. If a Fish was smart, and could decide to bite or not, you could naively create a situation where each Fish got to make that decision. That results in a different overall probability for catching a Fish (1-(.9^numberOfFish)). Pond or FishingLogic would have to show the Line to one Fish only and let it decide.
KeithS
+2  A: 

The Fish class could also "look at the sky" to decide how hungry it is. The Pond class seems rather inert.

Fisherman, Rod, Line, Fish, Sky

Fisherman.cast(), .drinkBeer(), .chooseRod(), .addLineToRod()
Rod.cast()
Line.cast()
Fish.bite()
Fish.checkDay()
Sky.isDay()
Tony Ennis