Закон Деметры
Закон Деметры (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.