Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

Транзитивность отношений и множеств в программировании

Лекция



Транзитивность — свойство бинарного отношения: бинарное отношение на множестве X транзитивно, если для любых трех элементов множества a, b, c выполнение отношений ab и bc влечет выполнение отношения ac:

Транзитивность отношений и множеств в программировании.

Одно из важнейших свойств бинарных отношений; по определению транзитивны отношения эквивалетнтности (в частности, равенство), отношения порядка (например, отношение включения множеств), импликация, отношение следования вершин ориентированного графа, отношение параллельности прямых (из a||bТранзитивность отношений и множеств в программировании и b||cТранзитивность отношений и множеств в программировании следует a||cТранзитивность отношений и множеств в программировании). В теории чисел транзитивны делимость (если aТранзитивность отношений и множеств в программировании делится на bТранзитивность отношений и множеств в программировании, и bТранзитивность отношений и множеств в программировании делится на cТранзитивность отношений и множеств в программировании, то aТранзитивность отношений и множеств в программировании делится на cТранзитивность отношений и множеств в программировании) и сравнение по модулю.

Транзитивное замыкание — пересечение всех транзитивных отношений, содержащих заданное — наименьшее транзитивное отношение, содержащееся в данном.

Нетранзитивность — отсутствие транзитивности, когда из выполнения abТранзитивность отношений и множеств в программировании и bcТранзитивность отношений и множеств в программировании выполнение acТранзитивность отношений и множеств в программировании не следует. Нетранзитивно, например, отношение смежности вершин в графе (смежная ко смежной вершина может быть смежной с исходной, а может и не быть). Отношение толерантности — рефлексивное и симметричное отношение, которые может быть нетранзитивным. Если же из выполнения abТранзитивность отношений и множеств в программировании и bcТранзитивность отношений и множеств в программировании следует невыполнение acТранзитивность отношений и множеств в программировании, то отношение называется антитранзитивным.

Простая идея

Если внутри множества A лежит какое-то множество x, то все элементы этого x тоже должны лежать в A.

То есть A как бы «содержит не только множества, но и их внутренности».

Пример 1: транзитивное множество

Пусть:

A = { ∅, {∅} }

Проверим.

Элементы A:

∅
{∅}

Теперь смотрим элементы этих элементов:

∅ не содержит элементов
{∅} содержит ∅

А ∅ действительно находится в A.

Значит:

A = { ∅, {∅} }

является транзитивным множеством.

Пример 2: нетранзитивное множество

Пусть:

B = { {∅} }

Элемент множества B:

{∅}

Но внутри {∅} лежит:

А ∅ не лежит в B.

То есть:

{∅} ∈ B
∅ ∈ {∅}
но ∅ ∉ B

Значит B не транзитивно.

Пример 3: обычное множество чисел

Пусть:

C = {1, 2, 3}

В обычной школьной математике числа 1, 2, 3 не рассматриваются как множества. Поэтому вопрос транзитивности здесь обычно не применяют напрямую.

Но если понимать числа как множества фон Неймана:

1 = {0}
2 = {0, 1}
3 = {0, 1, 2}

то множество:

C = {1, 2, 3}

не транзитивно, потому что:

1 ∈ C
0 ∈ 1
но 0 ∉ C

Чтобы оно стало транзитивным, нужно добавить 0:

{0, 1, 2, 3}

Интуитивная схема

A транзитивно:

если x ∈ A
и y ∈ x
то y ∈ A

Пример:

A = { ∅, {∅} }

{∅} ∈ A
∅ ∈ {∅}
∅ ∈ A

Все хорошо.

А вот:

B = { {∅} }

{∅} ∈ B
∅ ∈ {∅}
∅ ∉ B

Значит транзитивности нет.

Отличие транзитивности множества от транзитивности отношения

Важно не путать:

Транзитивность отношения:

a R b и b R c ⇒ a R c

Например:

если a > b и b > c, то a > c

Транзитивность множества:

x ∈ A и y ∈ x ⇒ y ∈ A

То есть здесь речь не об отношении между любыми объектами, а о структуре вложенности элементов внутри множества.

таким образом

Множество A транзитивно, если оно содержит элементы своих элементов.

если x лежит в A,
то все, что лежит внутри x,
тоже должно лежать в A.

Пример транзитивного:

{ ∅, {∅} }

Пример нетранзитивного:

