![](https://i-blog.csdnimg.cn/blog_migrate/5b971368950cac732f1cdf8ef2a01698.png)
如果您熟悉面向对象的编程,则很可能熟悉子类化和继承。 但是,继承一直是一个糟糕的说唱。 我认为这是因为某些开发人员在需要修改程序时将其视为万能的解决方案。 问题在于类层次结构可能变得无法管理。
我们还可以使用其他设计模式来使我们的应用程序更易于理解并随时可以进行更改。 我将向您展示如何使用继承以及装饰器和复合模式来改善程序设计的示例。
遗产
继承背后的想法是,一个对象“是另一个对象的”专门版本。 有一个父类(也称为超类),它定义了对象的基本属性。 并且有一个子类(子类)继承了父类的属性。
继承的一个例子是狗和贵宾犬。 所有的狗都有某些特征,例如四腿和吠叫。 贵宾犬是一种狗。 SUV是车辆。 圆是形状。 如果我们正在设计用于创建形状的程序,则这就是我们的类层次结构。
![基本继承图](https://i-blog.csdnimg.cn/blog_migrate/420e08a75ef02d8a45788876539b1e1b.png)
拥有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类中的代码,并使用每个形状不同的功能对其进行修改。 结果,我们将拥有更多的对象,但是更多的对象比更多的子类更易于管理。
此模式不限于创建图形。 您可以在要向对象添加职责的任何情况下使用它。
例如,我们可能有一个类来处理将用户注册到我们的帐户。 在将他们的信息保存到数据库之前,检查输入是否有效并且不包含任何恶意脚本是明智的。 我们可以使用一种方法来装饰类,以首先验证信息。 可以在我们接受用户输入的应用程序中的任何地方重用相同的装饰器。
复合图案
对象可以由其他对象组成。 应用程序的视图可以认为是由其他视图组成的组件。 如果我们要制作游戏,我们的游戏世界将显示我们创建的所有图形,例如圆形和正方形。 每次更新视图时,我们都需要重绘每个元素。 我们需要一种将所有元素作为一个整体进行管理的方法。
这就是复合模式可以为我们提供帮助的地方。 我们可以创建一个负责所有元素的类。 当我们想重绘元素时,我们调用此类的draw方法,它将在每个单独的元素上调用draw方法。
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();
网页也可以被视为一个组成部分。 该组件可以具有菜单,侧边栏和博客文章。 该帖子将是包含图片,标题和正文的子组件。 我们的主要应用程序组件的draw
方法将在菜单,边栏和发布上调用draw。 帖子组件将依次调用帖子图像,标题和正文上的绘图。
这是一个将网页分为几部分的视图:
![复合模式](https://i-blog.csdnimg.cn/blog_migrate/0a669635648ab6a9624a34eab081cb05.png)
这种模式不仅限于创建视图。 例如,如果我们要制作游戏,则可能有一个组件来管理屏幕上的元素,一个组件来管理音频以及一个组件来管理游戏状态。
这些都在我称为游戏引擎的组件中。 游戏引擎将具有一个initialize方法,该方法将在其每个子组件上调用initialize方法。 这就是使用复合模式的力量。 无需处理单个对象,我们可以将它们视为一个对象。
结论
继承使我们可以通过根据另一个对象定义一个对象来重用代码。 装饰器模式使我们可以向对象添加职责,而无需更改原始代码或子类。 组合模式对部分整体关系进行建模。 这些模式并非孤立地使用。
JavaScript已成为在网络上工作的语言。 它并非没有学习曲线,并且有许多框架和库也让您忙碌。 如果您正在寻找其他资源来学习或在工作中使用,请查看Envato Market上可用的资源 。
您可以根据需要将它们组合在一起。 我提供的示例也不应被视为使用这些模式的唯一应用程序。 他们只是一个指南。 随时使用您的创造力来应用它们。 没有一种方法可以实现它们,也没有一个用例。
翻译自: https://code.tutsplus.com/tutorials/inheritance-and-extending-objects-with-javascript--cms-29836