Наследование и расширение объектов с помощью JavaScript

13 января 2018

Если вы знакомы с объектно-ориентированным программированием, вы, скорее всего, знакомы с подклассом и наследованием. Однако наследование получило плохой рэп. Я считаю, что это связано с тем, что некоторые разработчики рассматривают его как решение для всех, когда вам нужно изменить программу. Проблема в том, что иерархии классов могут стать неуправляемыми.

Существуют другие шаблоны проектирования, которые мы можем использовать, чтобы наши приложения были более понятными и готовыми к изменениям. Я покажу вам примеры того, как вы можете использовать наследование и декоратор и составной шаблон для улучшения дизайна вашей программы.

Наследование

Идея наследования состоит в том, что один объект «является« специализированной версией другого объекта. Существует родительский класс (также известный как суперкласс), который определяет базовые свойства нашего объекта. И есть дочерний класс (подкласс), который наследует свойства родительского класса.

Примером наследования является собака и пудель. У всех собак есть определенные функции, такие как четыре ноги и способность лаять. Пудель - это «собака». Внедорожник является транспортным средством. Круг - это форма. Вот как выглядела бы наша иерархия классов, если бы мы разрабатывали программу для создания фигур.

Преимущество наличия класса 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);

Один из недостатков этого выбора дизайна заключается в том, что если вы решите изменить родительский класс, то, возможно, потребуется также изменить подклассы вместе со всеми созданными нами объектами.

Например, предположим, что позже мы решили, что было бы лучше заменить параметры x и y класса Shape на объект. Следовательно, нам нужно будет изменить конструктор для всех наших подклассов и аргументов для каждого объекта, который мы создали.

Мы видим, как это легко может стать проблематичным. Мы должны были бы убедиться, что в первый раз мы получили наш дизайн, чтобы избежать изменения. Но это не практично, и мы не должны стремиться к этому. Программа является постоянно развивающейся организацией, и для нас лучше разработчиков, если у нас есть гибкость, чтобы легко вносить изменения. По крайней мере, у нас не должно быть более одного уровня дочерних классов.

Decorator Patter

Декоратор позволяет нам прикрепить свойства к объекту после их создания. Это означает, что мы можем добавлять функциональные возможности без подкласса или заниматься реализацией нашего объекта.

Вместо того, чтобы думать, что круг является формой, мы могли бы использовать класс 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 для нашего основного компонента приложения вызовет рисование по меню, боковой панели и сообщению. Компонент сообщения, в свою очередь, будет вызывать призыв на изображение, заголовок и тело сообщения.

Вот представление того, что веб-страница хотела бы разделить на компоненты:

Этот шаблон не ограничивается созданием представлений. Например, если мы создаем игру, у нас может быть компонент для управления элементами на экране, компонентом для управления аудио и компонентом для управления состоянием игры.

Все это будет внутри компонента, который я называю игровым движком. У игрового движка был бы метод инициализации, который вызывал бы метод initialize для каждого из его подкомпонентов. Это сила в использовании составного шаблона. Вместо того, чтобы иметь дело с отдельными объектами, мы можем рассматривать их как один объект.

Заключение

Наследование позволяет нам повторно использовать код, определяя объект в терминах другого объекта. Шаблон декоратора позволяет нам добавлять обязанности к объекту без изменения исходного кода или подкласса. Комбинированный шаблон моделирует отношения между целыми целями. Эти шаблоны не предназначены для использования в изоляции.

JavaScript стал языком работы в Интернете. Это не лишено кривых обучения, и есть множество фреймворков и библиотек, которые тоже будут вам заняты. Если вы ищете дополнительные ресурсы для изучения или использования в своей работе, посмотрите, что у нас есть на Envato Market.

Их можно комбинировать по своему усмотрению. Представленные мной примеры также не должны рассматриваться как единственное приложение для использования шаблонов. Они просто руководство. Не стесняйтесь использовать свое творчество для их применения. Существует не один способ реализовать их или один вариант использования.