Fantasy RPG (item forging and Decorator Mode)

Source: Internet
Author: User
PDF download: http://www.tracefact.net/document/decorator.pdf

Fantasy RPG (item forging and Decorator mode) Introduction

Item forging is a common feature in various fantasy games. Take the well-known Diablo for example. Assuming that the role has a single-handed sword, the basic attack power may be only 13, but it has three equipment holes. When the sword is embedded with a sapphire, it has an additional frozen effect and adds 2 more attacking powers. When the sword is embedded with a ruby, it has additional flame damage and three more attacks. When a green gem is embedded in the sword, it has additional poisoning damage and four more attacks. Of course, three holes can also be embedded with the same color of the gem. This article describes how to use the Decorator mode to complete such a design.

Extend with inheritance

We should first think of a base class Weapon, which inherits from all kinds of weapons, such as Sword, Axe, and Bow. The Description field indicates the Description of the weapon. For example, "One-Hand light Sword", the Damage () method is used to obtain the Damage of the weapon, and GetDescription is used to obtain the Description of the weapon. We get the following design without considering the GEM:

Now let's consider how to create a weapon with a gem embedded. We first consider that we can use inheritance to implement such a design, but we find that if we need to define all the Sword, we need 3 + 6 + 7 = 16 classes (NOTE:There are three item holes, each of which has three options: blue, red, and green. Two or three items can be in the same color ), if we name the sword with two blue rubs as Blue2RedSword, we name it BlueRedGreenSword for the three-color sword, and the rest. Then, we will get the following huge class system (only partial ):

This is just the beginning. If we need to add another gem, such as white, it can add a curse effect, or we need to add another item hole to the weapon, then the number of our classes will quickly change from a dozen to dozens.

We found the problem of using inheritance:When inheritance is used, a large number of classes are created.In addition, using inheritance also meansWe need to instantiate a specific subclass to obtain the required functions (methods). This is determined in the compilation phase (compile time), and the class client cannot control the run time) change as needed unless you instantiate another subclass.

Use composite to expand

We found that inheritance brings about two major problems. Therefore, we consider using a different method. We can use composite to complete it. To be more detailed, we compose Sapphire (BlueDiamond), Ruby (RedDiamond), and GreenDiamond as entity variables to the base class, then calculate the additional Damage to all the gems in the base class's Damage () method (the base class's Damage () method is no longer abstract ).

Public abstract class Weapon {
Public writable URL int Damage (){
Int total = 0;
If (redDiamond! = Null)
Total + = redDiamond. Damage (); // Add Ruby Damage
If (blueDiamond! = Null)
Total + = blueDiamond. Damage (); // Add sapphire Damage
If (greenDiamond! = Null)
Total + = greenDiamond. Damage (); // Add the Damage to the green gem
Return total;
}
}

In the object subclass, We overwrite this method. In the method, we first call the base class method to obtain the additional damage of the gem, and then add the damage of the weapon to it.

Public class Sword: Weapon {
Public override ind Damage (){
Return base. Damage () + 15; // 15 is the Damage of the sword.
}
}

At this time, the figure should be like this:

Compared with inheritance, composite looks much better. It has a small number of classes, and can decide whether to embed gems for weapons at runtime. However, there are still problems with composite:

  • The gem is closely coupled with the sword. When we want to add a white gem to the Weapon, we need to add a BlueDiamond field to the Weapon base class, you also need to modify the Damage () method of the base class. In short, we need to modify the previous code for each maintenance.
  1. We forget a combination. We should remember that our sword can be embedded with three same-colored gems. For example, if three or three rubs are used, the above design is obviously not complete. Of course, we can abstract a Diamond base class from the three gems, and add three Diamond variables in Weapon. However, the problem persists: if we need to add another equipment hole, we have to modify the Weapon class again.
Add statuses and actions for objects

Now, if we are not a software designer, but a game player, and we want to add a ruby and a sapphire to the sword, what is the actual operation sequence?

  1. Of course, we must first have a sword. (You need to create an Sword object first. It only takes the Sword and does not contain any gems ).
  2. We add a ruby for the sword. (We wrap the Sword object, add 3 points of damage to it, and give it a flame effect ).
  3. We add a sapphire to the sword. (We wrap an Sword object containing a ruby, add 2 points of damage to it, and freeze it .)

Code should be like this:

Weapon sword = new Sword (); // create a sword
Sword = new RedDiamond (sword); // Add a Ruby to the sword
Sword = new BlueDiamond (sword); // Add sapphire to the sword

