| CONCEPT |
| Inheritance |
| |
| DESCRIPTION |
| Have you noticed how many objects in the system have the same |
| functionality in common? Let's look at rooms for instance, they |
| all have the ability to host people and provide commands. It's |
| not that every room is programmed with the same basic functions |
| again and again, rather it will use a model room and then make |
| some special changes to it. That doesn't work by copying the |
| file.. Ouch! Don't replicate code! But by putting a tiny inherit |
| declaration |
| |
| inherit "<model-class>"; |
| |
| at the beginning of your new file. This must come before any local |
| variables or functions. Once inherited your class will behave just |
| like the model class, because all the public methods are available |
| to the outside world. Now it is in your hands to change such an |
| inherited behaviour. You have the following tools to do so: |
| |
| * Access to variables |
| |
| It is one of the best design decisions in LPC that variables |
| are not accessible from outside, but you can use inherited |
| variables just as if they were your own. Modifiers apply however. |
| |
| * Method overloading |
| |
| int method_that_also_exists_in_the_model() { |
| <your new code> |
| } |
| |
| You can simply rewrite a method that is also defined in the model |
| class, and thus change how it behaves. Contrary to other languages |
| in LPC method overloading only matches the name of the method, so |
| even by changing the amount and type of parameters you will mask |
| out the original version of the method. You can even apply other |
| modifiers to it as the original. |
| |
| * Calling inherited methods |
| |
| int method_that_also_exists_in_the_model() { |
| <your new code> |
| return ::method_that_also_exists_in_the_model(); |
| } |
| |
| You can add to the behaviour of a method by redefining it, |
| then calling it from within your new version. You can actually |
| call inherited methods from anywhere in your code. The double |
| colon tells the compiler you are looking for the inherited |
| variant. |
| |
| EXAMPLE |
| Let's imagine very simple food in a file called "/the/food.c": |
| |
| // unless "modified" variables are accessible by inheritors |
| int vitamins = 10; |
| |
| // please overload this function with your own description |
| public short() { return "something edible"; } |
| |
| // let's do some standard action for food |
| public consume() { |
| this_player() -> nourish(vitamins); |
| destruct(this_object()); |
| } |
| |
| And now someone else decides to do some italian cooking in a |
| file called "/the/fusilli.c" |
| |
| inherit "/the/food"; |
| |
| // we have our own variables. |
| int gone_cold = 0; |
| |
| // and we simply redefine the short() function to replace it |
| public short() { |
| // description changes depending on gone_cold |
| return "a "+( gone_cold ? "stinking" : "steaming" ) |
| +" plate of fusilli"; |
| } |
| |
| // we have a new function to make food go cold |
| private deteriorate() { |
| gone_cold = 1; |
| write("The fusilli have gone cold.\n"); |
| } |
| |
| // assume this gets called at creation |
| private create() { |
| // we can access the variable we inherited from food.c |
| vitamins = 44; // tomato has plenty of vitamins |
| |
| // go cold in 5 minutes |
| call_out( #'deteriorate, 5 * 60 ); |
| } |
| |
| // we can overload the function even with new parameters |
| public consume(how) { |
| // fetch the name of the person, or use "Someone" |
| string name = this_player() -> name() || "Someone"; |
| |
| if (!gone_cold) { |
| write("You enjoy a delicious plate of fusilli.\n"); |
| say(name +" guzzles a plate of hot fusilli.\n"); |
| } |
| else if (how == "quickly") { |
| write("You eat the fusilli so quickly you " |
| "hardly notice they have gone cold.\n"); |
| say(name +" wolfs down a plate of cold fusilli.\n"); |
| } |
| else { |
| write("You eye the plate and wonder if you " |
| "really feel like eating cold fusilli.\n"); |
| return; // don't eat |
| } |
| |
| // and here comes the most important part: |
| // we execute consume() from food.c, so we |
| // actually inherit its behaviour. |
| ::consume(); |
| } |
| |
| ADVANCED USAGE |
| * Doing multiple inheritance |
| |
| While the Java(TM) language has so-called interfaces as a kludge, |
| LPC doesn't need them as it supports real multiple inheritance. |
| A very powerful feature, it lets you combine the behaviour of |
| several classes into a new one. Simply put several lines of |
| inherit declarations underneath each other. If you have name |
| collisions in the namespace of inherited methods, you will have |
| to address them explicitely with a "the/file"::method(args) syntax. |
| |
| * Wildcarded multiple inheritance |
| |
| LDMud 3.2.1@117 introduces an advanced voodoo syntax which allows |
| you to call several methods in model classes at once. This works by |
| writing a glob type match ('*' and '?' wildcards) into the string |
| in front of the double colon, as in "*"::create(). |
| I wouldn't recommend you to use this, it's better to be clearly |
| conscious of what you inherit and do. But if you're desperate, there |
| you go. |
| |
| Since LDMud 3.5.0 it is possible to pass arguments as well. |
| |
| ADVANCED EXAMPLE |
| inherit "foo"; |
| inherit "bar"; |
| inherit "baz"; |
| inherit "ball"; |
| |
| reset() { |
| "ba?"::reset(); |
| // calls bar::reset() and baz::reset() |
| |
| "ba*"::reset(); |
| // calls bar::reset(), baz::reset() and ball::reset() |
| |
| "*"::reset(); |
| // calls every inherited reset() function. |
| |
| "ball"::rejoice("Listen to italectro today!"); |
| // only explicit filename of model class allows |
| // passing arguments to the inherited method |
| } |
| |
| AUTHOR |
| symlynX of PSYC and Nemesis, with a little help from Someone |
| |
| SEE ALSO |
| functions(LPC), initialisation(LPC), modifiers(LPC), pragma(LPC), |
| overloading(C), |
| function_exists(E), functionlist(E), inherit_list(E), |
| symbol_variable(E), variable_exists(E), variable_list(E) |