Which is the most elegant Design for this simple OOAD problem?
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 o开发者_JAVA百科nly 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.
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?)
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.
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()
精彩评论