From the code of adding Ruby to the sword, we found the first thing:The gem should save a reference to the sword.Then we can add behavior or status for sword within the ruby class.

From the code for adding a sapphire to the sword, we found the second thing: the sword with a ruby added (only from the code, it belongs to the Gem) is still a sword, So we come to the following:The gem should be of the same type as the Weapon (Weapon Base Class), otherwise it will not be transferred again here.

This process is illustrated in the following figure:

If we use UML to represent the entire process, the following figure is formed:

We can see that with the extension of the gem, we can provide new capabilities for the sword: Extra damage bonus and extra weapon effects (sorry, I cannot display a gorgeous magic effect, only one sentence can be output on the screen with black and white characters: Addtional Effect: Fire !).

In Damage () and GetDescription (), we first call the corresponding method of the base class, and then add the additional Damage (Status) from the gem to the Damage (): iceDamage, and the extra effect (behavior) from the GEM: FrozenEffect ().

Decorator design mode

The above figure forms the Decorator design pattern of GOF. Now let's take a look at its official definition:Dynamically add additional functions to objects. The Decorator mode provides another Flexible Choice for class extension functions through inheritance.

Code implementation and testing

For simplicity, we only implement one weapon: Sword, two kinds of gems: Sapphire and Ruby.

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

Namespace Decorator {

// Define the Weapon base class
Public abstract class Weapon {

Protected string description; // weapon description (attack effect)

Public virtual string GetDescription (){
Return description;
}

Public abstract int Damage (); // weapon Damage
}

// Defines the sword
Public class Sword: Weapon {
Public Sword (){
Description = "One-Hand light Sword ";
}
Public override int Damage (){
Return 15;
}
}

// Define the gemstone base class
Public abstract class Diamond: Weapon {
Protected Weapon weapon; // Save the reference to the Weapon
}

// Defines the sapphire
Public class BlueDiamond: Diamond {
Private int iceDamage = 2; // additional sapphire damage

Public BlueDiamond (Weapon weapon ){
This. weapon = weapon; // Save the reference
}

Public string IceEffect (){
Return "\ nAddtional Effect: Frozen! ";
}

Public override int Damage (){
Return weapon. Damage () + iceDamage; // attack bonus
}

Public override string GetDescription (){
Return weapon. GetDescription () + IceEffect (); // Add Special Attack Effects
}
}

// Define Ruby
Public class RedDiamond: Diamond {
Private int fireDamage = 3; // additional sapphire damage

Public RedDiamond (Weapon weapon ){
This. weapon = weapon; // Save the reference
}

Public string FireEffect (){
Return "\ nAddtional Effect: Fire! ";
}

Public override int Damage (){
Return weapon. Damage () + fireDamage; // attack bonus
}

Public override string GetDescription (){
Return weapon. GetDescription () + FireEffect (); // Add Special Attack Effects
}
}

Class Program {
Static void Main (string [] args ){
Weapon sword = new Sword (); // create a new sword
// Print its description (attack effect) and damage
Console. WriteLine (sword. GetDescription () + "\ nDamage:" + sword. Damage ());
Console. WriteLine ();

Sword = new BlueDiamond (sword); // Add a sapphire to the sword
Console. WriteLine (sword. GetDescription () + "\ nDamage:" + word. Damage ());
Console. WriteLine ();

Sword = new RedDiamond (sword); // Add a Ruby to the sword
Console. WriteLine (sword. GetDescription () + "\ nDamage:" + sword. Damage ());
Console. WriteLine ();
}
}
}

Output:

One-Hand light Sword
Damage: 15

One-Hand light Sword
Addtional Effect: Frozen!
Damage: 17

One-Hand light Sword
Addtional Effect: Frozen!
Addtional Effect: Fire!
Damage: 20

Summary

In this article, we discuss the implementation process of the Decorator design pattern through a common example of adding gems (additional States and actions) to weapons (objects.

First, we proposed the problem to be solved: adding a gem to a weapon so that it has an additional attack (DAMAGE) bonus and Special attack effects. Then we raised the problem of using inheritance: a large number of classes and actions that can only be obtained by instantiating subclass. Then we used Composition to solve the problem, and encountered a new problem: the program is not easy to maintain. Every time we add a new treasure stone or add a new item hole, we need to modify the code. Finally, we use the Decorator mode to cleverly solve this problem.

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.