{ {∅} }
Транзитивность отношений и множеств в программировании
как связаны сьюрекция биекция и иьекция с транзитивностью

Сюръекция, инъекция и биекция не являются прямыми видами транзитивности. Это понятия из теории отображений / функций, а транзитивность множества — свойство вложенности элементов множества.

Но они связаны через более общую идею:
как элементы одного множества соотносятся с элементами другого множества.

1. Транзитивность множества

Множество A транзитивно, если:

𝑥𝐴 и 𝑦𝑥𝑦𝐴

То есть:

если элемент x лежит в A,
и внутри x лежит y,
то y тоже должен лежать в A.

Пример:

A = { ∅, {∅} }

Тут:

{∅} ∈ A
∅ ∈ {∅}
∅ ∈ A

Поэтому A транзитивно.

2. Инъекция

Инъекция — это функция, где разные элементы переходят в разные элементы.

𝑓:𝐴𝐵

Функция инъективна, если:

𝑎1𝑎2𝑓(𝑎1)𝑓(𝑎2)

Проще:

разные входы → разные выходы

Пример:

A = {1, 2, 3}
B = {a, b, c, d}

f(1) = a
f(2) = b
f(3) = c

Это инъекция, потому что никакие два элемента из A не попали в один и тот же элемент B.

Схема:

1 → a
2 → b
3 → c

d остался свободным

3. Сюръекция

Сюръекция — это функция, которая покрывает все множество назначения.

𝑓:𝐴𝐵f:A→B

Функция сюръективна, если каждый элемент из B имеет прообраз в A.

Проще:

каждый элемент B кем-то достигнут из A

Пример:

A = {1, 2, 3, 4}
B = {a, b, c}

f(1) = a
f(2) = b
f(3) = c
f(4) = c

Это сюръекция, потому что a, b, c все покрыты.

Схема:

1 → a
2 → b
3 → c
4 → c

4. Биекция

Биекция — это функция, которая одновременно:

инъективна + сюръективна

То есть:

каждому элементу A соответствует ровно один элемент B,
и каждый элемент B используется ровно один раз.

Пример:

A = {1, 2, 3}
B = {a, b, c}

f(1) = a
f(2) = b
f(3) = c

Схема:

1 → a
2 → b
3 → c

Это биекция: нет повторов и нет свободных элементов в B.

- Отображение f:x->y называется СЮРЪЕКЦИЕЙ, если Ay∈Y ∃ x∈X:y=f(x). Тогда y - образ, x - прообраз y.
- Отображение f:x->y называется ИНЪЕКЦИЕЙ, если x1 ≠ x2 => f(x1)≠f(x2), те разные элементы множества X переводятся в разные элементы множества Y.
или f(x1)≠f(x2) => x1=x2
- Отображение f:x->y называется БИЕКЦИЕЙ, если оно одновременно сюръективно и инъективно. При биективном отражении каждому элементу одного множества соответсвует ровно один элемент другого множества, при этом определено обратное отображение, которое обладает теми же свойствами.
Транзитивность отношений и множеств в программировании

Как это связано с транзитивностью?

Главное отличие

Транзитивность множества говорит о связи:

элемент → элемент элемента

Инъекция, сюръекция и биекция говорят о связи:

элемент одного множества → элемент другого множества

То есть:

Транзитивность:       x ∈ A, y ∈ x
Функции:              f(x) = y

Связь через отношения

Функцию можно рассматривать как отношение между элементами двух множеств.

Например:

f(1) = a

можно представить как пару:

(1, a)

Тогда функция — это множество пар:

f = { (1, a), (2, b), (3, c) }

А транзитивность тоже часто изучается через отношения.

Например, отношение > транзитивно:

5 > 3
3 > 1
значит 5 > 1

То есть в отношениях транзитивность означает:

𝑎𝑅𝑏 и 𝑏𝑅𝑐𝑎𝑅𝑐

Но инъекция/сюръекция/биекция сами по себе не требуют транзитивности

Функция может быть:

инъективной, но не связанной с транзитивным множеством
сюръективной, но не транзитивной
биективной, но не транзитивной

Например:

f: {1, 2, 3} → {a, b, c}
f(1)=a, f(2)=b, f(3)=c

Это биекция, но тут вообще не говорится о том, что:

если y ∈ x, то y ∈ A

Значит транзитивность множества здесь не участвует.

Где они реально могут встретиться вместе?

1. В теории множеств

Натуральные числа можно строить как транзитивные множества:

