3
\$\begingroup\$

I think that I have big problems with object-oriented analysis of a problem and with writing really good, object-oriented code based on the analysis. When I was trying to write an RPG, it struck me that as a beginner, it is very difficult to plan everything in detail and translate it into good code.

I will explain below how I have implemented a specific problem in code. Some things seem awkward. This is because the code will be extended later and should serve here only as an illustrative example.

The Idea

In role-playing games, a player has abilities that he claims to be in the game world. These skills can be improved. I would like to analyze this seemingly simple aspect analytically.

The concrete Problem

There is a being, that has skills. He can improve the level of his skills with paying money. He can't do it alone. He needs a trainer for that.

We can see, that we have three specific actors. The being, the skill and the trainer.

The being has (a) skill(s) and wants to improve them with his money.

That leads us to the following structure:

class Player
has: gold, skill(s)
can: improve his skills

class Skill
has: level
can: exert hisself

class Trainer
has: gold
can: improve skills of a being that has skills

Implementation

Very often I notice things during the implementation that I did not consider in the analysis and design of the pseudocode. As a result, the implementation is very different from the actual design, and in the worst case becomes illegible.

Again, it happened to me that I noticed facts that I did not consider. For example, that the trainer and the player need additional methods that can be summed up in a superclass.

The player and the coach exchange money with each other. Appropriate methods are needed.

Human.java

public class Human {
    protected int gold;

    public Human(int gold) {
        this.gold = gold;
    }

    public void receiveGold(int value) {
        if(gold > 0) {
            gold += value;
        }
    }

    public void payGold(Human human, int value) {
        if(gold >= value) {
            human.receiveGold(value);
        }
    }
}

The class Player is relatively self-explanatory. There are no noteworthy deviations from the plan.

Player.java

public class Player extends Human {
    private Skill skill;

    public Player(int gold, Skill skill) {
        super(gold);
        this.skill = skill;
    }

    public void improveSkill(Trainer trainer) {
        int price = trainer.train(skill, gold);
        payGold(trainer, price);
    }
}

This could be a class that represents a skill. It can be improved. Taking money for that is the job of the trainer.

Skill.java

// objects from this classes represent an ability, a skill

public class Skill {
    private String name;
    private int level;

    public Skill(String name, int level) {
        this.name = name;
        this.level = level;
    }

    public int getLevel() {
        return level;
    }

    // what the skill actually does has to be defined in a child class
    public void exert() {
        // do something
    }

    public void improve(int value) {
        if(value > 0) {
            level += value;
        }
    }
}

The Trainer can improve a skill of a player or another Being.

Trainer.java

// trainers can improve the skill of a human

public class Trainer extends Human {
    public Trainer(int gold) {
        super(gold);
    }

    public int train(Skill skill, int gold) {
        int price = skill.getLevel() * 5;
        if(price <= gold) {
            skill.improve(1);
        }
        return price;
    }
}

All this can only happen if a matrix is created for it, in which the different entities can interact with each other. I called this class Environment.

Environment.java

public class Environment {
    private Player player;
    private Trainer trainer;

    public Environment() {
        player = new Player(20, new Skill("Pottery", 1));
        trainer = new Trainer(5);
    }

    // now it has to happen!
    public void takePlace() {
        player.improveSkill(trainer);
        // the trainer is nice and wants to pay the money back
        trainer.payGold(player, 10);
    }

    public static void main(String [] args) {
        Environment e = new Environment();
        e.takePlace();
    }
}

What do you think of my implementation of the problem? Did I follow the rules of object orientation, or did I start any style breaks? Are there things that are unattractive or cumbersome and can be better implemented in other ways?

Now if I want to refine the program with textual output, where should the appropriate commands stand? Right there, where the things do happen, or should these commands be outsourced to an external class?

\$\endgroup\$
1
\$\begingroup\$

First, let me second @mkrieger1's suggestion. Go read Eric Lippert's blog posts about how (not) to solve this problem using the type system. In particular, the very last post in the series which encourages you to take a step back.

You have said, "[the player] can improve the level of his skills with paying money. He can't do it alone. He needs a trainer for that."

This does not sound quite right to me. In my experience, the Player can interact with various Non-Player Characters, some of which provide information, some of which provide inane chatter, and some of which are special and perform services.

You are leaving out the interaction part of the equation. It's not a simple case of writing code that directly invokes a dedicated method on an object:

p = getPlayer(100);
t = Trainer(0);

skill = p.getSkill();
t.train(s, 5);

Instead, consider:

A Player enters a Room (for Zork- or Rogue-likes, or a Zone for platformers). The Player moves close to the NPC. The Player interacts with the NPC. One or more Messages are displayed to the Player. The Player selects some Option like "Quit" or "Train." The Option has some effects and displays a Message.

In this case, the NPC was a Trainer and one of the messages displayed would indicate that training was an option. If the player elected to be trained, the option might have the result that nothing happened and a message was sent to the player indicating she didn't have enough gold. Or the result might be that the player's gold balance was decreased and the player's Skill was increased.

It's worth noting that in many cases, training is only an option once. If the trainer has already trained the player, the option is no longer available. Or, possibly the cost changes based on the skill level, or based on the number of trainings already given.

I think if you approach the problem less from the view of "let me model exactly this set of methods" and more from the view of "let me model the actual mechanics of the system" you will discover a different, possibly larger, set of classes and methods/events/messages.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.