Мы можем очистить наш код JavaScript, чтобы нам было легче работать с ними.

В этой статье мы рассмотрим некоторые идеи рефакторинга, которые актуальны для очистки классов JavaScript.

Встроенный класс

Начинаем с 2 классов и скатываем их в один. Это обратное предыдущему рефакторингу.

Например, вместо того, чтобы писать:

class PhoneNumber {
  constructor(phoneNumber) {
    this.phoneNumber = phoneNumber;
  }
}
class Person {
  constructor(name, phoneNumber) {
    this.name = name;
    this.phoneNumber = new PhoneNumber(phoneNumber);
  }
}

Мы перекатываем код для класса PhoneNumber обратно в класс Person следующим образом:

class Person {
  constructor(name, phoneNumber) {
    this.name = name;
    this.phoneNumber = phoneNumber;
  }
}

Мы можем захотеть сделать это, если класс, который мы выделили, не такой сложный.

В приведенном выше коде у нас есть класс PhoneNumber, у которого нет методов, поэтому мы можем просто свернуть его в класс Person, поскольку phoneNumber - это свойство, которое может иметь экземпляр Person.

Скрыть делегата

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

Это уменьшает привязку нашего кода к получению нужного нам элемента.

Например, вместо того, чтобы писать следующее:

class Department {
  constructor(name, deptHead) {
    this.name = name;
    this.deptHead = deptHead;
  }
  getDeptHead() {
    return deptHead;
  }
}
class Person {
  constructor(name, dept) {
    this.name = name;
    this.dept = dept;
  }
  getDept() {
    return this.dept;
  }
}
const deptHead = new Person('jane');
const dept = new Department('customer service', deptHead);
const employee = new Person('joe', dept);
const manager = employee.getDept().getDeptHead();

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

Мы можем написать следующее:

class Department {
  constructor(name, deptHead) {
    this.name = name;
    this.deptHead = deptHead;
  }
}
class Person {
  constructor(name, dept) {
    this.name = name;
    this.dept = dept;
  }
  getDept() {
    return this.dept;
  }
  getDeptHead() {
    return this.dept.deptHead;
  }
}
const deptHead = new Person('jane');
const dept = new Department('customer service', deptHead);
const employee = new Person('joe', dept);
const manager = employee.getDeptHead();

Все, что мы сделали, - это добавили метод getDeptHead к person, чтобы получить руководителя отдела напрямую из отдела.

Тогда нам не нужно сначала получать отдел, чтобы получить руководителя, что уменьшает взаимосвязь между классами Person и Department.

Внедрить иностранный метод

Мы можем добавить к классу внешний метод, который нельзя изменить напрямую, добавив к нему методы с помощью Object.assign.

Например, мы можем написать следующее:

class Foo {
  //...
}
const mixin = {
  bar() {
    //...
  }
}
Object.assign(Foo.prototype, mixin)

В приведенном выше коде мы добавили метод экземпляра bar в Foo, вызвав Object.assign, поскольку Foo - это класс, который мы не можем изменить.

Ввести локальное расширение

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

Например, мы можем написать следующее:

class Foo {
  //...
}
class Bar extends Foo {
  bar() {
    //...
  }
}

В приведенном выше коде мы создали подкласс Foo, у которого есть методы, которые мы хотим вызвать, где Foo - это класс, который мы не можем изменить.

Теперь нам не нужно изменять Foo, добавляя что-то в его прототип.

Самоинкапсулируемое поле

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

Например, вместо того, чтобы писать следующее:

class Counter {
  inRange(arg) {
    return arg >= this._low && arg <= this._high;
  }
}

Мы пишем:

class Counter {
  inRange(arg) {
    return arg >= low() && arg <= high();
  }
  get low() {
    return this._low;
  }
  get high() {
    return this._high;
  }
}

Это позволяет нам инкапсулировать поля, и мы также можем применять к ним другие операции без доступа к полям high и low и применения операций к ним напрямую.

Заключение

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

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

Мы можем инкапсулировать поля, чтобы мы могли получить значение и что-то сделать перед ним, прежде чем значение, которое мы вернули.

Заметка от команды Plain English

Plain English только что запустил канал на YouTube! Мы будем рады, если вы поддержите нас, подписавшись сейчас!