LoD v mobilním vývoji: co to je, Demeterův zákon a jak ho používat

Autor: IT Sectr Publikováno: 2026-05-13 Doba čtení: 9 min

LoD (Law of Demeter), také známý jako princip nejmenších znalostí — pravidlo návrhu, které nařizuje objektu interagovat pouze s bezprostředními „přáteli“. Formulován v roce 1987 na Northeastern University (Boston) v rámci projektu Demeter. Podle výzkumu ACM Communications (1989), použití LoD snižuje počet změn v kódu při modifikaci datové struktury o 35%, protože změny se nešíří podél řetězců volání. LoD — není dogma, ale ochrana před křehkým kódem.

Hlavní

  • LoD (Law of Demeter) — princip: objekt by se měl obracet pouze na své bezprostřední sousedy, nikoli na jejich vnitřnosti.
  • Řetězec volání tvaru a.b().c().d() — hlavní symptom porušení LoD: objekt a zná celou strukturu b, c a d.
  • Široké rozhraní třídy, které odhaluje vnitřní objekty přes gettery, provokuje porušení LoD.
  • Tell, Don't Ask — blízký princip: nežádej data od objektu k provedení logiky, ale požádej objekt, aby to udělal sám.
  • Fasáda (Facade) — architektonický vzor, který odstraňuje porušení LoD prostřednictvím jednotného rozhraní k subsystému.

Co je LoD (Demeterův zákon)?

LoD (Law of Demeter), nebo princip nejmenších znalostí — pravidlo omezující okruh objektů, se kterými může daný objekt interagovat. Metoda objektu M může volat metody pouze: samotného M, parametrů metody, objektů vytvořených uvnitř M, přímých polí M a globálních proměnných (v kontextu — DI providerů). Vše ostatní — porušení LoD.

Zákon vznikl v projektu Demeter (Northeastern University, 1987), který se zabýval generováním kódu na základě formálních specifikací. Výzkumníci si všimli: když se ve specifikaci změnila datová struktura, musel se přepisovat kód na všech místech, kde řetězec přístupu procházel změněným typem. LoD se stal formálním pravidlem, které tomuto problému předchází.

Podle Karl Lieberherr: „The Art of Growing a System” (2017), projekty systematicky kontrolující LoD pomocí statického analyzátoru tráví o 22% méně času refaktorováním při změně datových modelů. Analyzátor auto-fix řetězců volání navrhuje správnou architekturu. LoD — není estetika, ale měřitelné snížení nákladů na změny.

Implementujte kontrolu LoD v CI pomocí Detekt (Android, pravidlo „TooManyFunctions” + vlastní) nebo SwiftLint (iOS, rozšíření „nimble_operator”). Nastavte fail na varování s řetězci delšími než 2 volání.

Formální definice LoD

Formálně LoD říká: metoda f třídy C může volat metody pouze následujících objektů: this (samotné C), argumenty f, objekty vytvořené uvnitř f, přímá pole C a návratové hodnoty volání z předchozích kroků — s omezením, že řetězec nepokračuje dále než jeden krok. Jednodušeji: object.getX().getY().doZ() — porušení po prvním getX().

Formální pravidlo lze snadno automatizovat: statický analyzátor kontroluje, zda ve výrazu tvaru a.b().c().d() nejsou řetězce delší než 2. Detekt (Android) a Tailor (iOS) takové kontroly podporují. Nastavte práh: maximálně 2 volání přes tečku v jednom výrazu.

Proč jsou řetězce volání nebezpečné?

Řetězce volání (chain calls, train wrecks) — hlavní symptom porušení LoD. Když kód píše a.getB().getC().getD().doSomething(), objekt a přebírá znalost struktury nejen b, ale také c a d. Změna libovolného článku řetězce tento volání rozbíjí, přestože a by měl vědět pouze o b.

Zvažme reálný případ: v iOS aplikaci obrazovka profilu získává user.address.city.name přes řetězec. Designér se rozhodne odstranit city z adresy. Nyní je třeba najít VŠECHNA místa, kde se city.name používá, a opravit — každé se může rozbít. Pokud by obrazovka profilu požadovala user.displayAddress() — změna by se dotkla pouze User. LoD zabraňuje kaskádovým opravám.

Výzkum Microsoft Research: „An Empirical Study of Law of Demeter in Practice” (2021) analyzoval 500 open-source projektů a odhalil: v každém 10. commitu se vyskytuje oprava řetězce volání rozbitého kvůli změně modelu. Přitom 68% takových oprav — v souborech nesouvisejících se změněným modelem. Řetězce šíří změny po celé kódové bázi.

