Computer Science Canada Inheritence and Polymorphism |
Author: | Cervantes [ Sat Aug 20, 2005 9:44 pm ] | ||||
Post subject: | Inheritence and Polymorphism | ||||
Has anyone experimented with Inheritence and Polymorphism in Turing? I've looked at the help file for deferred, but it seems that that will only allow me to redefine (with the same set of parameters) the method in the child class without defining it in the parent class at all. I'd like to do something like this:
I want to be able to change the parameters for the child's method and change the code within it. Is this possible? So far, I've only come up with the roundabout method of simply renaming the Parent's method to Parent_Method_Name. Or have I missed the point of polymorphism? It seems sort of silly though that if I defer a procedure I force myself to rewrite it in all the child classes, and I force myself to make those methods have the exact same parameters as the parent's method. |
Author: | Hikaru79 [ Sun Aug 21, 2005 12:11 pm ] |
Post subject: | |
I don't have much experience with Turing's specific implementation of object orientation, but I do have other background in that area, so I'll try to answer your questions on a general level. A procedure's definition does not stop at its name. A procedure's definition includes the name AND the parameters. So if you have two procedures named Init, but one takes a string and one doesn't, they are two seperate procedures and there is nothing you can do about that. It's the way it is meant to be. You cannot overload a method in the subclass if it takes different parameters than its superclass. When you define a procedure in a subclass that already existed in the parent class, you overwrite it, so to speak. Or am I misunderstanding your question? ![]() |
Author: | Cervantes [ Sun Aug 21, 2005 1:39 pm ] |
Post subject: | |
Thanks Hikaru. While Turing doesn't do overloading, what you said affirms that I shouldn't be able to do that, not that I'm doing it the wrong way. I've decided to go with the roundabout method I mentioned earlier, which I suppose isn't really a roundabout method because it does something quite distinct. I've also found a use for polymorphism in my little program: the Animation procedure. I'm sure I'll find more as I keep working. If anyone's interested, I'm dabbling at making an RTS. I don't actually expect to complete it, or even get a demo of it done; rather, I'm just seeing how far I can get. ![]() Thanks again, Hikaru. |
Author: | Cervantes [ Sun Aug 21, 2005 8:39 pm ] |
Post subject: | |
I'm fairly sure Turing can't do this, but I'm curious if other languages can: is it possible to use polymorphism to add fields to a record in the child classes? |
Author: | Cervantes [ Mon Aug 22, 2005 9:16 am ] | ||
Post subject: | |||
Oh noes! I'm having grave trouble with the RTS. Specifically, I've come to the point where I have to make multiple units of varying types work together. I need to have a flexbile array of units, but those units could be Footmen or Knights (or others, when I make more). Here's the layout I was hoping to have: I would then create a flexible array of pointers to the ANY_OBJECT class, and manage everything by calling the Handle procedure (which is deferred from GENERIC_OBJECT to GENERIC_UNIT and GENERIC_STRUCTURE). The trouble is, Turing will not allow me to have a class inherit (or impliment, for that matter) multiple files, no matter what. I suppose that only makes sense, as I can't even imagine how things would work otherwise (I'm just hoping they would work my way ![]() I would love to find some way to make the inheritence and polymorphism approach work. Perhaps I could deferr most methods from the GENERIC_OBJECT class (methods such as Attack, Move, Rotate, Guard, and Build_Unit) to the GENERIC_UNIT (henceforth known as G_Unit) and GENERIC_STRUCTURE classes. G_Unit and GENERIC_STRUCTURE would resolve the methods they need, and leave the other ones as empty. For example, GENERIC_STRUCTURE would resolve the Build_Unit method, but would resolve methods such as Move and Attack as empty procedures. The flowchart for this approach would look like the above flowchart, but stopping at the third row. [EDIT] Whoops. That wouldn't work. The objects I create with the GENERIC_OBJECT class would not know whether they are supposed to be footmen, knights, or barrackses. The only way I can think of getting around this would be to pass (as a parameter) a string in to the Init procedure of the GENERIC_OBJECT that specifies the type of object (footman, knight, etc.). The Footman and Knight etc. classes would resolve the Init procedure, but all the code for this resolved Init procedure would be wrapped in an if statement: if object_type = "footman" for the footman class. This might work, provided that Turing will call all versions of the deferred procedures (call the Init procedure resolved by the footman and knight etc. classes). I guess there's only one way to find out... [EDIT^2] Nope, that's not how Turing works.
Error on the last line: "Deferred subprogram has not been resolved". Alas, I want it to call all the versions of the resolved procedure (from all of the child classes). Failing that, my next approach would be to read everything in from files. I would then have several modules to handle things such as building units and spellcasting. Haven't really thought much on this approach yet. 4/5 posts in this topic. I'm making my own blog! ![]() ![]() |
Author: | wtd [ Mon Aug 22, 2005 4:53 pm ] |
Post subject: | |
Use a programming language that pays more than lip service to object orientation. ![]() |
Author: | Cervantes [ Mon Aug 22, 2005 5:05 pm ] |
Post subject: | |
I'm looking for general ideas. Even if I can't do it, I want to know how! Are you saying that there are languages that, when a call is made to a deferred procedure in the parent class, will run the resolutions of that procedure in all the child classes? Better yet, I need multiple inheritence (assuming the flowchart representation would work if I could do multiple inheritence). Does Ruby or Java do this? |
Author: | wtd [ Mon Aug 22, 2005 5:17 pm ] | ||
Post subject: | |||
Ruby and Java only support multiple inheritance via mix-ins and interface inheritance, respectively. If you're looking for a lightweight language that does support MI, you may wish to look at O'Caml or Python. But it seems like you're just looking for a way to be able to pass Footman or Knight (for instance) to the same function/procedure. Typically, this makes use of the "is a" relationship. A Kight "is a" generic unit, therefore, anywhere you specify a generic unit, you can pass a Knight. Here's a snippet from the O'Caml toplevel, for instance:
|
Author: | Cervantes [ Mon Aug 22, 2005 5:41 pm ] |
Post subject: | |
Excellent, wtd. What you said about the "is a" relationship gave me the idea of creating a new object of the KNIGHT class using a pointer to GENERIC_OBJECT. It works. ![]() That O'Caml code: is that sort of like Ruby's irb? |
Author: | wtd [ Mon Aug 22, 2005 5:45 pm ] |
Post subject: | |
Cervantes wrote: That O'Caml code: is that sort of like Ruby's irb?
Pretty much. |
Author: | wtd [ Mon Aug 22, 2005 8:24 pm ] | ||
Post subject: | |||
Of course, you should realize that with the likes of Ruby or Python, you have duck typing, and don't necessarily need a strict inheritance hierarchy.
|
Author: | Tom West [ Mon Aug 22, 2005 9:31 pm ] |
Post subject: | |
I'm not entire certain why you would want multiple inheritance in this particular example. A standard class hierarchy would do fine. If you have a lot of cross references between classes, you will need to split the declaration and the implementation (see "implement by" in the on-line help), but you may well be able to get around that at all. (Besides, personal opinion here, multiple inheritance causes far more problems than it solves. There are solid reasons why it's been left out of almost all modern OO languages.) Can you elaborate why you need the bottom part of the hierarchy? Here's a little bit of code that uses your example to produce a really simply game using your hierarchy. The main concepts used are casting an GenericObject to a subclass so that you can use that subclass' exported procedures and functions. Anyway, let me know if this example needs to be elaborated upon. |
Author: | wtd [ Mon Aug 22, 2005 10:42 pm ] |
Post subject: | |
Tom West wrote: There are solid reasons why it's been left out of almost all modern OO languages.
Not true at all. ![]() Python, O'Caml, and Eiffel among others feature multiple inheritance. |
Author: | rizzix [ Tue Aug 23, 2005 12:56 am ] |
Post subject: | |
*cough*most*cough*ehm yea.. bad throat ![]() ![]() |
Author: | wtd [ Tue Aug 23, 2005 3:04 am ] |
Post subject: | |
This also supposes that there's anything even remotely modern about languages like Java and C#. ![]() New? Yes. Modern? That's highly debatable. |
Author: | Cervantes [ Tue Aug 23, 2005 8:01 am ] | ||
Post subject: | |||
Thanks for the reply, Mr. West. ![]() Tom West wrote: Can you elaborate why you need the bottom part of the hierarchy? I need to create an array of pointers to some particular class. I thought, since inheritence works down, I'd have to create an array of pointers to the bottom class. That was before I discovered that I could create a pointer to the top class and create new instances, using that pointer, of lower level classes. Thanks very much for the example. I think I will try to limit the importing of classes into other classes as much as I can, by using the GenericObject to do most things. For example, I could move the fire related methods into the GenericObject class, defer them, and resolve them in the GenericStructure class with appropriate code, though resolve them in the GenericUnit class with nothing:
I suppose this importing one class into another is what you referred to at the beginning as "cross references". I'm not sure how implementation could help me here, assuming I did have a lot of cross references. Would you explain this in some more depth? wtd: The Ruby code you posted lacks the Generic classes. I understand what you're illustrating, but I need the Generic classes if I wish to avoid re-writting lots of code lots of times. On the other hand, Ruby allows each array element to contain a different type of data, so I could add a Footman object or a Knight object to the array whenever I want. Thanks very much for all the responses. ![]() |
Author: | Cervantes [ Tue Aug 23, 2005 1:38 pm ] |
Post subject: | |
I've encountered another problem. What about buildings producing units? In the 'classexample' provided by Tom West, it was assumed that the Town Hall would produce Knights and only Knights; the Barracks would produce Footmen and only Footmen. There are two problems here. First, I don't want to have to hard-code what units a specific building can produce and which it can't: I'd rather have an array to store the possible units the building can produce. What would be the variable type of that array? I've already said the second problem: Buildings should be allowed to produce more than one unit. For example, the Barracks might be able to produce Footmen as well as Spearmen and Archers. I can't think of any way to do it, short of passing in an integer or string and using a case construct with a label for each type of unit the building can produce. And I really hate to have to do that. Thanks again! ![]() |
Author: | wtd [ Tue Aug 23, 2005 2:11 pm ] | ||
Post subject: | |||
Cervantes wrote: On the other hand, Ruby allows each array element to contain a different type of data, so I could add a Footman object or a Knight object to the array whenever I want.
Yes. This is an example of duck-typing. "If it walks like a duck, and it quacks like a duck..." Basically, if you add in something that isn't strictly speaking a Footman or a Knight, but works anyway, then there's no problem. ![]() This is also part of a larger discussion about structural typing vs. nominal typing. And you could create an array which can only hold a certain type of data easily enough.
|
Author: | Tom West [ Tue Aug 23, 2005 8:36 pm ] |
Post subject: | Multiple Inheritance |
Okay, I was being a little over-the-top about MI being evil, but I know a number of coding houses that program in C++ that specifically prohobit their programmers using MI and operator overloading. Both features can produce *really* elegant solutions to a very small set of problems and tend to produce unmaintainable solutions for a much larger class of problems ![]() |
Author: | wtd [ Tue Aug 23, 2005 8:44 pm ] |
Post subject: | Re: Multiple Inheritance |
Tom West wrote: Okay, I was being a little over-the-top about MI being evil, but I know a number of coding houses that program in C++ that specifically prohobit their programmers using MI and operator overloading.
Both features can produce *really* elegant solutions to a very small set of problems and tend to produce unmaintainable solutions for a much larger class of problems ![]() Operator overloading is vital for programming in an idiomatic way in C++. Understanding multiple inheritance is certainly also important. Prohibiting use of technologies like these does no good in the long run. It just advances ignorance about things that can't be avoided forever. |
Author: | Tom West [ Tue Aug 23, 2005 8:57 pm ] | ||||||||||
Post subject: | body and implement by | ||||||||||
One place that Turing can get a bit messy is the declare before use. If you have two classes, A and B that need to import each other, you need to split the declaration and the implementation. The declaration don't import each other, but the bodies can because the interface for each has already been declared. Here's an example. Note how A does import B (in the body) and B imports A (in its body). Note that you're still out of luck if you need to pass B as a parameter to a subprogram in A and A as a parameter to a subprogram in B. --- A.tu ---
--- Abody.tu ---
--- B.tu ---
--- Bbody.tu ---
--- test.t ---
|
Author: | Tom West [ Tue Aug 23, 2005 9:00 pm ] |
Post subject: | |
Cervantes wrote: I can't think of any way to do it, short of passing in an integer or string and using a case construct with a label for each type of unit the building can produce. And I really hate to have to do that. I know it feels kind of ugly, but to be honest, it's probably the least painful way to do it. (You notice I do the same thing in my example program.) By the way, it's usually best not to provide an "empty" implementation of a method that you are planning to implement in a descendent class. That way, if you forget to implement the method in one particular class, you'll get an error which will point out the problem right away, rather than a possibly hard-to-find bug in a program that operates, but incorrectly. |
Author: | wtd [ Tue Aug 23, 2005 9:05 pm ] |
Post subject: | |
How about, instead of a character, using an enum to represent possible types of units? |
Author: | md [ Tue Aug 23, 2005 11:01 pm ] |
Post subject: | |
Multiple inheritance is especially useful in some of the code I'm writing right now; for instance, everything is an object, but there are objects which have physics properties, and objects which have model properties, and objects which have both. The properties and functions for models and physics don't change when you combine them so those objects which require both a model and physics properties can simply inherit both the physics class and the model class, both of which inherit the object class. It makes for an interesting, yet very easily expanded class tree... Adn wtd... perhaps he meant that most *new* lanugages didn't support it; C++ is by no means a new language although it may be a modern one. |
Author: | Cervantes [ Thu Aug 25, 2005 2:24 pm ] |
Post subject: | |
wtd wrote: How about, instead of a character, using an enum to represent possible types of units?
Sounds good. I've just made an enum for the possible actions of a unit (Nothing, Move, Attack, Guard, Follow), though I had to make it global to the whole program. I suppose I could have made an implimentation of the GENERIC_OBJECT class, though I ended up integrating it into Turing itself. I'm going to leave the unit producing problem for a little while, in any case, until I get the units working better. |
Author: | wtd [ Thu Aug 25, 2005 3:12 pm ] |
Post subject: | |
Cornflake wrote: Adn wtd... perhaps he meant that most *new* lanugages didn't support it; C++ is by no means a new language although it may be a modern one.
In many ways it's not modern. Lacking truly strong typing, but having explicit typing, and no real module system is pretty strong evidence that it is by no means modern. |
Author: | rizzix [ Fri Aug 26, 2005 12:40 am ] |
Post subject: | Re: Multiple Inheritance |
wtd wrote: Operator overloading is vital for programming in an idiomatic way in C++. Understanding multiple inheritance is certainly also important. Prohibiting use of technologies like these does no good in the long run. It just advances ignorance about things that can't be avoided forever. I see no point in MI.. well actually after looking through c++, i can see how it helps there (on rare occasions), since it's such a poorly designed language!
eitherway.. you can do just about everything in single inheritance than you could do through multiple inheritance.. languages did not exclude the whole concept of MI for no apparent reason.. there's always a reason to everything.. very few to none of the MI languages are acceptable in the enterprise workspace... for one very good reason.. the feature is practically always abused! there's too much of uncertainty.. and uncertainty is not cool! it's too expensive to maintain! hence MI is quite pointless and troublesome.. so, people have ignored it.. and not blindly but thoughtfully! after all why the burdern of the additional uncertainty when you can simply do whatever you wanted to do through Single Inheritance.. |
Author: | wtd [ Fri Aug 26, 2005 2:56 am ] | ||
Post subject: | |||
Languages which implement MI aren't suitable for large-scale applications in mission-critical settings? Better tell that to the folks using Eiffel for banking in Europe. Or maybe you could tell that to the people using Ada95 to build software for rockets and military hardware. Further, if MI is such a horrible thing, why do languages like Java and C# support it via interfaces? Why does Objective-C support it via categories and protocols? Why does Ruby support it via mix-ins? If I may ask, how does MI introduce confusion that can't be easily handled by a decent language? Eiffel provides feature adaptation. O'Caml provides the ability to give convenient names to different class representations of the current object. Python makes it pretty easy to resolve naming issues as well. A simple example:
|
Author: | rizzix [ Fri Aug 26, 2005 6:43 pm ] |
Post subject: | |
wtd wrote: why do languages like Java and C# support it via interfaces? Why does Objective-C support it via categories and protocols?
yes they do.. but it is implmented ingeniously thourgh a single inheritance paradigm... thus avoiding a whole lot of uncertainty (this is not the case with "raw" MI implementation like in c++).. wtd wrote: Why does Ruby support it via mix-ins? Ruby's support through "mix-ins" also brings about uncertainty.. just as in your example above... there's no way of knowing for sure whether the method foo of class c will call both of it's super class' methods.. and if not..then which one in perticular.... to avoid such uncertainty you need to document all these details separatly...
now imagine a situation where you are extending a class that is buried deep down a MI hierarchal structure of around 500 classes or so.. think about the number of unnecessary documentation you need to look up just to make sure you know exactly how your extended class will function when you choose to go with it's super's implentation of the method "foo"... thus if you're going to implement MI at least implement it properly... Protocols/Interface is one way of implementing it correctly.. Of course it's true that there are some things done easier through Ruby's mixins and C++'s raw MI implemenation, that cannot possibly be done so easily through Interfaces.. Thus to bridge up this gap AOP was invented.. Just like Protocols and Interfaces, Aspects also follows a single inheritance hierarchal structure.. but they address the cross cutting concerns (i.e those that run across the heirarchy). |
Author: | wtd [ Fri Aug 26, 2005 6:59 pm ] | ||||
Post subject: | |||||
Actually, the rules for O'Caml are pretty simple. Whichever class is inherited last has priority.
Prints "bar". But if we switch that to:
Then we get "foo". |