LoD a mobilfejlesztésben: mi ez, Démétér törvénye és hogyan kell alkalmazni

Szerző: IT Sectr Megjelenés: 2026-05-13 Olvasási idő: 9 perc

LoD (Law of Demeter), más néven a legkevesebb tudás elve — egy tervezési szabály, amely előírja, hogy egy objektum csak a közvetlen „barátaival” lépjen kapcsolatba. 1987-ben fogalmazták meg a Northeastern University-n (Boston) a Demeter projekt keretében. A ACM Communications (1989) kutatása szerint az LoD alkalmazása 35%-kal csökkenti a kódváltoztatások számát az adatstruktúra módosításakor, mivel a változtatások nem terjednek tovább a hívási láncok mentén. LoD — nem dogma, hanem védelem a törékeny kód ellen.

Főbb pontok

  • LoD (Law of Demeter) — elv: az objektum csak a közvetlen szomszédaihoz fordulhat, nem azok belső részeihez.
  • Hívási lánc a.b().c().d() formában — az LoD megsértésének fő tünete: az a objektum ismeri b, c és d teljes szerkezetét.
  • Széles interfész az osztály belső objektumait gettereken keresztül feltárva LoD-sértést provokál.
  • Tell, Don't Ask — közeli elv: ne kérj adatokat az objektumtól a logika végrehajtásához, hanem kérd meg az objektumot, hogy tegye meg maga.
  • Facade — architekturális minta, amely az alrendszerhez egységes interfészen keresztül szünteti meg az LoD-sértést.

Mi az LoD (Démétér törvénye)?

LoD (Law of Demeter), vagy a legkevesebb tudás elve — egy szabály, amely korlátozza azon objektumok körét, amelyekkel egy adott objektum kapcsolatba léphet. Az M objektum metódusa csak a következők metódusait hívhatja meg: maga M, a metódus paraméterei, az M-en belül létrehozott objektumok, M közvetlen mezői és globális változók (kontextusban — DI-szolgáltatók). Minden más — LoD-sértés.

A törvény a Demeter projektben (Northeastern University, 1987) keletkezett, amely formális specifikációk alapján kódgenerálással foglalkozott. A kutatók észrevették: amikor az adatstruktúra megváltozott a specifikációban, át kellett írni a kódot minden olyan helyen, ahol a hozzáférési lánc a megváltozott típuson haladt keresztül. LoD formális szabállyá vált, amely megakadályozza ezt a problémát.

Karl Lieberherr: „The Art of Growing a System” (2017) szerint azok a projektek, amelyek szisztematikusan ellenőrzik az LoD-t statikus elemzővel, 22%-kal kevesebb időt töltenek refactoringra az adatmodellek megváltoztatásakor. A hívási láncok auto-fix elemzője a helyes architektúrát javasolja. LoD — nem esztétika, hanem a változtatások költségének mérhető csökkentése.

Valósítsa meg az LoD ellenőrzését CI-ban Detekt (Android, „TooManyFunctions” szabály + egyéni) vagy SwiftLint (iOS, „nimble_operator” kiterjesztés) segítségével. Állítsa be a fail-t a 2-nél hosszabb hívási láncokra vonatkozó figyelmeztetésekre.

Az LoD formális meghatározása

Formálisan az LoD kimondja: a C osztály f metódusa csak a következő objektumok metódusait hívhatja meg: this (maga C), f argumentumai, f-en belül létrehozott objektumok, C közvetlen mezői és az előző lépésekben lévő hívások visszatérési értékei — azzal a korlátozással, hogy a lánc nem folytatódik tovább egy lépésnél. Egyszerűbben: object.getX().getY().doZ() — sértés az első getX() után.

A formális szabály könnyen automatizálható: a statikus elemző ellenőrzi, hogy az a.b().c().d() formájú kifejezésben nincsenek-e 2-nél hosszabb láncok. Detekt (Android) és Tailor (iOS) támogatják az ilyen ellenőrzéseket. Állítsa be a küszöböt: maximum 2 hívás ponton keresztül egy kifejezésben.