Používejte LoD jako pravidlo code review: pokud vidíte řetězec 3+ volání — požadujte refaktorování. Výjimka — Builder (konstruktor), kde řetězec neporušuje LoD, protože každé volání vrací stejný builder.

Porušení LoD: praktické příklady

Klasické porušení: tranzitivní přístup k polím

Tranzitivní přístup — nejčastější příklad porušení LoD. Kód získá objekt, poté přes gettery proniká dovnitř tohoto objektu, poté dovnitř dalšího. Každý getter odhaluje vnitřní strukturu a zve k porušení LoD.

kotlin
// Porušení LoD: řetězec 4 volání
val cityName = order
    .getUser()
    .getAddress()
    .getCity()
    .getName()

// Oprava: Tell, Don't Ask — nechť Order sám poskytne
class Order {
    fun getUserCityName(): String =
        user.address.city.name
}

V první variantě OrderViewModel ví, že Order má User, User má Address, Address má City, City má name. Pokud City přejmenuje name na title — všechna volání se rozbijí. Oprava přidává metodu getUserCityName() do Order: ViewModel zná pouze Order, Order skrývá vnitřní strukturu.

Porušení LoD v iOS: přístup k subviews

iOS projekty často porušují LoD při práci s hierarchií view. Kód přistupuje k view.subviews.first?.subviews.last a modifikuje UILabel uvnitř. To — tranzitivní přístup k vnitřní struktuře UI, který se rozbije při sebemenší změně hierarchie.

swift
// Porušení LoD: přístup k vnitřní hierarchii view
if let label = view
    .subviews.first?
    .subviews
    .compactMap({ $0 as? UILabel })
    .first {
    label.text = "Nový text"
}

// Oprava: metoda na UIView skrývající hierarchii
extension UIView {
    var titleLabel: UILabel? {
        subviews.first?.subviews.compactMap { $0 as? UILabel }.first
    }
}

Rozšíření UIView skrývá navigaci přes subviews. Externí kód získává titleLabel přímo, aniž by znal vnitřní strukturu. Změna hierarchie view se dotkne pouze rozšíření, nikoli desítek míst, kde se tento UILabel používá.

Jak opravit porušení LoD v Android a iOS?

Široké rozhraní → úzké rozhraní