0 = ∅
1 = {0}
2 = {0, 1}
3 = {0, 1, 2}

Тогда каждое число является транзитивным множеством.

Например:

3 = {0, 1, 2}

Если:

2 ∈ 3
1 ∈ 2

то:

1 ∈ 3

А функции между такими числами могут быть инъекциями, сюръекциями или биекциями.

Например:

A = 3 = {0, 1, 2}
B = 3 = {0, 1, 2}

Функция:

f(0)=1
f(1)=2
f(2)=0

является биекцией между двумя транзитивными множествами.

То есть:

множества могут быть транзитивными,
а функции между ними могут быть инъективными, сюръективными или биективными.

Но это разные свойства.

2. Через порядок и отношения

Если у нас есть отношение порядка, например:

оно транзитивно:

a ≤ b и b ≤ c ⇒ a ≤ c

А функции могут сохранять этот порядок.

Например:

𝑓(𝑎)𝑓(𝑏)f(a)≤f(b)

если:

𝑎𝑏a≤b

Такие функции называются монотонными.

Тут уже появляются вместе:

транзитивное отношение порядка
+
функция между множествами
+
свойства функции

Но инъекция/сюръекция/биекция все равно отвечают за другое: как функция покрывает элементы и не склеивает ли она их.

Понятие Про что говорит Простая идея
Транзитивность множества Про вложенность элементов Элементы элементов тоже внутри множества
Транзитивность отношения Про цепочку связей Если aRb и bRc, то aRc
Инъекция Про функцию Разные входы дают разные выходы
Сюръекция Про функцию Все множество назначения покрыто
Биекция Про функцию Один-к-одному и все покрыто

таким образом

Транзитивность — это про цепочку:
A содержит x, x содержит y ⇒ A содержит y

Инъекция/сюръекция/биекция — это про отображение:
элементы A переходят в элементы B

Они могут использоваться в одной теории, но описывают разные стороны структуры множеств.

что такое транзитивность зависимостией и отношений в программированиии с примерами

В программировании транзитивность обычно означает:

если есть связь A → B, и есть связь B → C, то можно вывести связь A → C.

То есть связь передается по цепочке.

A связано с B
B связано с C
значит A связано с C

1. транзитивность отношений

Транзитивное отношение — это отношение, которое сохраняется при переходе через промежуточный элемент.

Формально:

если a R b и b R c, то a R c

Где R — это какое-то отношение.

Пример: отношение «больше»

10 > 5
5 > 2
значит 10 > 2

Отношение > транзитивно.

Пример в коде: роли и права

Допустим, у нас есть иерархия ролей:

admin > manager
manager > user

Тогда транзитивно:

admin > user

То есть если admin наследует права manager, а manager наследует права user, то admin получает и права user.

$roles = [
    'admin' => ['manager'],
    'manager' => ['user'],
    'user' => [],
];

function hasRoleAccess(array $roles, string $from, string $target): bool
{
    if ($from === $target) {
        return true;
    }

    foreach ($roles[$from] ?? [] as $childRole) {
        if (hasRoleAccess($roles, $childRole, $target)) {
            return true;
        }
    }

    return false;
}

var_dump(hasRoleAccess($roles, 'admin', 'user')); // true

Здесь связь такая:

admin → manager → user

И функция выводит:

admin → user

2. Транзитивность зависимостей

Транзитивная зависимость — это зависимость через промежуточный объект.

A зависит от B
B зависит от C
значит A транзитивно зависит от C

Пример: зависимости Composer / npm

Допустим, твой проект зависит от пакета A:

project → package-a

А package-a зависит от package-b:

package-a → package-b

Тогда твой проект транзитивно зависит от package-b:

project → package-a → package-b

То есть package-b может попасть в проект, хотя ты напрямую его не устанавливал.

Пример в composer.json

Твой проект:

{
  "require": {
    "monolog/monolog": "^3.0"
  }
}

monolog/monolog может зависеть от других пакетов, например от psr/log.

Тогда получается:

your-project → monolog/monolog → psr/log

psr/log — это транзитивная зависимость твоего проекта.

Ты ее явно не указал, но она нужна пакету, который ты используешь.

3. Прямые и транзитивные зависимости

Прямая зависимость

Ты используешь пакет напрямую:

Project → Laravel

Например:

{
  "require": {
    "laravel/framework": "^11.0"
  }
}

