Posted: Tue Jan 10, 2006 10:51 pm Post subject: Classes Part III - Inheritance and Polymorphism
Classes Part III:
Inheritance and Polymorphism
At the end of this tutorial, you will find an attached .zip. This file contains pretty much all the code we are using in this tutorial, and it is separated into stages, so that I'm not throwing the final code at you all at once.
An Introduction
We've been doing a lot of class-writing lately, and you may or may not like it. (Be sure to have read Part I and Part II before reading this.) Regardless, no programmer should like to repeat oneself. If you write a Sword class in an RPG you are making, would you want to essentially rewrite that class to make a Crossbow class? Since both are Weapons, they share a lot of the same properties. It makes sense, then, that we should determine what properties they have in common and extract them into a place where both the Sword class and the Crossbow class can access them. Accomplishing this is one of the primary goals of inheritance.
The Fundamentals of Inheritance
The basic concept of inheritance is that one class inherits things from another class. Data and methods on that data (the definition of a class/object) are what is inherited. In the example from the introduction, both the Sword class and Bow class would inherit from the Weapon class.
The class that inherits another class is called the child class, whereas the class that gives its data and methods to the child classes is called the parent class. For example, the Sword and Bow classes are child classes, while the Weapon class is the parent class.
Turing supports single-inheritance, but not multiple-inheritance. That is, a child class in Turing can only have one parent. Note that this does not prevent a parent class from having multiple children classes.
Recall from the first tutorial that import and export come at the start of the class. So too does inherit, and inherit comes before both import and export.
The Appearance of 'Inherit'
It's example time, and we'll build our example from scratch. We will make a Weapon class, then create Sword and Bow classes from it.
Stage I - The Weapon Class
We're not going to make a very complex Weapon class, because that's not the point of this exercise. The Weapon class will have an initialize method, to set all the data, such as damage, range, weapon name, etc. We'll set this data to generic values (this RPG is starting out small: there is only one type of weapon). We'll also give it a display procedure, to show us information on the Weapon.
Here is the Weapon class and a demonstration of its use.
code:
class Weapon
export initialize, display
var name, description : string
var weight, cost, damage, range : int
proc initialize
name := "Weapon"
description := "Grasp weapon in chosen hand and use to smite thine enemy."
weight := 5
cost := 30
damage := 8
range := 0 % This is a melee weapon
end initialize
proc display
put name
put description
put "Weight: ", weight
put "Cost: ", cost
put "Damage: ", damage
put "Range: ", range
end display
end Weapon
var my_sword : pointer to Weapon
new Weapon, my_sword
Weapon (my_sword).initialize
Weapon (my_sword).display
Nothing fancy, here.
Stage II - The Sword and Bow Classes Inherit Weapon
Next, we create the Sword and Bow classes.
code:
class Sword
inherit Weapon
end Sword
class Bow
inherit Weapon
end Bow
You can place this code immediately after the Weapon class, so our code now looks like this:
code:
class Weapon
export initialize, display
var name, description : string
var weight, cost, damage, range : int
proc initialize
name := "Weapon"
description := "Grasp weapon in chosen hand and use to smite thine enemy."
weight := 5
cost := 30
damage := 8
range := 0 % This is a melee weapon
end initialize
proc display
put name
put description
put "Weight: ", weight
put "Cost: ", cost
put "Damage: ", damage
put "Range: ", range
end display
end Weapon
class Sword
inherit Weapon
end Sword
class Bow
inherit Weapon
end Bow
We could also give each class its own file, which we will look into later.
The Sword and Bow classes have inherited the Weapon class. They have gained access to all the data and methods that the Weapon class had. It's almost as if we copied and pasted the code in the Weapon class into the Sword and Bow classes. However, we avoided the atrocity of copy & paste. Let's see if it actually works.
We'll test just the Sword (because the Bow behaves identically, at the moment). Add the following code after the Bow class.
code:
var my_sword : pointer to Sword
new Sword, my_sword
Sword (my_sword).initialize
Sword (my_sword).display
Run it, and it should output the following:
Output wrote:
Weapon
Grasp weapon in chosen hand and use to smite thine enemy.
Weight: 5
Cost: 30
Damage: 8
Range: 0
This does not describe a Sword, but we had no reason to expect it to. We did not nothing to the Sword class, other than having it inherit the Weapon class. Thus, the child class did indeed inherit all the data and methods of the parent class; the Sword class did indeed inherit all the data and methods of the Weapon class. However, merely making copies of classes gets us no where. We have to be able to modify the child classes in some way.
Polymorphism
Polymowhat? Poly means "many". Morph means "transform". Polymorphism means "many transforms"; it refers to the ability to have many forms of the same underlying thing. In this case, the "underlying thing" is a method.
Stage III - Modifying the Sword Class
We want to give the Sword a better description then "Grasp weapon in chosen hand and use to smite thine enemy." We also want to change the other data about it. To accomplish this, we will change the initialize method. But initialize was defined in the Weapon class. If the Sword class has gained all the data and methods of the Weapon class by inheriting it (which it did), then the Sword class already has an initialize method. And we all know we can't define a method twice. So how do we transform the initialize procedure?
The answer comes in the keyword deferred. We are going to defer our procedure, then resolve it in the Sword class. To resolve a method means to give it a new definition in the child classes. We will also resolve the initialize method in the Weapon class, so that the Weapon class still works on its own.
The syntax for deferring a procedure is very similar to the syntax for forwarding a procedure or function (used when a method calls another method, which in turn calls the first method: mutual recursion). The only difference is that instead of using the keyword forward, we use the keyword deferred. We resolve the methods by placing the keyword body before the procedure definition, just as if we were using forward to solve problems of mutual recursion.
code:
class Weapon
export initialize, display
var name, description : string
var weight, cost, damage, range : int
deferred proc initialize
body proc initialize
name := "Weapon"
description := "Grasp weapon in chosen hand and use to smite thine enemy."
weight := 5
cost := 30
damage := 8
range := 0 % This is a melee weapon
end initialize
proc display
put name
put description
put "Weight: ", weight
put "Cost: ", cost
put "Damage: ", damage
put "Range: ", range
end display
end Weapon
class Sword
inherit Weapon
body proc initialize
name := "Sword"
description := "Swords are metallic, double-bladed weapons that vary in length and size. They can be wielded in one or two hands."
weight := 4
cost := 15
damage := 8
range := 0
end initialize
end Sword
class Bow
inherit Weapon
body proc initialize
name := "Bow"
description := "Bows are made of flexible wood with a string tied to either end. They are used to smite enemies from afar, projecting arrows with great velocity and long distances."
weight := 1
cost := 10
damage := 6
range := 9
end initialize
end Bow
var my_sword : pointer to Sword
new Sword, my_sword
Sword (my_sword).initialize
var my_bow : pointer to Bow
new Bow, my_bow
Bow (my_bow).initialize
Sword (my_sword).display
put ""
Bow (my_bow).display
Output wrote:
Sword
Swords are metallic, double-bladed weapons that vary in length and size. They can be wielded in one or two hands.
Weight: 4
Cost: 15
Damage: 8
Range: 0
Bow
Bows are made of flexible wood with a string tied to either end. They are used to smite enemies from afar, projecting arrows with great velocity and long distances.
Weight: 1
Cost: 10
Damage: 6
Range: 9
As you can see, calling the initialize method for the Sword or Bow objects calls the new version of it, which we created in the Sword and Bow classes, rather than the one created in the Weapon class. The initialize method of the Weapon class has been transformed into a new method, more suitable to a Sword or Bow.
Note that exported methods are automatically deferred (and resolved in the same class, of course. Otherwise we wouldn't be able to call them). We could have just as easily written the same code, except removed the deferred line and removed the body in front of the initialize declaration. I encourage you to defer the procedure, anyways, since it makes the general outline of the class/objects clearer, and since you may decide later that you don't want to export that particular method.
Problem I - Parameters of Deferred Methods
We cannot change the parameters given to the method we are transforming. Consider the initialize method of the Weapon class. What if we wanted more flexibility? What if we wanted to be able to set the value of name, description and so on by passing in values as parameters? Easy enough:
code:
class Weapon
export initialize, display
var name, description : string
var weight, cost, damage, range : int
proc display
put name
put description
put "Weight: ", weight
put "Cost: ", cost
put "Damage: ", damage
put "Range: ", range
end display
end Weapon
var my_weapon : pointer to Weapon
new Weapon, my_weapon
Weapon (my_weapon).initialize ("Cervantes' Scimitar", "This scimitar is a fearsome weapon, indeed.", 3, 50, 6, 0)
Weapon (my_weapon).display
Now, let's create a Sword class that inherits the Weapon class, and try to redefine the initialize method. All swords are melee weapons, so we won't allow the range to be altered.
code:
class Weapon
export initialize, display
var name, description : string
var weight, cost, damage, range : int
deferred proc initialize (name_, description_ : string, weight_, cost_, damage_, range_ : int)
body proc initialize % The parameters used are the ones defined in the 'deferred' line. We could rewrite them here, but we'd have to make sure they are exactly correct
name := name_
description := description_
weight := weight_
cost := cost_
damage := damage_
range := range_
end initialize
proc display
put name
put description
put "Weight: ", weight
put "Cost: ", cost
put "Damage: ", damage
put "Range: ", range
end display
end Weapon
class Sword
inherit Weapon
body proc initialize (name_, description_ : string, weight_, cost_, damage_ : int)
name := name_
description := description_
weight := weight_
cost := cost_
damage := damage_
range := 0
end initialize
end Sword
var my_sword : pointer to Sword
new Sword, my_sword
Sword (my_sword).initialize ("Cervantes' Scimitar", "This scimitar is a fearsome weapon, indeed.", 3, 50, 6)
Sword (my_sword).display
You'll notice this code produces one error and one warning. The warning is that the initialize method of the Sword class has the incorrect number of parameters. It has to have the exact same parameters as were defined in the deferred line back in the Weapon class. The error is produced when we call the initialize method: we don't have the correct number of parameters, it tell's us.
The only way to get around this is to accept it. You take the same parameters; you just ignore the ones you don't like. In calling the method, you pass whatever you like into those parameters (though they still must be of the correct type, of course).
Problem II - Completely Redefining Methods with no Assistance
We must fully redefine the method, without the ability to call the version of the method that the parent class has. Say we wanted to change the display method of the sword; instead of outputting 0 for the range, we want it to output "Melee". We have to re-code it entirely, like this:
code:
class Weapon
export initialize, display
var name, description : string
var weight, cost, damage, range : int
deferred proc initialize
body proc initialize
name := "Weapon"
description := "Grasp weapon in chosen hand and use to smite thine enemy."
weight := 5
cost := 30
damage := 8
range := 0 % This is a melee weapon
end initialize
deferred proc display
body proc display
put name
put description
put "Weight: ", weight
put "Cost: ", cost
put "Damage: ", damage
put "Range: ", range
end display
end Weapon
class Sword
inherit Weapon
body proc initialize
name := "Sword"
description := "Swords are metallic, double-bladed weapons that vary in length and size. They can be wielded in one or two hands."
weight := 4
cost := 15
damage := 8
range := 0
end initialize
body proc display
put name
put description
put "Weight: ", weight
put "Cost: ", cost
put "Damage: ", damage
put "Range: Melee"
end display
end Sword
var my_sword : pointer to Sword
new Sword, my_sword
Sword (my_sword).initialize
Sword (my_sword).display
If this were Ruby, for example, I would use super to call the display method of the parent class (Weapon), which has already done most of the work for me. But to really do this in Ruby, I would have to have the method return an array of data, pop off the last element (the "range" element), and replace it with my own version, "Range: Melee".
An Example of Problems I and II
Let's look at another example--one that involves the initialize method, and will combine the first and second problems. In discussing Problem I, we wanted to change the initialize method of the Sword class so that range was zero, no matter what. The name, description, weight, cost, and damage are still going to be passed in as parameters, however. Thus, the first five lines of the initialize method are the same in both the Weapon class and the Sword class. They both look like this:
What's the point in repeating this code in both methods? It would be easier to code it once in the Weapon class. If these two problems discussed above didn't exist in Turing, the initialize method of the Sword class would take 5 parameters, and it would call the initialize method of the Weapon class, passing it (name_, description_, weight_, cost_, damage_, 0), and that would be that. Zero is passed as the last parameter to the Weapon's initialize method; range is set to 0; there is no repeat of code. But alas, Turing has no equivalent of Ruby's "super".
Classes Part III.zip
Description:
Contains essentially all the code used in the tutorial, and more. Code is separated into stages.
Posted: Tue Jan 10, 2006 10:54 pm Post subject: (No subject)
Expansion to Multiple Files
Earlier I mentioned that we could give the Sword and Bow class their own files. The advantage to doing this is merely that it keeps our code tidy, and it prevents us from having to search for the end of a class and the beginning of another.
To do this, we have to make each class into a Turing Unit (see step 5 of the modules tutorial). Just put the keyword unit at the start of the class, and ensure you only have one class per file. Save each file as <className>.tu. For example, I would save the Weapon unit as Weapon.tu.
Stage IV - Expanding to Multiple Files
You should have three .tu files, all in the same folder. You don't have to do anything special to link the files; the inherit Weapon line will do it all for you. (If you haven't looked at the .zip file at the end of this post, this would be the ideal time to do so.)
Next, you need a file to run. This will contain the code that creates the objects, initializes them, and displays them. Create a "Run.t" file (in the same folder as your Turing Units) and give it this code:
code:
import Sword, Bow
var my_sword : pointer to Sword
new Sword, my_sword
Sword (my_sword).initialize
var my_bow : pointer to Bow
new Bow, my_bow
Bow (my_bow).initialize
Sword (my_sword).display
put ""
Bow (my_bow).display
This is the code we were using previously, except that we had to import the Sword and Bow classes. Note that we did not import Weapon, since we don't directly create any Weapon objects in this code. Also, note that just importing Weapon will not give you access to Sword or Bow.
Problem III - Importing Many Units
We must import each unit manually. If expanded our Weapon system to include class definitions for 50 types of Weapons, we would need to import 50 units into our "Run.t" program. There is no way to make a package of classes.
Object Relationships: "is a"
The inherit clause does more than give the child class access to the data and methods of the parent class. Inheritance creates a relationship between child and parent: between Sword and Weapon, and between Bow and Weapon. It creates a parent-child relationship. It creates an "is a" relationship. Sword "is a" Weapon. Bow "is a" Weapon. Let's withdraw for a moment, and say this in even more English style terms: Both Swords and Bows are types of Weapons. Wherever a Weapon can go, a Sword or a Bow can go.
Stage V - A Player Character Equips a Weapon
Consider the RPG we are building this for. You want to be able to equip a Weapon--to put a Weapon in your hand to beat the Monsters with. You may wish to wield a Sword, engaging in melee combat; or you may wish to wield a Bow, picking the Goblins off from afar. You should be able to equip either a Sword or a Bow, but we don't want to have to code that into the equip_weapon procedure (this procedure may be found in the PlayerCharacter class, which could inherit the Character class, which could inherit the Object class, perhaps). Instead, we use the "is a" relationship. Since a Sword "is a" weapon, a Sword can go anywhere a Weapon can. Thus, in attempting to equip a Weapon to bash Monsters with, we expect to receive a Weapon object. A Sword object is a Weapon object too, so we can easily give the equip_weapon procedure a Sword object, though it expects a Weapon object. Let's code this.
We'll make a PlayerCharacter class, and it will have initialize, equip_weapon, and display procedures. The initialize and display procedures will be quite simple. The equip_weapon procedure will take a parameter which is a Weapon object, and store it in the variable, weapon. weapon is a pointer to the Weapon class (make sure you had a good understanding of Classes: Part II for this part).
We will initially set the value of the weapon variable to nil: it will initially be a nil pointer, not pointing to any particular class, not referencing any object. The variable type, however, is still pointer to Weapon.
As a final note, since we are working with Weapon objects, we have to import the Weapon class into the PlayerCharacter class. Since we are using units, this is as simple as putting import Weapon at the start of the PlayerCharacter class, and making certain that both Turing Units are in the same directory.
Here is the PlayerCharacter unit:
code:
unit
class PlayerCharacter
import Weapon
export initialize, equip_weapon, display
var name, race, klass : string
var weapon : pointer to Weapon
proc initialize (name_, race_, klass_ : string)
name := name_
race := race_
klass := klass_
weapon := nil
end initialize
proc equip_weapon (weapon_ : pointer to Weapon)
weapon := weapon_
end equip_weapon
proc display
put "Name: ", name
put "Race: ", race
put "Class: ", klass
put "Weapon: " ..
if weapon = nil then
put "None"
else
put Weapon (weapon).name
end if
end display
end PlayerCharacter
We had to make a slight modification to the Weapon class for this to work: we had to export the name variable. The export line of the Weapon class now looks like this:
code:
export initialize, display, name
And finally, the code to create a PlayerCharacter object and equip him with a Weapon. We will create Minsc, the human ranger, and he will wield a Sword.
code:
import Weapon, Sword, Bow, PlayerCharacter
var minsc : pointer to PlayerCharacter
new PlayerCharacter, minsc
Now, let's examine this a bit. minsc_sword is a Sword object. A Sword "is a" Weapon. The equip_weapon procedure of the PlayerCharacter class asks for a Weapon object as its parameter. But we gave equip_weapon a Sword object. Since a Sword "is a" Weapon, a Sword can go anywhere a Weapon can. A Sword can be passed to equip_weapon because of this relationship.
Problem IV - Anonymous Objects
In order to create an object, we must go through the process of making a pointer to the class, then creating a new instance of the class, referenced to the pointer. There is no other way. Thus, to give Minsc a Weapon, we had to create a Sword object, then pass that Sword object to the equip_weapon procedure. But what do I need minsc_sword in my main program for? I don't: The data of minsc_sword's existence is stored within the minsc object.
Things would be easier if I could create anonymous objects: objects without a name. If this were Ruby, the code would look roughly like this:
Sword.new creates an anonymous Sword object, an object without a name (I didn't do minsc_sword = Sword.new). This saves us from creating a Sword object in the global namespace and then passing it to equip_weapon.
Creating an Inventory - More of "is a"
In Part I of this tutorial, we discussed the following scenario: creating a pointer to ClassName, then creating a new instance of ClassName, linked to pointer_name. In code, it looks like this:
code:
var my_sword : pointer to Sword
new Sword, my_sword
Does it seem repetitive? It does, but it gives us flexibility: The class in the first line does not have to match the class in the second line. We'll see why soon.
Stage VI - Creating an Inventory
An inventory is nothing more than an array of objects that the PlayerCharacter object is holding on to. For our purposes, let's assume Weapons are the only type of Items in our game. (We could expand our Item system somewhat to include an Item class; Weapon, Armour, Scroll, Book, Boots, etc. could inherit Item. However, we don't need to do that to illustrate the current subject.)
The inventory is an array of Weapon objects; it is an array of pointer to Weapon.
code:
var inventory : array 1 .. 10 of pointer to Weapon
The inventory should be initialized each element being nil: pointing to nothing; referencing no object.
We will give PlayerCharacter add_to_inventory and remove_from_inventory methods, and we'll make display show the contents of the inventory as well. Here's the code:
code:
unit
class PlayerCharacter
import Weapon
export initialize, equip_weapon, add_to_inventory, remove_from_inventory, display
var name, race, klass : string
var weapon : pointer to Weapon
% For the moment, assume Weapons are the only type of Items in the game
var inventory : array 1 .. 10 of pointer to Weapon
proc initialize (name_, race_, klass_ : string)
name := name_
race := race_
klass := klass_
weapon := nil
for i : 1 .. upper (inventory)
inventory (i) := nil
end for
end initialize
proc equip_weapon (weapon_ : pointer to Weapon)
weapon := weapon_
end equip_weapon
proc add_to_inventory (weapon_ : pointer to Weapon)
% Iterate through each inventory slot, looking for an empty one
for i : 1 .. upper (inventory)
if inventory (i) = nil then
inventory (i) := weapon_
exit
end if
end for
end add_to_inventory
proc remove_from_inventory (i : int)
inventory (i) := nil
end remove_from_inventory
proc display_inventory
for i : 1 .. upper (inventory)
put i, ": " ..
if inventory (i) = nil then
put "Empty"
else
put Weapon (inventory (i)).name
end if
end for
end display_inventory
proc display
put "Name: ", name
put "Race: ", race
put "Class: ", klass
put "Weapon: " ..
if weapon = nil then
put "None"
else
put Weapon (weapon).name
end if
put "Inventory:"
display_inventory
end display
end PlayerCharacter
The "Run.t" code is the following:
code:
import Weapon, Sword, Bow, PlayerCharacter
var minsc : pointer to PlayerCharacter
new PlayerCharacter, minsc
var minsc_sword : pointer to Weapon
new Sword, minsc_sword
Sword (minsc_sword).initialize
PlayerCharacter (minsc).equip_weapon (minsc_sword)
var minsc_bow : pointer to Weapon
new Bow, minsc_bow
Bow (minsc_bow).initialize
PlayerCharacter (minsc).add_to_inventory (minsc_bow)
PlayerCharacter (minsc).display
There is an important trick here. When creating a Weapon object such as minsc_sword, the pointer points to Weapon, but a new instance of Sword was created, linked to the minsc_sword pointer.
To understand this, we have to use the "is a" relationship. Sword "is a" Weapon, so a Sword can go anywhere a Weapon can. This extends even to creating a Sword object linked to a pointer that points to the Weapon class.
Why did we have to do this? The answer is that the inventory holds an array of Weapon objects--an array of pointers to Weapon. The data stored in the array must be of the data type, pointer to Weapon. But, we don't want to create Weapon objects all over the place; we want to use our Sword and Bow classes, because they make our life easier. Thus, we create instances of the Sword and Bow classes, with linked to pointers that point to the Weapon class.
Stage VII - Expansion of the Item System
There are no new concepts in Stage VII, so this section will be short. Essentially, Stage VII expanded the Item system so that a new Item class was the top-dog. Weapon inherits Item, Sword and Bow inherit Weapon. Other types of Items could be added, such as Armour, Boots, Helmets, etc.
You can view the code for Stage VII in the .zip file, attached to the end of this post.
Questions (but no Answers)
To compliment this tutorial, it is recommended that you write a program that involves inheritance and polymorphism. I suggest expanding the example we have built in this tutorial, though I recommend staying away from graphics. If you truly want to make an RPG, make it a text RPG. Begin by planning out all the objects you'll need (Item -> Weapon/Armour, Character -> PlayerCharacter/NonPlayerCharacter, Map -> Terrain, Object -> Door/Chest). Determine object relationships. Plan a Class Hierarchy.
A Conclusion
Inheritance and polymorphism are powerful programming concepts. They are about reusing code that is common to different objects; it is about creating a logical, object-oriented-sensical structure; it is about creating relationships between objects, including the "is a" relationship.
By now, we've had a good look at inheritance and polymorphism. You can see how Turing lacks functionality in this area. This is because Turing is not a strong object-oriented nature; rather, object-orientedness was thrown in as an afterthought, and it was left in a haggard state. There are so many basic aspects of inheritance and polymorphism that Turing gets wrong that it gives a strong push to learn a more object-oriented programming language. It is an interesting thought that the last important part of Turing to learn is also the one to push the programmer away from the Turing language the most. Despite this, inheritance and polymorphism are still powerful programming concepts, even in Turing. Learn to love them!
TokenHerbz
Posted: Tue Apr 04, 2006 5:55 pm Post subject: (No subject)
OMG, Cervantes, your crazy to have so much knowlend in CLASS's
i read your 3 class tuts a few times, and still don't know how to use them well :S
MysticVegeta
Posted: Tue Apr 04, 2006 5:58 pm Post subject: (No subject)
Read part 1 a few times, then you will undersatnd whats going on in Part 2 and 3
TokenHerbz
Posted: Wed Apr 05, 2006 2:34 pm Post subject: (No subject)
Mistic i did, as i said:
I read your 3 tuts (class1, class2, class3), a few times (each)...
Sorry to confuse you :S
MysticVegeta
Posted: Wed Apr 05, 2006 5:18 pm Post subject: (No subject)
Yes I know thats why I said "Read Part 1 a few times", "then", "read 2 and 3" So read Part 1 a few times before even you glance at part 2, cause I remember reading them a long time ago so I had to read part 2 a few times cause I already knew the content of part 1.
Cervantes
Posted: Wed Apr 05, 2006 7:18 pm Post subject: (No subject)
I wouldn't recommend reading the part I tutorial (read: any tutorial) a few times. By which I mean the following: Read it once, well. Go through it slowly enough that you understand everything. Just reading it, even if you read it ten times, will not help you understand it if you're reading too fast and thinking not enough.
This tutorial explains it all, but there is no substitute to practice. Really, make something with classes. Something not too hard, like a Button class. Once you can make a single class and make objects from it, move on to the next part of the tutorial.
Make something for part II. An easy thing to make for part II is some class that has a method that accepts as a parameter another object of the same class. For example, a Vector class. It defines a function, add, that takes another Vector object as a parameter, adds the two vectors, then returns a new Vector object that is the resultant of the two given vectors (one vector was passed in as a parameter; one you called the add function on).
md
Posted: Wed Apr 05, 2006 7:25 pm Post subject: (No subject)
Less pointless debate about how inept each of you is, and more praise for Cervantes! Congrats on putting together such a wonderful tutorial on a subject so rarely used (and poorly implemented) in turing.
Sponsor Sponsor
Cervantes
Posted: Wed Apr 05, 2006 7:45 pm Post subject: (No subject)
Thanks!
ericfourfour
Posted: Sat Dec 09, 2006 11:29 pm Post subject: (No subject)
Sorry to necro-post but I found a very useful concept using classes in Turing by using the implement and implement by command.
I'm sure you've known about:
c++:
class Foo;
class Bar
{
Foo* foo;
};
class Foo
{
Bar* bar;
};
in c++. I don't know what it is called but I know it is very useful. Did you know however you could do that in Turing?
In Turing you can set it up like this:
code:
class Foo
implement by FooBody
end Foo
class Bar
import Foo
var foo : ^Foo
end Bar
class FooBody
implement Foo
import Bar
var bar : ^Bar
end FooBody
There is more and I can expand on it if you want. The only thing you have to watch out for is that Foo can inherit but FooBody cannot. They also must be in different Turing Unit files.
wtd
Posted: Sat Dec 09, 2006 11:45 pm Post subject: (No subject)
ericfourfour wrote:
Sorry to necro-post but I found a very useful concept using classes in Turing by using the implement and implement by command.
I'm sure you've known about:
c++:
class Foo;
class Bar
{
Foo* foo;
};
class Foo
{
Bar* bar;
};
This is simply forward declaration of a class.
CodeMonkey2000
Posted: Wed Dec 13, 2006 5:36 pm Post subject: (No subject)
theres something wrong, with my long bow class, if i want to specify that the type of arrow you use affects the damage and rang, how would i use polymorphisism for it?
code:
class Weapon
export initialize, display
var name, description : string
var weight, cost, damage, range : int
deferred proc initialize
body proc initialize
name := "Weapon"
description := "Grasp weapon in chosen hand and use to smite thine enemy."
weight := 5
cost := 30
damage := 8
range := 0 % This is a melee weapon
end initialize
proc display
put name
put description
put "Weight: ", weight
put "Cost: ", cost
put "Damage: ", damage
put "Range: ", range
end display
end Weapon
class Sword
inherit Weapon
body proc initialize
name := "Sword"
description := "Swords are metallic, double-bladed weapons that vary in length and size. They can be wielded in one or two hands."
weight := 4
cost := 15
damage := 8
range := 0
end initialize
end Sword
class Bow
inherit Weapon
body proc initialize
name := "Bow"
description := "Bows are made of flexible wood with a string tied to either end. They are used to smite enemies from afar, projecting arrows with great velocity and long distances."
weight := 1
cost := 10
damage := 6
range := 9
end initialize
end Bow
class LongArrow
inherit Weapon
body proc initialize
name := "Long Arrow"
description := "Increses the power of your longbow."
weight := 1
cost := 1
damage := 2
range := 0
end initialize
end LongArrow
class LongBow
inherit Bow, LongArrow
body proc initialize
name := "Long Bow"
description := "The long bow is a better verson of your old bow."
weight := 1
cost := 15
damage := 10
range := 15
end initialize
end LongBow
var my_sword : pointer to Sword
new Sword, my_sword
Sword (my_sword).initialize
var my_bow : pointer to Bow
new Bow, my_bow
Bow (my_bow).initialize
Sword (my_sword).display
put ""
Bow (my_bow).display
var my_long_bow : pointer to LongBow
new LongBow, my_long_bow
LongBow (my_long_bow).initialize
put ""
Bow (my_long_bow).display
Clayton
Posted: Wed Dec 13, 2006 8:24 pm Post subject: (No subject)
Try posting in [Turing Help] next time
But anyways... You would have to have some sort of pointer in your longbow class that keeps track of the type of ammunition you use in your bow. Then, have some sort of function in the parent class of your bow ammunition have a way to calculate the damage (bonus) that it would have to your bows power.
Cervantes
Posted: Thu Dec 14, 2006 12:55 am Post subject: (No subject)
spearmonkey2000 wrote:
code:
class LongBow
inherit Bow, LongArrow
...
Turing only supports single inheritance, not multi inheritance. So your LongBow can't inherit both from Bow and LongArrow. But then again, why should it inherit from LongArrow? Look at the "is a" relationship. Is a LongBow a LongArrow? Certainly not.
I'd recommend making an Ammunition class. From that you can make classes for Arrows, Bolts, ThrowingDaggers, what have you... A Bow object can then have a method called fire that takes in an Arrow object, possibly among other things. You could then create child classes from Arrow, such as FireArrow or PiercingArrow.
CodeMonkey2000
Posted: Thu Dec 14, 2006 6:47 pm Post subject: (No subject)
so u cant morph 2 parents classes? then wat do u do when u are in that senerio? are you forced to copy and paste? or is there another way around this?