Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Rebooting my OOP knowledge (Design Question)

Status
Not open for further replies.

gerikes

Programmer
Jun 5, 2007
5
US
Hi All. Sorry about the length of the post, I have a hard time being brief about anything but new exercise routines.

After having come out of College and learned how Object Oriented Programming worked (what polymorphism is, what an interface is, etc.) I get to the real world and begin to find as I explore more mission-critical, non-academic examples that my knowledge of actually how to program in a true OOP way is severely lacking ("Wait, an interface has a purpose?"). I can normally get by, but I'm a stickler for doing things the "right way".

As always, I trouble myself with a semi-large problem (and as usual it's a game). Along with some TDD techniques, I'm hoping to use it as my experimentation grounds for relearning proper OOP DESIGN techniques, having already the knowledge of how it all works mechanically.

I have a character in the game (a clone of "Harvest Moon", a farming simulator) controlled by the player who can hold a bunch of tools. They can move around the world and, by utilizing certain tools on certain parts of the environment, a different action happens. For example, by using the "watering pot" tool on the ground, the end result is:

1.) The watering pot loses some water.
2.) The area of ground targeted becomes "watered".
3.) The character gains some efficiency points with the specific type of tool that they're using (the watering pot).
4.) The character grows a little more tired.

As you can see, one action causes multiple items to change. Here's how I would have gone about doing it in College:

I have an object for player, plot, tool, etc. In the Player class, I have a "UseTool" method that takes as parameters the tool and the target. If everything looks good (the tool can be used against the target, and the player can use the tool, amongst other things), then the tool is used (the tool might reduce it's durability, the character might become a bit more tired, the target would be affected by the tool, etc.). This giberrish is ALL in the Player class.

Of course, I now know that...

1.) This would not only make the "Player" class be used for just as a object to store the player data, but now it does decisions based on tool usage, which breaks Single-Responsibility.

2.) Whenever I want to add a tool and it's results, I need to change the Player class, breaking OCP.

Probably amongst other things.

So now, I'm thinking to take all the action validation ("Can a player use this tool on this target?") and the action execution ("What happens when they do") and place them into an Action class. When a player tries to use a tool, some process (that I'm not worried about quite yet) will go through and pass in the player, tool used, and target into this new Action object through the constructor, and then have methods to...

1.) Check if the player can use the tool on the target.
2.) Actually go about altering the data if the action is later on carried out.

Code:
public abstract class Action
{
    public bool IsValid();
    public void Execute();
        
    public Action(IActor actor, ITool tool, IActionTarget target)
    {
    }
}

For every action that I want, I would make a new class that overrides the two methods. When a player tries to use a tool, some factory will determine from the tool what type of Action subclass would be used, and create a new object of that subclass.

Ok, now to the actual questions...

1.) How am I doing so far. Any better ideas for something like this?

2.) Realizing that in order to check if an action if valid with a tool, I might need to do calls like tool.CanWater() or target.CanBeWatered(), I could see the tool or target classes becoming filled with "Can___()" properties. One thought I had was to make an interface. Imagine for targets having...


Code:
public interface IWaterable
{
    bool CanBeWatered();
}

And then have code in my action such as...

Code:
public class WateringAction : Action
{
    public bool IsValid()
    {
        if (actionTarget is IWaterable)
        {
            return actionTarget.CanWater();
        }
        else
        {
            return false;
        }
    }

...
}

This way, I only need to put properties onto the possible target classes that need to be there. If a class (say, a bird class) can NEVER be the target of a watering action, then I simply don't put the IWaterable interface onto that unit.

Of course, this seems like I'm just reducing my problem, rather than get rid of it.

So, any tips, pointers on a situation like this?

Thanks in advanced!
 
I think you're doing very well. However, I would be careful about your CanWater thing. It seems to me that all you really care about is whether a given character can use a given tool. You don't need to do something different to validate different characters against different tools. CanUse, for example, would be the only Can_ method I can think of really.

The way that I would do this is to define the criteria evaluated. How tired is the character, what's the character's skill level, the type of the character and so on. Assuming all of these enter into it, your Action object gets passed a character and the character attributes to be evaluated to reach a decision, and also the tool involved. It then reaches a decision and communicates it.

You'll want to get a hard look at the concepts of cohesion and coupling. Use these concepts when determining which objects do which work. Now, I wouldn't couple the actual performing of the work with the Action object, I would couple it with the Tool object, since using a tool is something that is integral to a tool. I would define a Use method as a member of a Tool interface, and the Use method would be implemented differently for each tool. That way, you can work out the logic of a character using a tool independently of the logic of actually having the tool do the work, and you can keep adding new tools without having to redefine the way that characters use them. (Polymorphism at work...)

Now, your last might be helped by an interface. Rather than having all the "cans" you mention, just have an iTool interface that has a CanUse method that takes an iPerson object as an argument. That way, you can pass any person object that implements the iPerson interface to any tool object that implements the iTool interface, and you can use whatever evaluation criteria you like. This has the advantage of decoupling the development process of what to do with a person who can use the tool from the process of determining whether the person can use it or not.

HTH

Bob
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top