laravel/framework — прямая зависимость.

Транзитивная зависимость

Laravel внутри себя зависит от других пакетов:

Project → Laravel → Symfony Console

Тогда symfony/console — транзитивная зависимость проекта.

4. Пример в коде: классы

Допустим:

class PaymentController
{
    public function __construct(
        private PaymentService $paymentService
    ) {}
}

class PaymentService
{
    public function __construct(
        private CheckoutClient $checkoutClient
    ) {}
}

class CheckoutClient
{
    public function request(): void
    {
        // HTTP-запрос к платежной системе
    }
}

Зависимости:

PaymentController → PaymentService
PaymentService → CheckoutClient

Значит:

PaymentController → CheckoutClient

Но важно: PaymentController зависит от CheckoutClient не напрямую, а транзитивно.

Схема:

PaymentController
        ↓
PaymentService
        ↓
CheckoutClient

5. Почему это важно в архитектуре

Транзитивные зависимости могут создавать скрытую связанность.

Например, плохо:

class PaymentController
{
    public function pay(): void
    {
        $client = new CheckoutClient();
        $client->request();
    }
}

Тут контроллер напрямую знает о низкоуровневом клиенте.

Лучше:

interface PaymentGatewayInterface
{
    public function charge(int $amount): void;
}

class CheckoutPaymentGateway implements PaymentGatewayInterface
{
    public function charge(int $amount): void
    {
        // работа с Checkout.com
    }
}

class PaymentService
{
    public function __construct(
        private PaymentGatewayInterface $gateway
    ) {}

    public function pay(int $amount): void
    {
        $this->gateway->charge($amount);
    }
}

Теперь PaymentService зависит не от конкретного CheckoutPaymentGateway, а от интерфейса:

PaymentService → PaymentGatewayInterface
CheckoutPaymentGateway → PaymentGatewayInterface

Это уменьшает жесткую связанность.

6. Транзитивные зависимости в базах данных

В нормализации БД есть понятие транзитивной зависимости атрибутов.

Например таблица:

orders
------------------------------------------------
order_id | customer_id | customer_name | city
------------------------------------------------
1        | 10          | Ivan          | Kyiv
2        | 10          | Ivan          | Kyiv

Зависимости:

order_id → customer_id
customer_id → customer_name
customer_id → city

Значит:

order_id → customer_name
order_id → city

Но это транзитивная зависимость через customer_id.

Проблема: данные клиента повторяются в заказах.

Лучше разделить:

orders
-----------------------
order_id | customer_id
-----------------------
1        | 10
2        | 10

customers
------------------------------
customer_id | customer_name | city
------------------------------
10          | Ivan          | Kyiv

Так мы убираем транзитивную зависимость из таблицы заказов.

7. Транзитивность в графах

Многие связи в программировании удобно представлять как граф:

A → B → C

Если нас интересует не только прямое соединение, но и достижимость, мы строим транзитивное замыкание.

Транзитивное замыкание показывает все вершины, до которых можно добраться.

Например:

A → B
B → C
C → D

Тогда транзитивно:

A → C
A → D
B → D

Пример: зависимости модулей

AuthModule → UserModule
UserModule → DatabaseModule
DatabaseModule → ConfigModule

Тогда:

AuthModule транзитивно зависит от DatabaseModule
AuthModule транзитивно зависит от ConfigModule
UserModule транзитивно зависит от ConfigModule

8. Пример на JavaScript: поиск транзитивных зависимостей

const deps = {
  app: ['auth'],
  auth: ['user'],
  user: ['db'],
  db: ['config'],
  config: []
};

function collectDeps(module, visited = new Set()) {
  for (const dep of deps[module] || []) {
    if (!visited.has(dep)) {
      visited.add(dep);
      collectDeps(dep, visited);
    }
  }

  return visited;
}

console.log([...collectDeps('app')]);
// ['auth', 'user', 'db', 'config']

Здесь:

app напрямую зависит от auth
app транзитивно зависит от user, db, config

9. Транзитивность в наследовании

В ООП наследование тоже транзитивно.

class Animal {}
class Mammal extends Animal {}
class Dog extends Mammal {}

Связь:

Dog → Mammal
Mammal → Animal

Значит:

Dog → Animal

То есть объект Dog также является Animal.

$dog = new Dog();

var_dump($dog instanceof Dog);    // true
var_dump($dog instanceof Mammal); // true
var_dump($dog instanceof Animal); // true

