MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | CONCEPT |
| 2 | Inheritance |
| 3 | |
| 4 | DESCRIPTION |
| 5 | Have you noticed how many objects in the system have the same |
| 6 | functionality in common? Let's look at rooms for instance, they |
| 7 | all have the ability to host people and provide commands. It's |
| 8 | not that every room is programmed with the same basic functions |
| 9 | again and again, rather it will use a model room and then make |
| 10 | some special changes to it. That doesn't work by copying the |
| 11 | file.. Ouch! Don't replicate code! But by putting a tiny inherit |
| 12 | declaration |
| 13 | |
| 14 | inherit "<model-class>"; |
| 15 | |
| 16 | at the beginning of your new file. This must come before any local |
| 17 | ariables or functions. Once inherited your class will behave just |
| 18 | like the model class, because all the public methods are available |
| 19 | to the outside world. Now it is in your hands to change such an |
| 20 | inherited behaviour. You have the following tools to do so: |
| 21 | |
| 22 | * Access to variables |
| 23 | |
| 24 | It is one of the best design decisions in LPC that variables |
| 25 | are not accessible from outside, but you can use inherited |
| 26 | variables just as if they were your own. Modifiers apply however. |
| 27 | |
| 28 | * Method overloading |
| 29 | |
| 30 | int method_that_also_exists_in_the_model() { |
| 31 | <your new code> |
| 32 | } |
| 33 | |
| 34 | You can simply rewrite a method that is also defined in the model |
| 35 | class, and thus change how it behaves. Contrary to other languages |
| 36 | in LPC method overloading only matches the name of the method, so |
| 37 | even by changing the amount and type of parameters you will mask |
| 38 | out the original version of the method. You can even apply other |
| 39 | modifiers to it as the original. |
| 40 | |
| 41 | * Calling inherited methods |
| 42 | |
| 43 | int method_that_also_exists_in_the_model() { |
| 44 | <your new code> |
| 45 | return ::method_that_also_exists_in_the_model(); |
| 46 | } |
| 47 | |
| 48 | You can add to the behaviour of a method by redefining it, |
| 49 | then calling it from within your new version. You can actually |
| 50 | call inherited methods from anywhere in your code. The double |
| 51 | colon tells the compiler you are looking for the inherited |
| 52 | variant. |
| 53 | |
| 54 | EXAMPLE |
| 55 | |
| 56 | Let's imagine very simple food in a file called "/the/food.c": |
| 57 | |
| 58 | // unless "modified" variables are accessible by inheritors |
| 59 | int vitamins = 10; |
| 60 | |
| 61 | // please overload this function with your own description |
| 62 | public short() { return "something edible"; } |
| 63 | |
| 64 | // let's do some standard action for food |
| 65 | public consume() { |
| 66 | this_player() -> nourish(vitamins); |
| 67 | destruct(this_object()); |
| 68 | } |
| 69 | |
| 70 | And now someone else decides to do some italian cooking in a |
| 71 | file called "/the/fusilli.c" |
| 72 | |
| 73 | inherit "/the/food"; |
| 74 | |
| 75 | // we have our own variables. |
| 76 | int gone_cold = 0; |
| 77 | |
| 78 | // and we simply redefine the short() function to replace it |
| 79 | public short() { |
| 80 | // description changes depending on gone_cold |
| 81 | return "a "+( gone_cold ? "stinking" : "steaming" ) |
| 82 | +" plate of fusilli"; |
| 83 | } |
| 84 | |
| 85 | // we have a new function to make food go cold |
| 86 | private deteriorate() { |
| 87 | gone_cold = 1; |
| 88 | write("The fusilli have gone cold.\n"); |
| 89 | } |
| 90 | |
| 91 | // assume this gets called at creation |
| 92 | private create() { |
| 93 | // we can access the variable we inherited from food.c |
| 94 | vitamins = 44; // tomato has plenty of vitamins |
| 95 | |
| 96 | // go cold in 5 minutes |
| 97 | call_out( #'deteriorate, 5 * 60 ); |
| 98 | } |
| 99 | |
| 100 | // we can overload the function even with new parameters |
| 101 | public consume(how) { |
| 102 | // fetch the name of the person, or use "Someone" |
| 103 | string name = this_player() -> name() || "Someone"; |
| 104 | |
| 105 | if (!gone_cold) { |
| 106 | write("You enjoy a delicious plate of fusilli.\n"); |
| 107 | say(name +" guzzles a plate of hot fusilli.\n"); |
| 108 | } |
| 109 | else if (how == "quickly") { |
| 110 | write("You eat the fusilli so quickly you " |
| 111 | "hardly notice they have gone cold.\n"); |
| 112 | say(name +" wolfs down a plate of cold fusilli.\n"); |
| 113 | } |
| 114 | else { |
| 115 | write("You eye the plate and wonder if you " |
| 116 | "really feel like eating cold fusilli.\n"); |
| 117 | return; // don't eat |
| 118 | } |
| 119 | |
| 120 | // and here comes the most important part: |
| 121 | // we execute consume() from food.c, so we |
| 122 | // actually inherit its behaviour. |
| 123 | ::consume(); |
| 124 | } |
| 125 | |
| 126 | ADVANCED USAGE |
| 127 | |
| 128 | * Doing multiple inheritance |
| 129 | |
| 130 | While the Java(TM) language has so-called interfaces as a kludge, |
| 131 | LPC doesn't need them as it supports real multiple inheritance. |
| 132 | A very powerful feature, it lets you combine the behaviour of |
| 133 | several classes into a new one. Simply put several lines of |
| 134 | inherit declarations underneath each other. If you have name |
| 135 | collisions in the namespace of inherited methods, you will have |
| 136 | to address them explicitely with a "the/file"::method(args) syntax. |
| 137 | |
| 138 | * Wildcarded multiple inheritance |
| 139 | |
| 140 | LDMUD 3.2.1@117 introduces an advanced voodoo syntax which allows |
| 141 | you to call several methods in model classes at once, but for some |
| 142 | technical reasons it cannot pass any arguments. This works by |
| 143 | writing a glob type match ('*' and '?' wildcards) into the string |
| 144 | in front of the double colon, as in "*"::create(). I wouldn't |
| 145 | recommend you to use this, it's better to be clearly conscious of |
| 146 | what you inherit and do. But if you're desperate, there you go. |
| 147 | |
| 148 | ADVANCED EXAMPLE |
| 149 | |
| 150 | inherit "foo"; |
| 151 | inherit "bar"; |
| 152 | inherit "baz"; |
| 153 | inherit "ball"; |
| 154 | |
| 155 | reset() { |
| 156 | "ba?"::reset(); |
| 157 | // calls bar::reset() and baz::reset() |
| 158 | |
| 159 | "ba*"::reset(); |
| 160 | // calls bar::reset(), baz::reset() and ball::reset() |
| 161 | |
| 162 | "*"::reset(); |
| 163 | // calls every inherited reset() function. |
| 164 | |
| 165 | "ball"::rejoice("Listen to italectro today!"); |
| 166 | // only explicit filename of model class allows |
| 167 | // passing arguments to the inherited method |
| 168 | } |
| 169 | |
| 170 | AUTHOR |
| 171 | symlynX of PSYC and Nemesis, with a little help from Someone |
| 172 | |
| 173 | SEE ALSO |
| 174 | functions(LPC), initialisation(LPC), modifiers(LPC), pragma(LPC), |
| 175 | overloading(C) |
| 176 | function_exists(efun), functionlist(efun), inherit_list(efun), |
| 177 | symbol_variable(efun), variable_exists(efun), variable_list(efun). |