JS++ 接口

JS++ 接口

正如我们在子类型多态性一章中所学到的,为超类型定义的操作可以安全地被其子类型所替代。因此,为 “动物 “对象定义的操作可以安全地接受 “猫 “或 “狗 “对象。

此外,我们提到,你不应该混淆子类型和继承。子类型描述了类型之间的关系,而继承关注的是实现。这种区别在接口中变得很明显。

接口类似于我们刚刚学到的抽象类和方法,只是接口的所有 “方法 “都是抽象的。一个接口不包含任何实现。

接口指定类型,但不指定实现。让我们来做个交通方面的比喻。你需要从旧金山到洛杉矶。你可能并不关心你如何到达那里,你只需要一种运输方式。接口并不定义 “如何”,所以在这种情况下,接口就是 “运输方式”。如何 “将在 “火车”、”汽车”、”飞机 “或 “巴士 “等类中实现。只要 “运输方式 “能够移动,它就应该满足你的需求。然而,你不能乘坐一个抽象的 “运输方式”;你需要一个 “汽车”、”火车”、”飞机 “或 “巴士”。

为了将这些关系可视化。

interface IModeOfTransport
{
    void move();
}

class Car : IModeOfTransport
{
    override void move() {
        // ...
    }
}

class Train : IModeOfTransport
{
    override void move() {
        // ...
    }
}

class Airplane : IModeOfTransport
{
    override void move() {
        // ...
    }
}

class Bus : IModeOfTransport
{
    override void move() {
        // ...
    }
}

每个具体的类都有不同的移动方式。例如,”汽车 “和 “飞机 “的 “移动 “方法的实现会有很大不同,因为飞机可以飞行。

从这些关系中,你应该能够理解你可以像这样实例化上面的一个类,比如’汽车’。

Car rentalCar = new Car();

比方说,你正在开发一个旅游网站。你想为用户提供从旧金山到洛杉矶的几种选择。在这种情况下,你可能想概括一下这个类型。

IModeOfTransport transport = new Car();

现在,当用户选择了一个不同的选项(除了 “汽车”),你可以很容易地将 “运输 “变量中持有的数据类型改为 “火车”、”飞机 “或 “巴士 “的实例。

然而,你也认识到,你不能通过 “运输方式 “从旧金山到洛杉矶。你必须指定什么样的运输方式。因此,你应该认识到这将导致一个编译器错误。

IModeOfTransport transport = new IModeOfTransport();

因此,我们只使用接口来指定类型和类型关系。接口不提供实现,所以它们不能被实例化。

有了这种认识,我们就可以改变我们的 “动物 “类型关系。首先,让我们从一个非常基本的例子开始。我们的一些动物缺乏颜色。现在只是一个黑白的网页。让我们来添加一点颜色。

导航到你的OOP项目的’src/Animals/’文件夹,包含Animal.jspp、Cat.jspp、Dog.jspp等。在这个文件夹中,创建一个名为IColorizable.jspp的新文件。在IColorizable.jspp中,让我们输入以下代码。

module Animals
{
    interface IColorizable
    {
        void colorize();
    }
}

你会注意到的第一件事是,我们的接口名称前缀为 “I”(大写的 “i”)。这就是所谓的 命名规则。 命名惯例帮助我们命名类、接口、模块、函数和变量,使之与加入我们团队的其他程序员、其他第三方库等使用的名称一致。在JS++中,典型的做法是在接口名称前加上字母 “I”(大写 “i”)。

请注意,我们不需要 “抽象 “修饰语。一个接口内的所有方法签名都被认为是抽象的,因为一个接口内不允许有实现。

接下来,我们必须指定哪些类与IColorizable共享关系。现在,让我们只给’狗’类一些颜色。

import Externals.DOM;

external ;

module Animals
{
    class Dog : Animal, IColorizable
    {
        string _name;

        Dog(string name) {
            super("icofont-animal-dog");
            _name = name;
        }

        final void render() {element.attr("title", _name);
            super.render();
        }

        final void talk() {
            alert("Woof!");
        }

        final void colorize() {
            $element.css("color", "brown");
        }
    }
}

