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

В этой статье мы рассмотрим, как применить закон Деметры к нашему коду.

Зачем нам нужна свободная муфта?

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

Каждая единица имеет ограниченные знания о других единицах, поэтому упоминаются только некоторые части других единиц.

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

Таким образом, это означает, что если у нас есть 2 класса A и B, тогда A ссылается только на методы B, на которые нужно ссылаться. Другие участники остаются закрытыми вместе с другими деталями реализации.

Как мы применяем Закон Деметры?

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

Пример классов, которые слишком часто ссылаются друг на друга, следующий:

class PostalCode {
  constructor(postalCode) {
    this.postalCode = postalCode;
  }
  setPostalCode(postalCode) {
    this.postalCode = postalCode;
  }
}
class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }
  getPostalCode() {
    return this.postalCode;
  }
  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  setAddress(address) {
    this.address = new Address(address);
  }
  getAddress() {
    return this.address;
  }
}

У нас есть класс Address, который ссылается на PostalCode, и класс Person, который ссылается на Address и Occupation.

Если что-либо из Address и PostalCode изменится, мы должны изменить класс Person и Address.

Кроме того, мы должны установить почтовый индекс Person, написав:

person.getAddress().getPostalCode().setPostalCode('12345');

Это много цепочек, включающих возвращаемые экземпляры разных классов. Если какой-либо из этих методов изменится, придется переписать всю цепочку.

Вместо этого нам следует объединить все ссылки в один метод следующим образом:

class PostalCode {
  constructor(postalCode) {
    this.postalCode = postalCode;
  }
  setPostalCode(postalCode) {
    this.postalCode = postalCode;
  }
}
class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }
  getPostalCode() {
    return this.postalCode;
  }
  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  setAddress(address) {
    this.address = new Address(address);
  }
  getAddress() {
    return this.address;
  }
  getPostalCode() {
    return this.postalCode;
  }
  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

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

Дело в том, что мы должны знать всю систему, чтобы что-то сделать.

PostalCode не нужно подключать к Address, поскольку их можно изменять индивидуально.

Если мы объединим их вместе, то мы должны знать о Address, прежде чем изменять PostalCode.

В приведенном выше примере показано сцепление, которого можно избежать и должно быть.

Узор фасада

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

Например, мы можем скрыть несколько классов за классом фасада, а затем использовать класс фасада для косвенного взаимодействия с несколькими классами, которые скрываются за классом фасада, следующим образом:

class ClassA {
}
class ClassB {
}
class ClassC {
}
class Facade {
  constructor() {
    this.a = new ClassA();
    this.b = new ClassB();
    this.c = new ClassC();
  }
}
class Foo {
  constructor() {
    this.facade = new Facade();
  }
}

В приведенном выше примере класс Foo ничего не знает о том, что стоит за классом Facade. Класс Facade содержит экземпляры ClassA, ClassB и ClassC.

Он обеспечивает простой интерфейс для сложной системы, состоящей из ClassA, ClassB и ClassC.

Когда изменяется какой-либо из классов, стоящих за Facace, нам просто нужно изменить класс Facade.

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

Это удовлетворяет закону Деметры, потому что мы обращаемся к классу Facade только для того, чтобы делать что-либо с ClassA, ClassB и ClassC. Нам не нужно знать об их реализации.

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

Он устранил необходимость ссылаться на несколько частей сложной системы, поскольку класс Facade предоставляет все, что нам нужно.

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

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

Заключение

Закон Деметры заключается в том, чтобы максимально скрыть реализацию от внешнего кода, чтобы им не приходилось ссылаться на разные части кода, чтобы что-то сделать.

Мы должны создавать только классы, которые разговаривают с тесно связанными классами, а не со всем.

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

Один из хороших способов реализовать законы Закона Деметры - использовать паттерн Фасад. В шаблоне указано, что у нас есть класс фасада, который служит точкой входа для других классов, которые являются частью сложной системы.

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