Теория 📔
August 1

Закон Деметры

Закон Деметры (Law of Demeter), также известный как принцип наименьшего знания, гласит, что модуль (или класс) должен иметь ограниченное знание о других модулях (или классах). Другими словами, объект должен взаимодействовать только с непосредственными зависимостями, а не с внутренними структурами зависимостей.

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

Пример без соблюдения Закона Деметры

Допустим, у нас есть сервис UserService, который предоставляет информацию о пользователе, и сервис AccountService, который обрабатывает информацию об аккаунтах пользователей.

// account.model.ts
export class Account {
  constructor(public id: number, public balance: number) {}
}

// user.model.ts
export class User {
  constructor(public id: number, public name: string, public account: Account) {}
}

// user.service.ts
import { Injectable } from '@angular/core';
import { User } from './user.model';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  private user: User = new User(1, 'John Doe', new Account(123, 1000));

  getUser(): User {
    return this.user;
  }
}

// account.service.ts
import { Injectable } from '@angular/core';
import { UserService } from './user.service';

@Injectable({
  providedIn: 'root',
})
export class AccountService {
  constructor(private userService: UserService) {}

  getAccountBalance(): number {
    // Нарушение Закона Деметры: AccountService знает о внутренней структуре User и Account
    return this.userService.getUser().account.balance;
  }
}

В данном примере AccountService нарушает Закон Деметры, потому что он знает о внутренней структуре User и взаимодействует с Account через User.

Пример с соблюдением Закона Деметры

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

// user.service.ts
import { Injectable } from '@angular/core';
import { User } from './user.model';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  private user: User = new User(1, 'John Doe', new Account(123, 1000));

  getUser(): User {
    return this.user;
  }

  getUserAccountBalance(): number {
    return this.user.account.balance;
  }
}

// account.service.ts
import { Injectable } from '@angular/core';
import { UserService } from './user.service';

@Injectable({
  providedIn: 'root',
})
export class AccountService {
  constructor(private userService: UserService) {}

  getAccountBalance(): number {
    // Соблюдение Закона Деметры: AccountService не знает о внутренней структуре User и Account
    return this.userService.getUserAccountBalance();
  }
}

Теперь AccountService зависит только от методов, предоставляемых UserService, и не знает о внутренней структуре класса User. Это упрощает тестирование и поддержку кода, поскольку изменения во внутренней структуре User не затронут AccountService.

Преимущества соблюдения Закона Деметры

  1. Уменьшение связности: Компоненты становятся менее зависимыми друг от друга, что упрощает их модификацию и тестирование.
  2. Инкапсуляция: Скрываются внутренние детали реализации, что улучшает защиту данных.
  3. Улучшенная поддержка: Изменения в одном компоненте меньше влияют на другие компоненты, что снижает риск ошибок при изменении кода.

Применение Закона Деметры помогает создавать более чистую, модульную и поддерживаемую архитектуру приложения в Angular и TypeScript.