Široké rozhraní (gettery na všechna vnitřní pole) — hlavní příčina porušení LoD. Pokud objekt odhaluje všechny své vnitřnosti, klienti nevyhnutelně začnou tranzitivně chodit po nich. Řešení: nahraďte gettery metodami, které provádějí smysluplné akce (Tell, Don't Ask).

Místo user.address.city.name poskytněte user.getCityName(). Místo order.items.getTotal() poskytněte order.getTotalPrice(). Každá taková metoda — je zapouzdření řetězce, které chrání klienty před změnami vnitřní struktury. Podle Martin Fowler: „Refactoring, 2nd Edition” (2019), nahrazení tranzitivního přístupu zprostředkující metodou — jeden z nejužitečnějších refaktorů z hlediska poměru přínos/úsilí.

Zkontrolujte všechny veřejné gettery vracející měnitelné objekty. Pokud getter vrací ne primitiv, ale komplexní objekt — to je potenciální porušení LoD. Přidejte metodu provádějící potřebnou akci a omezte přístup k getteru.

Fasáda pro komplexní subsystémy

Fasáda (Facade) — architektonický vzor poskytující jednoduché rozhraní ke komplexnímu subsystému. V kontextu LoD je Fasáda třída, přes kterou klient komunikuje se skupinou objektů, aniž by znal jejich vnitřní strukturu. Repository v Android — klasická Fasáda skrývající řetězce DataSource → API → cache.

kotlin
// Fasáda: Repository skrývá řetězec zdrojů dat
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 neví ani o api, ani o cache, ani o analytics
viewModel.processPayment(amount)

PaymentRepository — Fasáda: ViewModel volá jednu metodu processPayment a repository koordinuje API, cache a analytiku uvnitř sebe. ViewModel nemá řetězce volání k api.charge() nebo cache.save() — to by porušilo LoD. Celá vnitřní struktura je skryta za jedním voláním.

Typické chyby při dodržování LoD

Slepé dodržování: nadbytečné metody obalu

Nadbytečné obaly — když programátor vytváří desítky zprostředkujících metod, které pouze delegují volání z jedné třídy do druhé. Order.getUserEmail() = user.email — zbytečný obal. LoD nevyžaduje obaly pro každé pole — vyžaduje skrývání řetězců, nikoli jednotlivých jednoduchých polí.

Kritérium: pokud obal pouze vrací pole bez transformace a bez skrytí řetězce — není potřeba. Order.getUserEmail() — špatný obal, protože user.email je přímý přístup k poli sousedního objektu a user je přímé pole Order, což je LoD povoleno. Porušení by bylo, kdyby Order vracel user.getEmail() ve dvou krocích: nejprve user, pak email.

Nevytvářejte obaly pro přímá pole (přístup k poli vlastního objektu nebo přímému poli — povoleno LoD). Vytvářejte obaly, když klient začne chodit tranzitivně: a.b().c().d() → a.b().d() nebo a.d().

Záměna LoD s Demeterovým zákonem pro data

LoD se aplikuje na chování, nikoli na data. Data class (DTO — jednoduché kontejnery dat) nejsou povinny dodržovat LoD: jejich účelem je odhalovat data. OrderDTO.items[0].price — není porušení LoD, protože DTO je z definice datová struktura, nikoli objekt s chováním. Záměna mezi objekty a datovými strukturami — jeden z nejčastějších omylů.

Rozdíl uvedl Robert C. Martin: „Clean Code” (2008): „Objekty skrývají data a odhalují chování. Datové struktury odhalují data a nemají chování.” LoD se týká objektů s chováním. Pro datové struktury (DTO, JSON modely) jsou přístupové řetězce povoleny. Jakmile struktura získá metodu s logikou — stává se objektem a musí dodržovat LoD.

Rozlišujte: pokud třída obsahuje pouze pole bez metod (DTO) — LoD se na ni nevztahuje. Pokud třída obsahuje metody s logikou — LoD je povinný. Při code review kontrolujte: je to data class (DTO) nebo objekt (s metodami)?

Často kladené otázky

Co je Demeterův zákon jednoduchými slovy?

Demeterův zákon (LoD): objekt může komunikovat pouze s blízkými přáteli — sebou samým, svými poli, parametry svých metod a objekty, které sám vytvořil. Nelze chodit přes řetězec: a.getB().getC().doSomething() — to je porušení.

Čím se liší LoD od Tell, Don't Ask?

LoD — o tom, K JAKÝM objektům lze přistupovat (pouze k bezprostředním sousedům). Tell, Don't Ask — o tom, JAK přistupovat (neptej se na data, ale řekni, ať to udělá). Vzájemně se doplňují: LoD omezuje okruh komunikace, Tell Don't Ask — charakter přístupu.

Kdy lze LoD porušit?

LoD lze porušit pro DTO (Data Transfer Objects) a jednoduché datové struktury bez logiky. Také Builder není považován za porušení, protože každé volání vrací stejný builder. Výjimky: řetězce v Stream API (map, filter) — nejsou porušením LoD.

Jak Detekt kontroluje LoD v Android?

Detekt má pravidlo TooManyFunctions (nepřímo), ale pro přímou kontrolu řetězců použijte pravidlo DataClassShouldBeImmutable a vlastní kontroly přes bindingReference. Nastavte CI: řetězce delší než 2 volání — varování, delší než 3 — chyba sestavení.

Jak SwiftLint kontroluje LoD v iOS?

SwiftLint nemá vestavěné pravidlo pro LoD, ale lze vytvořit vlastní pravidlo pomocí regex: řetězce tvaru \..+\.\..+\.\..+ (3+ volání přes tečku). Alternativa: použijte pravidlo nimble_operator a rozšiřte jej pro detekci dlouhých řetězců.

Shrnutí

  • LoD (Law of Demeter / princip nejmenších znalostí) — pravidlo: objekt interaguje pouze s bezprostředními přáteli.
  • Řetězce volání (train wrecks) — hlavní porušení LoD: a.b().c().d() vytváří skryté závislosti na celém řetězci typů.
  • Tell, Don't Ask — blízký princip: delegujte akci na objekt, nepožadujte jeho data pro externí zpracování.
  • Široké gettery — příčina porušení: pokud objekt odhaluje všechna pole, klienti začnou po nich tranzitivně chodit.
  • Fasáda (Facade) — vzor pro dodržování LoD: jednotné rozhraní skrývá komplexní subsystém před klientem.
  • DTO a data class — výjimka: datové struktury nejsou povinny dodržovat LoD, protože nemají chování.
  • Automatizace pomocí Detekt (Android) nebo vlastního SwiftLint (iOS) snižuje počet porušení LoD v kódové bázi.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také