Закон Деметры
Закон Деметры (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.
Преимущества соблюдения Закона Деметры
- Уменьшение связности: Компоненты становятся менее зависимыми друг от друга, что упрощает их модификацию и тестирование.
- Инкапсуляция: Скрываются внутренние детали реализации, что улучшает защиту данных.
- Улучшенная поддержка: Изменения в одном компоненте меньше влияют на другие компоненты, что снижает риск ошибок при изменении кода.
Применение Закона Деметры помогает создавать более чистую, модульную и поддерживаемую архитектуру приложения в Angular и TypeScript.