JavaScript高级编程技巧:用JavaScript继承和扩展对象

如果你熟悉面向对象编程,你很可能熟悉子类(Subclassing)和继承(Inheritance)。 然而,继承得到了一个坏名声,这是因为有些开发人员在需要修改程序时将其视为一个全面的解决方案,导致class层次结构可能变得难以管理。

JavaScript高级编程技巧:用JavaScript继承和扩展对象
如果你熟悉面向对象编程,你很可能熟悉子类(Subclassing)和继承(Inheritance)。 然而,继承得到了一个坏名声,这是因为有些开发人员在需要修改程序时将其视为一个全面的解决方案,导致class层次结构可能变得难以管理。
我们还可以使用其他设计模式来使我们的应用程序更易于理解并随时可以进行更改。下面我将向你们展示如何使用继承以及装饰器和复合模式来改进您的程序设计。

继承

继承的理念是“一个对象“是”另一个对象的专用版本”。有一个父类(也称为超类(superclass)),它定义了我们的对象的基本属性。 还有一个子类(子类(subclass))继承父类的属性。
继承的例子是:一只狗和一只贵宾犬,所有的狗都有一些特征,比如四条腿和吠叫的能力。而贵宾犬也具有这种能力所以它是一种狗。SUV是一种车辆。圆是一个形状。如果我们设计一个用于创建形状的程序,下面的图就是我们的class层次结构的样子。
JavaScript高级编程技巧:用JavaScript继承和扩展对象
拥有Shape类的好处是我们可以重用我们在其他类中定义的属性和方法。
注意‘getArea’方法在我们的每个子类中都被重新定义了。 我们不必重新定义这个方法,我们可以这样做的,以取代我们父类的方法实现,那是因为每个形状都有自己的计算面积的方法。
覆盖子类中的父方法是多态的一个例子。 多态性是对象具有多种形式的能力。 它允许子类具有与它们的超类同名的方法,可以具有不同的实现方法。
这是我们的代码,看起来像创建一个Shape和Circle子类的例子:

class Shape {
    constructor(x, y) {
        this.xPosition = x;
        this.yPosition = y;
    }
getArea() {...}
}
class Circle extends Shape {
    constructor(x, y, radius) {
        super(x, y, radius);
        this.radius = radius
    }
    getArea() {...}
}
let circle = new Circle(1,2,3);

这种设计选择的缺点之一是,如果你决定父类需要改变,那么子类可能也需要改变,以及我们创建的所有对象。
例如,假设我们稍后决定用对象替换Shape类的x和y参数会更好。因此我们需要更改我们所有子类的构造函数以及我们实例化的每个对象的参数。
我们可以看到这很容易变成问题。 我们必须确保我们第一次得到我们的设计是正确的,这样我们才能避免做出改变。 但这不实际,也不是我们需要的。 程序是一个不断发展的实体,如果我们能够灵活地进行修改,对我们的开发人员来说更好。 至少,我们不应该有超过一层的子类。

装饰器模式

装饰器允许我们在创建对象后附加属性。这意味着我们可以添加功能而不需要子类或关注对象的实现。
我们可以使用Shape类来创建圆,并用我们想要的其他属性来包装它,而不是想一个圆是一个形状。下面是使用装饰器创建圆对象的替代方法:

class Shape {
    constructor(data) {
        this.x = data.x;
        this.y = data.y;
    }
    getArea() {...}
}
function CircleDecorator(shape) {
    shape.radius = 3;
    shape.getArea = function() {...};
    return shape;
}
let shape = new Shape({x:1,y:2});
let circle = new CircleDecorator(shape);

我们可以使用装饰器添加或修改Shape类的成员,而不是继承它的子类。 用我们的形状的例子,你可能会发现你只是想创建一个具有所有你需要的属性的圆形对象,并为其他形状做同样的事情。没关系的,因为装饰器允许我们重用我们的Shape类中的代码,并修改它与每个形状不同的功能。所以我们将拥有更多的对象,而且更多的对象比更多的子类更容易管理。
这种模式不限于创建图形。您可以在任何想要将对象添加东西的情况下使用它。例如,我们可能有一个类来处理用户注册到我们的帐户。 在将他们的信息保存到我们的数据库之前,检查输入是否有效并且不包含任何恶意脚本这一个明智举动,我们可以用一种方法来修饰类,以便首先验证信息。 这个相同的装饰器可以在我们接受用户输入的应用程序中的任何地方重用。

复合模式

对象可以由其他对象组成。 应用程序的视图可以被认为是由其他视图组成的组件。 如果我们在制作游戏,我们的游戏世界会显示我们所创建的所有图形,如圆形和方形。 每次我们更新我们的内容时候,我们都需要重新绘制每一个元素。 我们需要一种将所有元素作为一个组来管理的方法。
这是复合模式可以帮助我们的地方。我们可以创建一个负责所有元素的类,当我们想重绘元素时,我们称这个类的绘制方法,它将调用每个单独元素的绘制方法。

class Component {
    constructor(name){
        this.components = [];
        this.element = document.createElement(name);
    }
    add(elem) {
        this.components.push(elem);
    }
    draw() {
        for (const elem of this.components) {
            elem.draw();
        }
    }
}
class Circle {
    constructor(data) {
        this.x = data.x;
        this.y = data.y;
        this.radius = data.radius;
    }
    getArea() {...}
    draw() {...}
}
let world = new Component('div');
let circle = new Circle({x: 1, y:1, radius: 2});
let circle2 = new Circle({x: 10, y:10, radius: 2});
world.add(circle);
world.add(circle2);
world.draw();

一个网页也可以被认为是一个组件。 这个组件可以有一个菜单,一个侧边栏和一个博客文章。 这个帖子将是一个具有图片,标题和正文的子组件。 我们的主要应用程序组件的绘制方法将调用菜单,侧边栏和后期绘制。 帖子组件将依次调用帖子图片,标题和正文的绘制。
以下是网页分成几个部分的视图:
JavaScript高级编程技巧:用JavaScript继承和扩展对象
这种模式不限于创建视图。例如,如果我们在制作游戏,我们可能会有一个组件来管理屏幕上的元素,管理音频的组件以及管理游戏状态的组件。这些都将在我称之为游戏引擎的组件中。 游戏引擎会有一个初始化方法,它会调用每个子组件的初始化方法。 这是使用复合模式的力量。 我们可以把它们当作一个对象来处理,而不是处理个别对象。

结论

继承允许我们通过用另一个对象来定义一个对象来重用代码。 修饰器模式允许我们在不改变原始代码或子类的情况下将responsibilities添加到对象中。 复合模式模拟部分 – 整体关系,这些模式并不意味着孤立地使用。

原创文章,作者:webstack,如若转载,请注明出处:https://www.webstacks.cn/experience/453.html

发表评论

登录后才能评论