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", "jan.kowalski@example.com", new Account(1, 0, []));
$user2 = new User(2, "Adam Nowak", "adam.nowak@example.com", 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!