你现在应该了解函式基本的功能. 你应该可以宣告并呼叫一个函式. 另外, 你应该能认识函式定义, 虽然你可能是第一次接触 LPC. 你现在并不见得能定义你自己的函式. 函式是 LPC 物件的基石. 函式中的程式码, 要别的函式呼叫它们的时候才会执行. 呼叫一个函式时, 作出呼叫的函式要给它输入值, 才能执行被呼叫的函式. 被呼叫的函式执行其程式码, 并传回某种资料型态的传回值给呼叫它的函式. 没有传回值的函式属於无传回值 (void) 型态.仔细看过你自己的工作室程式码之後, 它看起来大概像这样 (视 mudlib 而定):
-----
inherit "/std/room";-----void create() { ::create();
set_property("light", 2);
set_property("indoors", 1);
set("short", "Descartes 的工作室");
set("long", "此处是 Descartes 工作的地方.\n这里是一个立方体.\n");
set_exits( ({ "/d/standard/square" }), ({ "square" }) );
}如果你到目前为止, 所有的课本内容都了解的话, 你应该能认出以下的程式码:
这一章会找出这些问题的解答, 你现在应该脑中应该有这些问题:
- 1) create() 是函式的定义. (嘿 ! 他没有宣告它)
- 2) 它呼叫 set_property() 、set()、set_exits(), 没有一个函式在这段程式码中曾有宣告或定义.
- 3) 最上面有一行, 不是宣告变数或函式, 也不是函式定义 !
- 1) 为什麽没有宣告 create() ?
- 2) 为什麽 set_property() 、set() 、set_exits() 已经宣告并定义过了 ?
- 3) 档案最上面那一行到底是啥东西 ?
5.2 物件导向程式设计 (object oriented programming, OOP)
继承 (inheritance) 是定义真正物件导向程式设计的特性之一. 它让你创造通用的程式码, 能以多种用途用於许多不同的程式中. 一个 mudlib 所作的, 就是创造这些通用的档案 (物件) , 让你用来制造特定物件.如果你必须把定义前面工作室全部所需要的程式码写出来, 你大概必须要写 1000 行程式码才能得到一个房间所有的功能. 当然, 那根本是浪费磁碟空间. 再者, 这种程式码与玩家和其他房间的互动性很差, 因为每一个创造者都写出自己的函式以作出一个房间的功能. 所以, 你可能使用 query_long() 写出房间的长叙述, 其他的巫师可能使用 long() . 这就是 mudlib 彼此不相容最主要的原因, 因为它们使用不同的物件互动协定.
OOP 克服了这些问题. 前面的工作室中, 你继承已经定义在 "/std/room.c" 档案中的函式. 它拥有普通房间所需要的全部函式定义其中. 当你要制造一个特定的房间, 你拿这个房间档案中定义好的通用函式功能, 并加上你自己的函式 create() 以制造一个独特的房间.
你现在大概猜得出来, 这一行:-----
inherit "/std/room";-----让你继承 "std/room.c" 的函式功能. 藉由继承函式功能, 它代表你可以使用 "/std/room.c" 里面已经宣告并定义好的函式. 在 Nightmare Mudlib 中, "/std/room.c" 里面有许多函式, 其中有 set_property()、set()、set_exits() 函式, 都已经宣告并定义过. 在你的 creat() 函式里, 你呼叫那些函式来设定你房间一开始的值. 这些值让你的房间不同於别的房间, 却保留与记忆体中其他房间互动的能力.
实际的写作中, 每一个 mudlib 都不同, 所以要你使用不同一套的标准函式来达到相同的功能. 说明有哪些函式存在和它们是作什麽用的, 已经超出了这本课本的范围. 如果你的 mudlib 有自己详细的说明资料, 你会找到教你如何使用各种继承档案的说明文件以创造物件. 这些说明应该会告诉你有哪些函式、它们需要哪些输入、它们输出的资料型态、以及它们的功能.
本章距离完整解释继承如此复杂的主题还有一大段距离. 本文的目的只是让你能了解如何使用继承来创造你的物件. 以後的课本将对此会有完整的讨论. 现在你
应该已经了解底下几点:注解:1) 每一个 mudlib 都有一套通用物件库, 有它们自己的通用函式. 创造者透过继承使用它们, 让撰写物件程式码这件工作更轻松, 并与其他物件之间能良好互动. 2) 可被继承的档案里头的函式, 每个 mudlib 都不一样. 你的 mud 里应该有说明文件解释如何使用这些可被继承的档案. 如果你还不知道有哪些函式可用, 那你就没有办法用它们. 任何时候, 都请你特别注意输入和输出的资料型态. 3) 你藉由底下这行继承函式的功能: -----
inherit "filename";-----
filename 是被继承的物件档案名称. 这行放在你程式码的开头.你可能看到有几处地方有 ::create() 或 ::init() 或 ::reset() 语法. 你现在不需要完全了解这个, 但是应该告诉你一点线索, 知道它到底是什麽. 「::」运算子是一种特殊的方法来呼叫继承物件的函式 (叫做范围解析运算子 scope resolution operator). 例如, 大多数 mud 的 room.c 都有叫做 create() 的函式. 当你继承 room.c 并设定 create() 时, 你所作的事称为超越 (override) room.c 的 create() 函式. 这表示不管任何东西呼叫你房间的 create() , 它会呼叫「你的」版本, 而不是 room.c 里面的那一个. :: 运算子让你能呼叫 room.c 里的 create() 而不是你的 create().
一个例子:
-----
#1-----inherit "/std/room";
void create() { create(); }
-----
#2-----inherit "/std/room";
void create() { ::create(); }
第一个例子是个恐怖的例子. 当它被载入时, driver 呼叫 create() , 之後 create() 再呼叫 create(), create() 又呼叫 create(), 这时 create() 又呼叫 create()......换句话说, 所有的 create() 就一直呼叫自己直到 driver 侦测到太深的递回 (recursion) 并跳出来.
第二个例子基本上只是浪费记忆体, 它的功能跟 room.c 没有两样. 对它而言, driver 先呼叫它的 room.c , 然後呼叫 ::create() , 也就是 room.c 里的 create() . 其他的地方就跟 room.c 的功能一样.