10. Транзитивность в Docker

Пример:

your-app image → php:8.3-fpm image
php:8.3-fpm image → debian base image
debian base image → system libraries

Твой Docker-образ транзитивно зависит от системных библиотек базового образа.

Если в базовом образе есть уязвимость, она может затронуть и твой образ, даже если ты ее напрямую не ставил.

11. Транзитивность в безопасности

Это очень важно в supply chain security.

Например:

your-project → package-a → package-b → vulnerable-package

Ты не устанавливал vulnerable-package напрямую, но он попал в проект через цепочку зависимостей.

Поэтому команды типа:

composer audit
npm audit

проверяют не только прямые зависимости, но и транзитивные.

12. Транзитивность в DDD / архитектуре

В DDD транзитивные зависимости часто появляются, когда один слой начинает тянуть за собой слишком много деталей.

Плохая цепочка:

Controller → Service → Repository → ORM Model → Database

Если контроллер начинает знать про ORM-модель или SQL-детали, транзитивная зависимость становится фактически прямой.

Лучше держать границы:

Controller → Application Service → Domain Interface
Infrastructure Repository → Domain Interface

То есть верхние слои не должны зависеть от деталей нижних слоев напрямую.

13. Простая аналогия

Представь:

Проект зависит от Laravel.
Laravel зависит от Symfony.
Symfony зависит от polyfill-пакета.

Тогда:

Проект транзитивно зависит от Symfony и polyfill-пакета.

Хотя в composer.json ты мог написать только Laravel.

Коротко

Транзитивность отношений:

A связано с B
B связано с C
значит A связано с C

Транзитивность зависимостей:

A зависит от B
B зависит от C
значит A зависит от C

В программировании это встречается в:

пакетах Composer/npm
классах и сервисах
наследовании ООП
базах данных
модулях
Docker-образах
правах доступа
графах зависимостей
безопасности
архитектуре приложения

Главная мысль:

транзитивная зависимость — это скрытая зависимость, которая появляется через цепочку других зависимостей.

как связана транзитивность зависимостей с законом дематры
Транзитивность отношений и множеств в программировании

Транзитивность зависимостей напрямую связана с законом Деметры.

Закон Деметры говорит:

объект должен общаться только со своими “ближайшими друзьями”, а не с объектами, которые спрятаны внутри них.

Проще:

Не лезь через цепочку:
A → B → C → D

Работай только с ближайшим объектом:
A → B

1. Что такое транзитивная зависимость

Если:

A зависит от B
B зависит от C

то:

A транзитивно зависит от C

То есть A вроде бы не использует C напрямую, но все равно становится связанным с ним через B.

Пример:

class OrderController
{
    public function show(Order $order): string
    {
        return $order->getCustomer()->getAddress()->getCity();
    }
}

Здесь цепочка:

OrderController → Order → Customer → Address → City

OrderController напрямую вызывает только $order, но фактически он знает внутреннюю структуру:

у Order есть Customer
у Customer есть Address
у Address есть City

Это и есть проблема транзитивных зависимостей.

2. Как здесь нарушается закон Деметры

Закон Деметры не любит такие цепочки:

$order->getCustomer()->getAddress()->getCity();

Потому что объект OrderController теперь зависит не только от Order, но и от внутренней структуры Customer и Address.

Если завтра структура изменится:

Customer больше не хранит Address напрямую
Address переименовали в Location
City стали хранить как объект CityName

то может сломаться код в контроллере, хотя формально контроллер работал только с заказом.

3. Плохой вариант

class OrderController
{
    public function show(Order $order): string
    {
        $city = $order
            ->getCustomer()
            ->getAddress()
            ->getCity();

        return 'City: ' . $city;
    }
}

Проблема:

OrderController знает слишком много:
- что у Order есть Customer
- что у Customer есть Address
- что у Address есть City

То есть контроллер зависит от цепочки объектов.

4. Лучше по закону Деметры

Вместо того чтобы контроллер ходил внутрь объекта, можно дать объекту более выразительный метод:

class Order
{
    public function __construct(
        private Customer $customer
    ) {}

    public function getCustomerCity(): string
    {
        return $this->customer->getCity();
    }
}

class Customer
{
    public function __construct(
        private Address $address
    ) {}

    public function getCity(): string
    {
        return $this->address->getCity();
    }
}

Теперь контроллер:

