Law of Demeter, znany także jako „principle of least knowledge”, jest zasadą programowania obiektowego, która mówi, że obiekt powinien mieć dostęp tylko do swoich bezpośrednich relacji i nie powinien znać wewnętrznej struktury innych obiektów, z którymi współpracuje.
Zasada ta ma na celu zmniejszenie złożoności kodu i zwiększenie elastyczności systemu, poprzez minimalizowanie liczby zależności między różnymi częściami systemu. Według tej zasady, obiekt nie powinien wywoływać metod na innych obiektach, z którymi nie ma bezpośredniego połączenia. Zamiast tego, powinien delegować odpowiedzialność za wykonanie tej metody do innego obiektu, z którym ma bezpośrednią relację.
Przykładowo, jeśli obiekt A współpracuje z obiektami B i C, to zgodnie z zasadą Demeter, powinien wywoływać metody tylko na obiektach B i C bezpośrednio. Nie powinien natomiast wywoływać metod na obiektach, które B i C zawierają w swojej strukturze.
Zasada ta ma na celu zwiększenie modularności i elastyczności systemu, poprzez minimalizowanie liczby zależności między różnymi jego częściami. Dzięki temu łatwiej jest wprowadzać zmiany w jednej części systemu bez wpływu na inne jego części.
Już śpieszę z przykładem w php. Stwórzmy dwie klasy Order i Customer:
class Order { public function __construct(private Customer $customer) { } public function getCustomerName(): string { return $this->customer->getName(); } } class Customer { public function __construct(private string $name) { } public function getName(): string { return $this->name; } } $customer = new Customer("Jan Kowalski"); $order = new Order($customer); echo $order->getCustomerName(); // powinno wyświetlić "Jan Kowalski"
Klasa Order reprezentuje zamówienie, a klasa Customer reprezentuje klienta. Klasa Order ma bezpośrednią relację z klasą Customer poprzez pole $customer. Metoda getCustomerName() w klasie Order deleguje odpowiedzialność za zwrócenie nazwy klienta do metody getName() w klasie Customer, z którą ma bezpośrednią relację. Zasada Demeter jest zachowana, ponieważ klasa Order nie ma dostępu do wewnętrznej struktury obiektu Customer, a jedynie korzysta z jego publicznej metody getName().
Oto kolejny, trochę większy przykład w PHP, który pokazuje zasadę Demeter w kontekście relacji między klasami:
class User { public function __construct( private int $id, private string $name, private string $email, private Account $account ) { } public function getId(): int { return $this->id; } public function getName(): string { return $this->name; } public function getEmail(): string { return $this->email; } public function getAccount(): Account { return $this->account; } } class Account { public function __construct( private int $id, private int $balance, private Transaction $transactions ) { } public function getId(): int { return $this->id; } public function getBalance(): int { return $this->balance; } public function getTransactions(): Transaction { return $this->transactions; } } class Transaction { public function __construct( private int $id, private DateTimeImmutable $date, private int $amount ) { } public function getId(): int { return $this->id; } public function getDate(): DateTimeImmutable { return $this->date; } public function getAmount(): int { return $this->amount; } } class Bank { private User $users = []; public function addUser(User $user): void { $this->users[] = $user; } public function getUserById(int $userId): ?User { foreach ($this->users as $user) { if ($user->getId() == $userId) { return $user; } } return null; } public function deposit(int $userId, int $amount) { $user = $this->getUserById($userId); if (null !== $user) { $account = $user->getAccount(); $balance = $account->getBalance(); $transactions = $account->getTransactions(); $newTransaction = new Transaction(count($transactions) + 1, date('Y-m-d H:i:s'), $amount); $transactions[] = $newTransaction; $balance += $amount; $account->balance = $balance; $account->transactions = $transactions; } } } $bank = new Bank(); $user1 = new User(1, "Jan Kowalski", "[email protected]", new Account(1, 0, [])); $user2 = new User(2, "Adam Nowak", "[email protected]", new Account(2, 0, [])); $bank->addUser($user1); $bank->addUser($user2); $bank->deposit(1, 100); $user1 = $bank->getUserById(1); $account = $user1->getAccount(); echo "Account balance for {$user1->getName()}: {$account->getBalance()}"; $transactions = $account->getTransactions(); echo "{$user1->getName()} transactions:"; foreach ($transactions as $transaction) { echo "Transaction #{$transaction->getId()}: {$transaction->getDate()} {$transaction->getAmount()}"; }
W tym przykładzie klasa Bank zawiera metody do wykonywania operacji bankowych, takich jak wpłaty lub wypłaty. Metoda deposit() przyjmuje ID użytkownika i kwotę, którą należy wpłacić na jego konto. Metoda ta wykorzystuje metodę getUserById() do pobrania obiektu użytkownika o zadanym ID, a następnie korzysta z metody getAccount() użytkownika, aby uzyskać dostęp do jego konta. Metoda ta nie korzysta bezpośrednio z pól obiektu Account, ale zamiast tego wywołuje metody getBalance() i getTransactions() dla uzyskania informacji o saldzie i transakcjach.
Po dokonaniu wpłaty, przykład pobiera użytkownika i wyświetla jego saldo, a następnie pobiera transakcje z jego konta i wyświetla je. Dzięki zasadzie Demeter, klasa Bank nie musi wiedzieć, jak jest zbudowany obiekt Account ani jakie są jego pola, ale może korzystać z jego publicznych metod, aby wykonać operacje bankowe.
Nikt jeszcze nie komentował. Bądź pierwszy!