Лекция
Транзитивность — свойство бинарного отношения: бинарное отношение на множестве транзитивно, если для любых трех элементов множества , , выполнение отношений и влечет выполнение отношения :
Одно из важнейших свойств бинарных отношений; по определению транзитивны отношения эквивалетнтности (в частности, равенство), отношения порядка (например, отношение включения множеств), импликация, отношение следования вершин ориентированного графа, отношение параллельности прямых (из и
следует
). В теории чисел транзитивны делимость (если
делится на
, и
делится на
, то
делится на
) и сравнение по модулю.
Транзитивное замыкание — пересечение всех транзитивных отношений, содержащих заданное — наименьшее транзитивное отношение, содержащееся в данном.
Нетранзитивность — отсутствие транзитивности, когда из выполнения и
выполнение
не следует. Нетранзитивно, например, отношение смежности вершин в графе (смежная ко смежной вершина может быть смежной с исходной, а может и не быть). Отношение толерантности — рефлексивное и симметричное отношение, которые может быть нетранзитивным. Если же из выполнения
и
следует невыполнение
, то отношение называется антитранзитивным.
Простая идея
Если внутри множества 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.
Пример транзитивного:
{ ∅, {∅} }
Пример нетранзитивного:
{ {∅} }

Сюръекция, инъекция и биекция не являются прямыми видами транзитивности. Это понятия из теории отображений / функций, а транзитивность множества — свойство вложенности элементов множества.
Но они связаны через более общую идею:
как элементы одного множества соотносятся с элементами другого множества.
Множество A транзитивно, если:
То есть:
если элемент x лежит в A, и внутри x лежит y, то y тоже должен лежать в A.
Пример:
A = { ∅, {∅} }
Тут:
{∅} ∈ A
∅ ∈ {∅}
∅ ∈ A
Поэтому A транзитивно.
Инъекция — это функция, где разные элементы переходят в разные элементы.
Функция инъективна, если:
Проще:
разные входы → разные выходы
Пример:
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 остался свободным
Сюръекция — это функция, которая покрывает все множество назначения.
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
Биекция — это функция, которая одновременно:
инъективна + сюръективна
То есть:
каждому элементу 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
Значит транзитивность множества здесь не участвует.
Натуральные числа можно строить как транзитивные множества:
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
является биекцией между двумя транзитивными множествами.
То есть:
множества могут быть транзитивными, а функции между ними могут быть инъективными, сюръективными или биективными.
Но это разные свойства.
Если у нас есть отношение порядка, например:
≤
оно транзитивно:
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
Транзитивное отношение — это отношение, которое сохраняется при переходе через промежуточный элемент.
Формально:
если 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
Транзитивная зависимость — это зависимость через промежуточный объект.
A зависит от B B зависит от C значит A транзитивно зависит от C
Допустим, твой проект зависит от пакета A:
project → package-a
А package-a зависит от package-b:
package-a → package-b
Тогда твой проект транзитивно зависит от package-b:
project → package-a → package-b
То есть package-b может попасть в проект, хотя ты напрямую его не устанавливал.
Твой проект:
{
"require": {
"monolog/monolog": "^3.0"
}
}
monolog/monolog может зависеть от других пакетов, например от psr/log.
Тогда получается:
your-project → monolog/monolog → psr/log
psr/log — это транзитивная зависимость твоего проекта.
Ты ее явно не указал, но она нужна пакету, который ты используешь.
Ты используешь пакет напрямую:
Project → Laravel
Например:
{
"require": {
"laravel/framework": "^11.0"
}
}
laravel/framework — прямая зависимость.
Laravel внутри себя зависит от других пакетов:
Project → Laravel → Symfony Console
Тогда symfony/console — транзитивная зависимость проекта.
Допустим:
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
Транзитивные зависимости могут создавать скрытую связанность.
Например, плохо:
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
Это уменьшает жесткую связанность.
В нормализации БД есть понятие транзитивной зависимости атрибутов.
Например таблица:
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
Так мы убираем транзитивную зависимость из таблицы заказов.
Многие связи в программировании удобно представлять как граф:
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
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
В ООП наследование тоже транзитивно.
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
Пример:
your-app image → php:8.3-fpm image php:8.3-fpm image → debian base image debian base image → system libraries
Твой Docker-образ транзитивно зависит от системных библиотек базового образа.
Если в базовом образе есть уязвимость, она может затронуть и твой образ, даже если ты ее напрямую не ставил.
Это очень важно в supply chain security.
Например:
your-project → package-a → package-b → vulnerable-package
Ты не устанавливал vulnerable-package напрямую, но он попал в проект через цепочку зависимостей.
Поэтому команды типа:
composer audit npm audit
проверяют не только прямые зависимости, но и транзитивные.
В DDD транзитивные зависимости часто появляются, когда один слой начинает тянуть за собой слишком много деталей.
Плохая цепочка:
Controller → Service → Repository → ORM Model → Database
Если контроллер начинает знать про ORM-модель или SQL-детали, транзитивная зависимость становится фактически прямой.
Лучше держать границы:
Controller → Application Service → Domain Interface Infrastructure Repository → Domain Interface
То есть верхние слои не должны зависеть от деталей нижних слоев напрямую.
Представь:
Проект зависит от 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
Если:
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
Это и есть проблема транзитивных зависимостей.
Закон Деметры не любит такие цепочки:
$order->getCustomer()->getAddress()->getCity();
Потому что объект OrderController теперь зависит не только от Order, но и от внутренней структуры Customer и Address.
Если завтра структура изменится:
Customer больше не хранит Address напрямую Address переименовали в Location City стали хранить как объект CityName
то может сломаться код в контроллере, хотя формально контроллер работал только с заказом.
class OrderController
{
public function show(Order $order): string
{
$city = $order
->getCustomer()
->getAddress()
->getCity();
return 'City: ' . $city;
}
}
Проблема:
OrderController знает слишком много: - что у Order есть Customer - что у Customer есть Address - что у Address есть City
То есть контроллер зависит от цепочки объектов.
Вместо того чтобы контроллер ходил внутрь объекта, можно дать объекту более выразительный метод:
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
спрятаны внутри доменной модели.
Транзитивная зависимость показывает проблему:
A зависит от B B зависит от C значит A косвенно зависит от C
Закон Деметры пытается уменьшить эту проблему:
A не должен напрямую обращаться к C через B
То есть закон Деметры ограничивает доступ к транзитивным зависимостям.
Плохой вариант:
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
Важно: не каждая цепочка методов плохая.
Например, это обычно нормально:
$query->where('status', 'paid')
->orderBy('id', 'desc')
->limit(10);
Почему?
Потому что это fluent interface: методы возвращают тот же объект-запрос или специальный builder.
Тут не происходит перехода по внутренним объектам:
QueryBuilder → QueryBuilder → QueryBuilder
А вот это опаснее:
$order->getCustomer()->getAddress()->getCity();
Потому что каждый вызов возвращает новый внутренний объект:
Order → Customer → Address → City
Признаки нарушения закона Деметры:
$a->getB()->getC()->getD();
или:
$service->repository()->connection()->query();
или:
$user->getProfile()->getSettings()->getTimezone();
Смысл проблемы:
текущий объект знает слишком много о внутреннем устройстве других объектов
Можно запомнить так:
Транзитивная зависимость: A → B → C Нарушение закона Деметры: A сам лезет к C через B Исправление: A просит B выполнить нужное действие
То есть вместо:
$a->getB()->getC()->doSomething();
лучше:
$a->askBToDoSomething();
или:
$b->doSomethingForA();
В объектном стиле это часто называется:
Tell, Don’t Ask
То есть:
не спрашивай объект о его внутренностях, скажи ему, что нужно сделать
Плохой вариант:
$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, а не от всей цепочки объектов.
Закон Деметры помогает сохранять инкапсуляцию.
Без него:
объект открывает наружу свою внутреннюю структуру
С ним:
объект сам управляет своими внутренними объектами
Плохой стиль:
$order->getCustomer()->getAddress()->getCity();
Лучший стиль:
$order->deliveryCity();
или:
$order->shippingAddressCity();
Иногда геттеры нормальны.
Например, 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();
Главная идея:
не ходи по цепочке внутренних объектов; проси ближайший объект выполнить нужное действие.
Да, в целом идея таблицы правильная, но я бы уточнил формулировки:
| Контекст | Пример | Есть транзитивность? | Закон Деметры | Оценка |
|---|---|---|---|---|
| Классы: архитектурный уровень | 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 | Не всегда верно |
Самое важное: транзитивность сама по себе не хорошая и не плохая. Плохой она становится, когда создает скрытую связанность, протекание деталей реализации или неправильные бизнес-выводы.
Комментарии
Оставить комментарий
Разработка программного обеспечения и информационных систем
Термины: Разработка программного обеспечения и информационных систем