Miért veszélyesek a hívási láncok?

Hívási láncok (chain calls, train wrecks) — az LoD-sértés fő tünete. Amikor a kód a.getB().getC().getD().doSomething()-et ír, az a objektum nemcsak b, hanem c és d szerkezetéről is tudomást szerez. Változtatás a lánc bármely szeme megtöri ezt a hívást, holott a-nak csak b-ről kellene tudnia.

Vegyünk egy valós esetet: egy iOS alkalmazásban a profilképernyő user.address.city.name-et kér le egy láncon keresztül. A tervező úgy dönt, hogy eltávolítja a city-t a címből. Most meg kell találni az ÖSSZES helyet, ahol city.name-et használják, és javítani — mindegyik elromolhat. Ha a profilképernyő user.displayAddress()-t kért volna — a változtatás csak User-t érintette volna. LoD megakadályozza a lépcsőzetes javításokat.

A Microsoft Research: „An Empirical Study of Law of Demeter in Practice” (2021) kutatás 500 nyílt forráskódú projektet elemzett, és kimutatta: minden 10. commitban előfordul egy modellváltozás miatt eltört hívási lánc javítása. Ugyanakkor az ilyen javítások 68%-a — a megváltoztatott modellhez nem kapcsolódó fájlokban található. Láncok terjesztik a változtatásokat az egész kódbázisban.

Használja az LoD-t code review szabályként: ha 3+ hívásból álló láncot lát — kérjen refactoringot. Kivétel — Builder (konstruktor), ahol a lánc nem sérti az LoD-t, mivel minden hívás ugyanazt a builder-t adja vissza.

LoD-sértések: gyakorlati példák

Klasszikus sértés: tranzitív hozzáférés a mezőkhöz

Tranzitív hozzáférés — az LoD-sértés leggyakoribb példája. A kód lekér egy objektumot, majd gettereken keresztül behatol ennek az objektumnak a belsejébe, majd a következőébe. Minden getter feltárja a belső szerkezetet, és LoD-sértésre hív.

kotlin
// LoD-sértés: 4 hívásból álló lánc
val cityName = order
    .getUser()
    .getAddress()
    .getCity()
    .getName()

// Javítás: Tell, Don't Ask — hagyja, hogy Order maga szolgáltassa
class Order {
    fun getUserCityName(): String =
        user.address.city.name
}

Az első változatban az OrderViewModel tudja, hogy az Order-ben van User, a User-ben Address, az Address-ben City, a City-ben name. Ha a City átnevezi a name-et title-re — minden hívás elromlik. Javítás hozzáadja a getUserCityName() metódust az Order-hez: a ViewModel csak Order-t ismer, az Order elrejti a belső szerkezetet.

LoD-sértés iOS-ben: hozzáférés a subviews-hoz

Az iOS projektek gyakran megsértik az LoD-t a view hierarchiával való munka során. A kód hozzáfér a view.subviews.first?.subviews.last-hez, és módosítja a benne lévő UILabel-t. Ez — tranzitív hozzáférés a UI belső szerkezetéhez, amely a hierarchia legkisebb változásakor elromlik.

swift
// LoD-sértés: hozzáférés a view belső hierarchiájához
if let label = view
    .subviews.first?
    .subviews
    .compactMap({ $0 as? UILabel })
    .first {
    label.text = "Új szöveg"
}

// Javítás: metódus az UIView-en, amely elrejti a hierarchiát
extension UIView {
    var titleLabel: UILabel? {
        subviews.first?.subviews.compactMap { $0 as? UILabel }.first
    }
}

Az UIView kiterjesztés elrejti a subviews közötti navigációt. A külső kód közvetlenül kapja meg a titleLabel-t, anélkül hogy ismerné a belső szerkezetet. Változtatás a view hierarchiában csak a kiterjesztést érinti, nem a tucatnyi helyet, ahol ezt a UILabel-t használják.

Hogyan javítsuk az LoD-sértést Android és iOS rendszerben?

Széles interfész → keskeny interfész