class OrderController
{
    public function show(Order $order): string
    {
        return 'City: ' . $order->getCustomerCity();
    }
}

Связь стала короче:

OrderController → Order

А детали:

Order → Customer → Address

спрятаны внутри доменной модели.

5. Главная связь

Транзитивная зависимость показывает проблему:

A зависит от B
B зависит от C
значит A косвенно зависит от C

Закон Деметры пытается уменьшить эту проблему:

A не должен напрямую обращаться к C через B

То есть закон Деметры ограничивает доступ к транзитивным зависимостям.

6. Пример с сервисами

Плохой вариант:

class PaymentController
{
    public function pay(PaymentService $paymentService): void
    {
        $paymentService
            ->getGateway()
            ->getHttpClient()
            ->post('/payments');
    }
}

Цепочка:

PaymentController → PaymentService → Gateway → HttpClient

Контроллер теперь знает, что внутри PaymentService есть Gateway, а внутри Gateway есть HttpClient.

Это нарушение закона Деметры.

Лучше

class PaymentController
{
    public function pay(PaymentService $paymentService): void
    {
        $paymentService->pay(1000);
    }
}

class PaymentService
{
    public function __construct(
        private PaymentGatewayInterface $gateway
    ) {}

    public function pay(int $amount): void
    {
        $this->gateway->charge($amount);
    }
}

Теперь контроллер знает только:

PaymentController → PaymentService

А не всю внутреннюю цепочку:

Gateway → HttpClient → API

7. Закон Деметры — это не запрет на все цепочки

Важно: не каждая цепочка методов плохая.

Например, это обычно нормально:

$query->where('status', 'paid')
      ->orderBy('id', 'desc')
      ->limit(10);

Почему?

Потому что это fluent interface: методы возвращают тот же объект-запрос или специальный builder.

Тут не происходит перехода по внутренним объектам:

QueryBuilder → QueryBuilder → QueryBuilder

А вот это опаснее:

$order->getCustomer()->getAddress()->getCity();

Потому что каждый вызов возвращает новый внутренний объект:

Order → Customer → Address → City

8. Как понять, что есть проблема

Признаки нарушения закона Деметры:

$a->getB()->getC()->getD();

или:

$service->repository()->connection()->query();

или:

$user->getProfile()->getSettings()->getTimezone();

Смысл проблемы:

текущий объект знает слишком много о внутреннем устройстве других объектов

9. Архитектурная формула

Можно запомнить так:

Транзитивная зависимость:
A → B → C

Нарушение закона Деметры:
A сам лезет к C через B

Исправление:
A просит B выполнить нужное действие

То есть вместо:

$a->getB()->getC()->doSomething();

лучше:

$a->askBToDoSomething();

или:

$b->doSomethingForA();

В объектном стиле это часто называется:

Tell, Don’t Ask

То есть:

не спрашивай объект о его внутренностях,
скажи ему, что нужно сделать

10. Пример: корзина и сумма заказа

Плохой вариант:

$total = $cart
    ->getItems()
    ->first()
    ->getProduct()
    ->getPrice()
    ->getAmount();

Код знает слишком много:

Cart содержит Items
Item содержит Product
Product содержит Price
Price содержит Amount

Лучше:

$total = $cart->calculateTotal();

А внутри:

class Cart
{
    private array $items = [];

    public function calculateTotal(): int
    {
        $total = 0;

        foreach ($this->items as $item) {
            $total += $item->totalPrice();
        }

        return $total;
    }
}

class CartItem
{
    public function __construct(
        private Product $product,
        private int $quantity
    ) {}

    public function totalPrice(): int
    {
        return $this->product->priceAmount() * $this->quantity;
    }
}

Теперь внешний код зависит только от Cart, а не от всей цепочки объектов.

11. Связь с инкапсуляцией

Закон Деметры помогает сохранять инкапсуляцию.

Без него:

объект открывает наружу свою внутреннюю структуру

С ним:

объект сам управляет своими внутренними объектами

Плохой стиль:

$order->getCustomer()->getAddress()->getCity();

Лучший стиль:

$order->deliveryCity();

или:

$order->shippingAddressCity();

12. Важный нюанс

Иногда геттеры нормальны.

Например, DTO:

class UserDTO
{
    public function __construct(
        public string $name,
        public string $email
    ) {}
}

Для DTO нормально читать данные:

$userDto->email;

Но для доменных объектов и сервисов длинные цепочки часто означают плохую инкапсуляцию.