现在,我们必须对我们的 “狗 “实例调用 colorize() 方法。编辑main.jspp。

import Animals;

external ;

IColorizable fido = new Dog("Fido");
fido.colorize();

Animal[] animals = [
    new Cat("Kitty"),
    new Cat("Kat"),
    fido,    new Panda(),
    new Rhino()
];

foreach(Animal animal in animals) {
    animal.render();
}("#content").append("<p>Number of animals: " + Animal.getCount().toString() + "</p>");

试着编译。你会得到一个错误。

[  ERROR  ] JSPPE5034: Could not determine type for array literal. All elements in array literal must be the same, a mixture of primitive types, or descendants of the same base class at line 8 char 19 at main.jspp

发生这种情况的原因是’fido’变量的类型是’IColorizable’。同时,这个数组只接受’动物’类型的元素。如果你还记得,我们的’狗’类直接继承了’IColorizable’,而我们的’动物’基类却没有。因此,我们不能将’IColorizable’的对象直接插入’Animal’类型的数组中;否则,我们就可以像在JavaScript中那样进行不安全的操作。

JS++  接口

注意,在图表中,’动物’和’IColorizable’之间没有类型关系。

有多种方法来解决这个问题。我们将通过使我们所有的动物都可着色来解决这个问题。编辑Dog.jspp并删除与’IColorizable’的类型关系。然而,保留’colorize’的方法实现。你以后会看到为什么。下面是你必须在Dog.jspp中删除的内容的可视化。

import Externals.DOM;

external ;

module Animals
{
    class Dog : Animal ~~, IColorizable~~
    {
        string _name;

        Dog(string name) {
            super("icofont-animal-dog");
            _name = name;
        }

        final void render() {element.attr("title", _name);
            super.render();
        }

        final void talk() {
            alert("Woof!");
        }

        final void colorize() {
            $element.css("color", "brown");
        }
    }
}

现在打开Animal.jspp并添加。

external ;

module Animals
{
    abstract class Animal : IColorizable
    {
        protected varelement;
        private static unsigned int count = 0;

        protected Animal(string iconClassName) {
            string elementHTML = makeElementHTML(iconClassName);
            element =(elementHTML);

            attachEvents();

            Animal.count++;
        }

        public static unsigned int getCount() {
            return Animal.count;
        }

        public virtual void render() {
            ("#content").append(element);
        }

        public abstract void colorize();

        public abstract void talk();
        private void attachEvents() {
            $element.click(talk);
        }

        private string makeElementHTML(string iconClassName) {
            string result = '<div class="animal">';
            result += '<i class="icofont ' + iconClassName + '"></i>';
            result += "</div>";
            return result;
        }
    }
}

由于我们的 “动物 “类是 “抽象 “的,我们不需要 “实现”‘着色’方法。相反,我们只是通过将’colorize’方法声明为’抽象’,将其进一步推迟到’动物’的派生类。还记得我们如何没有从 “狗 “类中删除 “着色 “方法吗?这就是原因。我们已经把实现’colorize’的责任交给了’Animal’的派生类,但是我们仍然能够描述’Animal’和’IColorizable’之间的类型关系,其中’IColorizable’是’Animal’的一个超类型。

现在,我们只需要为我们的其他动物添加颜色。我将保持简短。这里有一个模板,你需要在’Cat.jspp’、’Panda.jspp’和’Rhino.jspp’中添加这样的代码。

final void colorize() {
    $element.css("color", colorName);
}

将’colorName’替换为你想让动物变成的颜色。让你的猫变成 “金色”,熊猫变成 “黑色”,而犀牛变成 “银色”。

编辑main.jspp。

import Animals;

external ;

Animal[] animals = [
    new Cat("Kitty"),
    new Cat("Kat"),
    new Dog("Fido"),    new Panda(),
    new Rhino()
];

foreach(Animal animal in animals) {
    animal.colorize();
    animal.render();
}("#content").append("<p>Number of animals: " + Animal.getCount().toString() + "</p>");

编译。

$ js++ src/ -o build/app.jspp.js

打开index.html。结果应该是这样的。

JS++  接口

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程

JavaScript 教程