Széles interfész (getterek az összes belső mezőre) — az LoD-sértés fő oka. Ha az objektum feltárja az összes belső részét, az ügyfelek elkerülhetetlenül tranzitíven kezdenek járni rajtuk. Megoldás: cserélje ki a gettereket jelentéssel bíró műveleteket végrehajtó metódusokra (Tell, Don't Ask).

A user.address.city.name helyett biztosítson user.getCityName()-t. Az order.items.getTotal() helyett biztosítson order.getTotalPrice()-t. Minden ilyen metódus — a lánc beágyazása, amely védi az ügyfeleket a belső szerkezet változásaitól. Martin Fowler: „Refactoring, 2nd Edition” (2019) szerint a tranzitív hozzáférés közvetítő metódussal való helyettesítése — az egyik leghasznosabb refactoring a haszon/erőfeszítés arány alapján.

Ellenőrizze az összes publikus gettert, amely mutable objektumokat ad vissza. Ha egy getter nem primitívet, hanem összetett objektumot ad vissza — ez potenciális LoD-sértés. Adjon hozzá egy metódust, amely végrehajtja a szükséges műveletet, és korlátozza a getterhez való hozzáférést.

Facade összetett alrendszerekhez

Facade — architekturális minta, amely egyszerű interfészt biztosít egy összetett alrendszerhez. Az LoD kontextusában a Facade egy osztály, amelyen keresztül az ügyfél kommunikál egy objektumcsoporttal anélkül, hogy ismerné azok belső szerkezetét. Repository Android-ban — klasszikus Facade, amely elrejti a DataSource → API → cache láncokat.

kotlin
// Facade: A Repository elrejti az adatforrások láncát
class PaymentRepository(
    private val api: PaymentApi,
    private val cache: PaymentCache,
    private val analytics: AnalyticsTracker
) {
    suspend fun processPayment(amount: Double): Result {
        analytics.track("payment_start")
        val result = api.charge(amount)
        cache.save(result)
        return result
    }
}

// ViewModel nem tud sem api-ról, sem cache-ről, sem analytics-ról
viewModel.processPayment(amount)

PaymentRepository — Facade: a ViewModel egyetlen processPayment metódust hív, a repository pedig koordinálja az API-t, cache-t és analitikát magában. ViewModel-nek nincsenek hívási láncai az api.charge()-hoz vagy cache.save()-hez — ez sértené az LoD-t. A teljes belső szerkezet egyetlen hívás mögé van rejtve.

Gyakori hibák az LoD követése során

Vak követés: túlzott wrapper metódusok

Túlzott wrapperek — amikor a programozó tucatnyi közvetítő metódust hoz létre, amelyek egyszerűen átirányítják a hívást egyik osztályból a másikba. Order.getUserEmail() = user.email — haszontalan wrapper. LoD nem kér wrappert minden mezőhöz — azt kéri, hogy rejtse el a láncokat, nem az egyes egyszerű mezőket.

Kritérium: ha a wrapper egyszerűen visszaad egy mezőt transzformáció és láncelrejtés nélkül — nem szükséges. Order.getUserEmail() — rossz wrapper, mert a user.email közvetlen hozzáférés a szomszédos objektum mezőjéhez, és a user Order közvetlen mezője, amit az LoD engedélyez. Sértés akkor lenne, ha az Order user.getEmail()-t két lépésben adná vissza: először user, majd email.

Ne hozzon létre wrappereket közvetlen mezőkhöz (hozzáférés a saját objektum mezőjéhez vagy közvetlen mezőhöz — az LoD által engedélyezett). Akkor hozzon létre wrappert, amikor az ügyfél tranzitíven kezd járni: a.b().c().d() → a.b().d() vagy a.d().

Az LoD összetévesztése Démétér adatokra vonatkozó törvényével

LoD a viselkedésre vonatkozik, nem az adatokra. A data class (DTO — egyszerű adattárolók) nem kötelesek betartani az LoD-t: céljuk az adatok feltárása. OrderDTO.items[0].price — nem LoD-sértés, mert a DTO definíció szerint adatstruktúra, nem viselkedéssel rendelkező objektum. Összetévesztés az objektumok és adatstruktúrák között — az egyik leggyakoribb hiba.

A különbséget Robert C. Martin: „Clean Code” (2008) mutatta be: „Az objektumok elrejtik az adatokat és feltárják a viselkedést. Az adatstruktúrák feltárják az adatokat és nem rendelkeznek viselkedéssel.” Az LoD a viselkedéssel rendelkező objektumokra vonatkozik. Az adatstruktúrák (DTO, JSON modellek) esetében a hozzáférési láncok megengedettek. Amint egy struktúra logikával rendelkező metódust kap — objektummá válik, és be kell tartania az LoD-t.

Különböztesse meg: ha egy osztály csak mezőket tartalmaz metódusok nélkül (DTO) — az LoD nem alkalmazandó rá. Ha egy osztály logikával rendelkező metódusokat tartalmaz — az LoD kötelező. Code review során ellenőrizze: ez data class (DTO) vagy objektum (metódusokkal)?

Gyakran Ismételt Kérdések

Mi Démétér törvénye egyszerű szavakkal?

Démétér törvénye (LoD): egy objektum csak közeli barátaival kommunikálhat — önmagával, mezőivel, metódusai paramétereivel és azokkal az objektumokkal, amelyeket ő maga hozott létre. Nem lehet láncon keresztül menni: a.getB().getC().doSomething() — ez sértés.

Miben különbözik az LoD a Tell, Don't Ask-tól?

LoD — arról szól, hogy MELYIK objektumokhoz lehet fordulni (csak közvetlen szomszédokhoz). Tell, Don't Ask — arról szól, hogy HOGYAN kell fordulni (ne kérdezz adatokat, hanem mondd meg, hogy csinálja). Kiegészítik egymást: az LoD korlátozza a kommunikációs kört, a Tell Don't Ask — a fordulás jellegét.

Mikor lehet megsérteni az LoD-t?

LoD megsérthető DTO (Data Transfer Objects) és egyszerű, logikát nem tartalmazó adatstruktúrák esetében. Továbbá a Builder nem minősül sértésnek, mert minden hívás ugyanazt a builder-t adja vissza. Kivételek: láncok a Stream API-ban (map, filter) — nem LoD-sértés.

Hogyan ellenőrzi a Detekt az LoD-t Android-ban?

Detekt rendelkezik a TooManyFunctions szabállyal (közvetve), de a láncok közvetlen ellenőrzéséhez használja a DataClassShouldBeImmutable szabályt és egyéni ellenőrzéseket a bindingReference segítségével. Állítsa be a CI-t: 2-nél hosszabb láncok — figyelmeztetés, 3-nál hosszabbak — build hiba.

Hogyan ellenőrzi a SwiftLint az LoD-t iOS-ben?

SwiftLint nem rendelkezik beépített szabállyal az LoD-hez, de létrehozható egyéni szabály regex segítségével: \..+\.\..+\.\..+ formájú láncok (3+ hívás ponton keresztül). Alternatíva: használja a nimble_operator szabályt, és bővítse ki a hosszú láncok észlelésére.

Összegzés

  • LoD (Law of Demeter / a legkevesebb tudás elve) — szabály: az objektum csak közvetlen barátaival lép kapcsolatba.
  • Hívási láncok (train wrecks) — az LoD fő sértése: a.b().c().d() rejtett függőségeket hoz létre a típusok teljes láncától.
  • Tell, Don't Ask — közeli elv: delegálja a műveletet az objektumnak, ne kérje az adatait külső feldolgozáshoz.
  • Széles getterek — a sértések oka: ha az objektum feltárja az összes mezőt, az ügyfelek tranzitíven járni kezdenek rajtuk.
  • Facade — minta az LoD betartásához: egységes interfész rejti el az összetett alrendszert az ügyfél elől.
  • DTO és data class — kivétel: az adatstruktúrák nem kötelesek betartani az LoD-t, mivel nincs viselkedésük.
  • Automatizálás Detekt (Android) vagy egyéni SwiftLint (iOS) segítségével csökkenti az LoD-sértések számát a kódbázisban.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is