Fantasy RPG (role skills and Strategy Mode)

Source: Internet
Author: User
Document directory
  • Implementation problems in the base class
  • Override the problem of Basic Methods
PDF download: http://www.tracefact.net/Document/Strategy.pdf

Introduction to fantasy RPG (role skills and Strategy Mode)

I have read some books and articles on design patterns. Although they are formal and authoritative (and I think there are some stereotypes), they always feel that people are not so close. As a result, I think about writing my own understanding of the design pattern like writing a story. We will use a fantasy role-playing game (D & D) as a blueprint to demonstrate the GOF design pattern through module creation or function implementation in the game. Of course, this is not a real game, just to understand the design model, so I will try to make the game as simple as possible. Let's Start off.

Inheritance and Problems

Before starting our game journey, we need to define the roles that players can choose. We first thought of four role occupations: Barbarian, Soldier, Paladin, and Wizard ).

According to the idea of OO, we need to first define an abstract class as the base class, and then inherit from the four occupations to realize code reuse. Before that, let me analyze the role's capabilities (methods ):

  • DisplayInfo (): displays basic information about a role. (For example, server GUARD: passion for perfection, determination to maintain the law, and fight back evil forces-three weapons of server guard ...)
  • Walk (): Let the role Walk.
  • Stay (): Let the role stand.

Generally, this principle is followed during design:

  1. For all the inherited classes, but the implementation of each inherited class is different, we only give the definition in the base class, do not give the implementation, but implement it in the inherited class. In other words, an abstract method is defined in the base class.
  2. For all inherited classes, and each inherited class implements exactly the same method, we implement it directly in the base class, and inherit from the Child class to achieve code reuse.

Obviously, each role has the capabilities of DisplayInfo (), Walk (), and Stay. DisplayInfo () is different for each role, while Walk () and Stay () are the same for each role. Therefore, we constructed the base class Charactor and implemented the following design:

Implementation problems in the base class

So far, our program has implemented only four different roles and can walk and stand. To enrich roles, we now allow them to assemble weapons. Therefore, we need to add a new method. we name it UseWeapon (), its implementation effect is that the role picks up a sword. We first thought of putting UseWeapon () in the base class, so that code can be reused.

Therefore, our design becomes:

This looks good. We use the inheritance in four object-oriented ideas (abstraction, encapsulation, inheritance, and polymorphism. However, it was not a long time. In a few days, we felt that the role settings were monotonous and had a bad personality. We wanted to modify the game rules as follows:

  1. The Barbarian uses an axe.
  2. The mage does not need weapons.

This leads to the use of inheritance in this example: We found that both the mage and the barbarian can use the sword, which is not in line with the game rules we have defined.

We abstracted the problem above and concluded that:Adding an entity Method to the base class makes it possible for subclasses that do not have this method to also have this method, and all subclass methods have the same implementation.

Override the problem of Basic Methods

This problem seems to be well solved. Since the savage and the mage are different, we only need to let the savage and the mage overwrite the base class's UseWeapon () method. At the same time, we declare the base class method as a virtual method and provide the implementation. This implementation can be considered as the default Implementation of the role (the default role uses the sword ).

This time, our design becomes as follows:

In this way, it seems that this problem has been well solved. One day, we have new requirements:

  1. You need to add a new role Warrior (Warrior). He also uses an ax.
  2. A new role called Cleric is required, and he does not use weapons.

It also brings about new problems:

  1. We didn't implement code reuse. For barbarian and warrior, their implementations of UseWeapon () are the same (all with axes ), but we have to write exactly the same code in each subclass.
  2. If the implementation of this method needs to be changed frequently, we need to modify the methods in all related subclasses repeatedly.
  3. The priest and MAGE do not use weapons, but they all inherit the UseWeapon () method, even if they overwrite the base class method with a (empty) UseWeapon () method that does nothing, they will still expose UseWeapon () capabilities (this method can be accessed from their instances ).
Interface and Problems

We found that inheritance is not so easy to use, especially the use of Inheritance Problem 3 seems to be difficult to solve, so we changed our thinking. This time, we used interfaces for implementation.

We define an IWeaponable interface, remove the UseWeapon () method from the base class, and then implement this interface for subclasses that can use weapons; this interface is not implemented for sub-classes (priest and MAGE) that cannot use weapons.

The current design is like this:

The new problems caused by using interfaces are far more than they solve. First, let's take a look at what problems it solves:

  • The priest and MAGE no longer have the ability to use weapons, and their instances will not expose the UseWeapon () method.

Let's look at what problems it has caused:

  1. Because an interface is just a contract and does not contain implementations, a large number of subclasses need to implement this interface.
  2. The code is not reused. The UseWeapon () method is the same for all the roles that use swords and axes. If modification is required in the future, all related subclasses must be modified.
Encapsulation Behavior

Currently, when we design this role, the UseWeapon () method is either implemented in the base class or in the subclass, whether it is using inheritance or interfaces, in fact, we are all oriented towards implementation programming. One principle of OO is interface-oriented programming. One of the main problems facing programming is that it is not flexible enough. In this example, all roles can either use swords, axes, or nothing. If I want a single role to use an ax and a sword, that is, dynamically assigning weapons to it, it cannot be achieved.

OO has a principle called "Encapsulate what varies", which is also known as "encapsulation change ". In our current situation, UseWeapon () is constantly changing, so we should try to encapsulate it. This time, we still need to use the interface, but the class implementing this interface is no longer the base class or subclass of the role we define, but the class dedicated to the behavior of UseWeapon. As shown in:

We can see that the implementation of the interface is divided into its own inheritance system, rather than in our role class. Each class that implements this interface implements a specific implementation of the UseWeapon () method. For example, the UseWeapon () Implementation of UseSword class is to pick up a sword. UseWeapon Implementation of UseAx class is to pick up an ax. However, the implementation of UseNothing does nothing. The role simply sends a complaint: I can't use any weapon.

What we need to do now is to combine this method system with our role system. What should we do? It is to declare an IWeaponable variable in the base class of our role and combine it. As shown in:

Note that the Character class still has a UseWeapon () method, but this method is different from the previous UseWeapon () method:

  1. It is not used to override child classes.
  2. We actually call the UseWeapon () method of the IWeaponable interface through Character's UseWeapon () method (actually called the UseWeapon () method of the actual body class ).

Therefore, its code is roughly like this:

Public void UseWeapon (){
WeaponBehavior. UseWeapon ();
}

WeaponBehavior should be assigned a value before use. We will do this in the constructor of the subclass:

Public Barbarian (){
WeaponBehavior = new UseAxe ();
}

At the same time, because various UseWeapon methods are encapsulated in WeaponBehavior, We can dynamically change it. We can add another weapon replacement method to the Character base class:

Public void ChangeWeapon (IWeaponable newWeapon ){
WeaponBehavior = newWeapon;
}

Strategy Mode

In fact, all we have done above has completed the Strategy Mode. Now let's take a look at the official definition of the Stragegy mode:

The Strategy Pattern defines a series of algorithms that encapsulate each of them and enable them to exchange with each other. The Strategy Mode makes the algorithm independent from the client that uses it.

Code implementation and testing

We have completed the design, and now we can implement the whole process through code. For simplicity, we only create two roles and three weapons (including one that cannot use weapons ), the necessary descriptions are included in the comments.

Using System;
Using System. Collections. Generic;
Using System. Text;

Namespace Strategy {

# Region encapsulate UseWeapon () behavior

// Define the weapons Interface
Public interface IWeaponable {
Void UseWeapon ();
}
// Use the sword class
Public class UseSword: IWeaponable {
Public void UseWeapon (){
Console. WriteLine ("Action: Sword Armed. There is a sharp sword in my hands now .");
}
}

// Use an ax class
Public class UseAxe: IWeaponable {
Public void UseWeapon (){
Console. WriteLine ("Action: Axe Armed. There is a heavy axe in my hands now .");
}
}

// Weapon class unavailable
Public class UseNothing: IWeaponable {
Public void UseWeapon (){
Console. WriteLine ("Speak: I can't use any weapon .");
}
}

# Endregion


# Region defines the Angle class

// Role base class
Public abstract class Character {

Protected IWeaponable WeaponBehavior; // call the actual UseWeapon method through this interface.

// Use weapons and call methods through interfaces
Public void UseWeapon (){
WeaponBehavior. UseWeapon ();
}

// Dynamically change weapons for the role
Public void ChangeWeapon (IWeaponable newWeapon ){
Console. WriteLine ("Haha, I 've got a new equip .");
WeaponBehavior = newWeapon;
}

Public void Walk (){
Console. WriteLine ("I'm start to walk ...");
}

Public void Stop (){
Console. WriteLine ("I'm stopped .");
}

Public abstract void DisplayInfo (); // display role information
}

// Define the Barbarian
Public class Barbarian: Character {

Public Barbarian (){
// Initialize the WeaponBehavior variable inherited from the base class
WeaponBehavior = new UseAxe (); // The Barbarian uses an ax.
}

Public override void DisplayInfo (){
Console. WriteLine ("Display: I'm a Barbarian from northeast .");
}
}

// Defines the Paladin
Public class Paladin: Character {

Public Paladin (){
WeaponBehavior = new UseSword ();
}

Public override void DisplayInfo (){
Console. WriteLine ("Display: I'm a paladin ready to sacrifle ice .");
}
}

// Define the mage
Public class Wizard: Character {

Public Wizard (){
WeaponBehavior = new UseNothing ();
}

Public override void DisplayInfo (){
Console. WriteLine ("Display: I'm a Wizard using powerful magic .");
}
}
# Endregion

// Test the program
Class Program {
Static void Main (string [] args ){
Character barbarian = new Barbarian (); // by default, the barbarian uses an ax
Barbarian. DisplayInfo ();
Barbarian. Walk ();
Barbarian. Stop ();
Barbarian. UseWeapon ();

Barbarian. ChangeWeapon (new UseSword (); // You can also use the sword now.
Barbarian. UseWeapon ();

Barbarian. ChangeWeapon (new UseNothing (); // It also makes him useless. Of course, this will not happen :)
Barbarian. UseWeapon ();
}
}
}

It is worth noting that the Strategy Mode does not solve problem 3 we mentioned earlier. Mage should not expose UseWeapon () capabilities (NOTE:If you use the C # language in VS2005, when you press the dot behind the mage, The UseWeapon () method should not be found on the Smart prompt, rather than providing a UseWeapon () method that does nothing () method.

Summary

In this article, we demonstrated the Strategy Mode in the design mode through a skill design that implements a fantasy role-playing game (RPG.

We first implement it in the inheritance mode, and then analyze the problems that may occur due to inheritance. Then we use the interface to implement it again and analyze the problems that may arise when using the interface.

Finally, we have completed the entire design through the Strategy Mode of encapsulation behavior and defined it.

I hope this article will help you!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.