Коротко

Связь такая:

Транзитивная зависимость показывает:
A косвенно зависит от C через B.

Закон Деметры говорит:
A не должен лезть к C через B.

То есть закон Деметры нужен, чтобы уменьшить вред от транзитивных зависимостей.

Главный плохой признак:

$a->getB()->getC()->getD();

Лучше:

$a->doWhatINeed();

или:

$b->doItForMe();

Главная идея:

не ходи по цепочке внутренних объектов; проси ближайший объект выполнить нужное действие.

сделай таблицу из этих данных

Да, в целом идея таблицы правильная, но я бы уточнил формулировки:

  1. “Архитектурная транзитивность” не всегда хорошо — она нормальна, если зависимости направлены правильно и слои не протекают наружу.
  2. “Объектная транзитивность” не всегда плохо — плохо именно когда объект ходит по цепочке внутренних объектов и нарушает Закон Деметры.
  3. requires transitive в Java — это не просто “хорошо”, а инструмент, который полезен для API-модулей, но может раскрывать лишние зависимости.
  4. FK-цепочка в БД сама по себе нормальна; проблема появляется при чрезмерной связности, сложных JOIN-цепочках и транзитивных функциональных зависимостях внутри одной таблицы.
Контекст Пример Есть транзитивность? Закон Деметры Оценка
Классы: архитектурный уровень Controller → Service → Repository → DB Да Обычно не нарушен, если каждый слой общается только со следующим слоем Хорошо — нормальная архитектурная цепочка
Классы: объектный уровень $service->getRepo()->getDb()->query() Да Нарушен: объект обращается к “другу друга” Плохо — появляется избыточная связанность
Модули Java requires transitive some.module; Да Не относится напрямую к Закону Деметры Хорошо для публичных API-зависимостей, но плохо, если раскрывает лишнее
Базы данных: FK-цепочка orders → customers → countries Да Не относится напрямую к Закону Деметры Нормально, если связи контролируемые; плохо при чрезмерной вложенности и сложных JOIN
Базы данных: транзитивная функциональная зависимость order_id → customer_id → customer_name в одной таблице orders Да Не относится напрямую к Закону Деметры Плохо для нормализации — признак нарушения 3НФ
Maven / Gradle / Composer / npm project → package-a → package-b Да Не нарушен, это уровень зависимостей пакетов Нормально как механизм, но опасно при конфликтах версий, уязвимостях и скрытых зависимостях
Игровая логика победил A над B, B над C, значит A победил C Не всегда Не применимо Плохо, если механически выводить результат — может нарушить бизнес-логику
Права доступа / роли Admin → Manager → User Да Обычно не нарушен Хорошо, если иерархия ролей явно задана
Наследование ООП Dog extends Mammal, Mammal extends Animal Да Не нарушен сам по себе Нормально, если наследование отражает реальное is-a отношение
Длинная цепочка геттеров $order->getCustomer()->getAddress()->getCity() Да Нарушен Плохо для доменной модели; лучше $order->deliveryCity()

Исправленная главная мысль

Лучше формулировать не так:

архитектурная транзитивность = хорошо
объектная транзитивность = плохо

А так:

Транзитивность на уровне архитектуры допустима,
если она скрыта за границами слоев и интерфейсов.

Транзитивность на уровне объектов опасна,
если код начинает ходить по внутренним объектам через цепочки вызовов.
Транзитивность отношений и множеств в программировании

Коротко

Тип Пример Итог
Архитектурная цепочка Controller → Service → Repository Нормально
Нарушение Деметры $controller->getService()->getRepo()->getDb() Плохо
Пакетная зависимость project → framework → library Нормально, но надо контролировать
Бизнес-логика A > B, B > C, значит A > C Не всегда верно

Самое важное: транзитивность сама по себе не хорошая и не плохая. Плохой она становится, когда создает скрытую связанность, протекание деталей реализации или неправильные бизнес-выводы.

создано: 2026-05-06
обновлено: 2026-05-11
1



Помог ли вам этот ответ?
Нажмите оценку и напишите коротко почему. Так мы сможем сделать следующие ответы точнее и полезнее.
Насколько вы довольны ответом?
Ваш отзыв напрямую влияет на качество следующих подсказок и ответов.


Поделиться:
Пожаловаться

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей

Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Разработка программного обеспечения и информационных систем

Термины: Разработка программного обеспечения и информационных систем