JS++ 虚拟方法
正如我们在上一节中提到的,如果我们想要运行时的多态性,使用铸模会导致代码不干净。
举例来说,让我们改变main.jspp的代码,使我们所有的动物都在一个数组内。在这里,我们将在数组上进行循环,以渲染动物。打开main.jspp,把代码改成。
import Animals;
Animal[] animals = [
new Cat("Kitty"),
new Cat("Kat"),
new Dog("Fido"),
new Panda(),
new Rhino()
];
foreach(Animal animal in animals) {
if (animal instanceof Cat) {
((Cat) animal).render();
}
else if (animal instanceof Dog) {
((Dog) animal).render();
}
else {
animal.render();
}
}
现在我们的代码甚至不如我们原来的代码优雅,我们只是实例化了动物,指定了最具体的类型,然后调用render()。
然而,这段代码可以被大量简化,直到它变得优雅。事实上,我们可以将 “foreach “循环减少到一条语句。答案是:虚拟方法。
虚拟方法实现了 “晚期绑定”。换句话说,要调用的具体方法是在运行时而不是编译时解决的。我们不需要所有的 “instanceof “检查、所有的转换和所有的 “if “语句,正如我们在上面的代码中看到的那样。我们可以实现更优雅的东西。
首先,打开Animal.jspp,改变’render’方法,使其包括’virtual’修饰符。
external ;
module Animals
{
class Animal
{
protected varelement;
protected Animal(string iconClassName) {
string elementHTML = makeElementHTML(iconClassName);
element =(elementHTML);
}
public virtual void render() {
("#content").append(element);
}
private string makeElementHTML(string iconClassName) {
string result = '<div class="animal">';
result += '<i class="icofont ' + iconClassName + '"></i>';
result += "</div>";
return result;
}
}
}
保存Animal.jspp,这就是我们需要做的唯一改动。
然而,仅仅使我们的方法成为虚拟的还不够。在Cat.jspp和Dog.jspp中,我们在它们的’render’方法上使用’overwrite’修改器。覆盖 “修饰符指定了编译时的分辨率。我们想要的是运行时分辨率。我们要做的就是改变Cat.jspp和Dog.jspp,使用 “覆盖 “修饰符,而不是 “覆盖 “修饰符。为了简洁起见,我将只展示对Cat.jspp的修改,但你也需要对Dog.jspp进行修改。
external ;
module Animals
{
class Cat : Animal
{
string _name;
Cat(string name) {
super("icofont-animal-cat");
_name = name;
}
override void render() {element.attr("title", _name);
super.render();
}
}
}
就是这样。我们所要做的就是改变修改器。现在我们终于可以编辑main.jspp了,这样循环内就只有一条语句了。
import Animals;
Animal[] animals = [
new Cat("Kitty"),
new Cat("Kat"),
new Dog("Fido"),
new Panda(),
new Rhino()
];
foreach(Animal animal in animals) {
animal.render();
}
编译你的代码并打开index.html。一切都应该工作。现在,我们已经能够大规模地简化我们的代码,并仍然得到预期的行为。具体来说,我们将 “foreach “循环的代码从以下几个方面减少了。
foreach(Animal animal in animals) {
if (animal instanceof Cat) {
((Cat) animal).render();
}
else if (animal instanceof Dog) {
((Dog) animal).render();
}
else {
animal.render();
}
}
对这一点。
foreach(Animal animal in animals) {
animal.render();
}
我们之所以能够如此大幅度地简化代码,是因为将一个方法标记为 “虚拟 “意味着潜在的运行时多态性。与 “覆盖 “修饰符一起,编译器知道我们想在 “渲染 “方法上进行后期绑定,所以 “后期 “绑定正好发生在需要的时候:”渲染 “方法将在运行时被解析,如果并且只有在它需要被解析的时候(在 “foreach “循环中)。