LoD у мобилном развоју: шта је то, Деметрин закон и како га применити

Аутор: IT Sectr Објављено: 2026-05-13 Време читања: 9 мин

LoD (Law of Demeter), такође познат као принцип најмањег знања — правило пројектовања које налаже објекту да интерагује само са непосредним „пријатељима”. Формулисан је 1987. године на Northeastern University (Бостон) у оквиру пројекта Demeter. Према истраживању ACM Communications (1989), примена LoD смањује број измена у коду при модификацији структуре података за 35%, пошто се измене не шире дуж ланаца позива. LoD — није догма, већ заштита од крхког кода.

Главно

  • LoD (Law of Demeter) — принцип: објекат треба да се обраћа само својим непосредним суседима, а не њиховој унутрашњости.
  • Ланац позива облика a.b().c().d() — главни симптом кршења LoD: објекат a зна целу структуру b, c и d.
  • Широки интерфејс класе, који открива унутрашње објекте кроз гетере, провоцира кршење LoD.
  • Tell, Don't Ask — близак принцип: не питај податке од објекта да би извршио логику, већ замоли објекат да то уради сам.
  • Фасада (Facade) — архитектонски образац који отклања кршење LoD кроз јединствени интерфејс ка подсистему.

Шта је LoD (Деметрин закон)?

LoD (Law of Demeter), или принцип најмањег знања — правило које ограничава круг објеката са којима одређени објекат може да интерагује. Метода објекта M може да позива методе само: самог M, параметара методе, објеката створених унутар M, непосредних поља M и глобалних променљивих (у контексту — DI провајдера). Све остало — кршење LoD.

Закон је настао у пројекту Demeter (Northeastern University, 1987), који се бавио генерисањем кода на основу формалних спецификација. Истраживачи су приметили: када се у спецификацији мењала структура података, морао се преписивати код на свим местима где је ланац приступа пролазио кроз измењени тип. LoD је постао формално правило које спречава овај проблем.

Према Karl Lieberherr: „The Art of Growing a System” (2017), пројекти који систематски проверавају LoD кроз статички анализатор троше 22% мање времена на рефакторинг при промени модела података. Анализатор auto-fix ланаца позива сугерише исправну архитектуру. LoD — није естетика, већ мерљиво смањење трошкова измена.

Имплементирајте проверу LoD у CI кроз Detekt (Android, правило „TooManyFunctions” + прилагођено) или SwiftLint (iOS, додатак „nimble_operator”). Подесите fail на упозорења са ланцима дужим од 2 позива.

Формална дефиниција LoD

Формално LoD каже: метода f класе C може да позива методе само следећих објеката: this (сам C), аргументи f, објекти створени унутар f, непосредна поља C и повратне вредности позива са претходних корака — са ограничењем да се ланац не наставља даље од једног корака. Једноставније: object.getX().getY().doZ() — кршење после првог getX().

Формално правило се лако аутоматизује: статички анализатор проверава да ли у изразу облика a.b().c().d() нема ланаца дужих од 2. Detekt (Android) и Tailor (iOS) подржавају такве провере. Подесите праг: максимално 2 позива кроз тачку у једном изразу.

Зашто су ланци позива опасни?

Ланци позива (chain calls, train wrecks) — главни симптом кршења LoD. Када код пише a.getB().getC().getD().doSomething(), објекат a преузима знање о структури не само b, већ и c и d. Промена било које карике ланца ломи овај позив, иако a треба да зна само о b.

Размотримо реалан случај: у iOS апликацији екран профила добија user.address.city.name кроз ланац. Дизајнер одлучује да уклони city из адресе. Сада треба пронаћи СВА места где се користи city.name и поправити — свако може да се поквари. Да је екран профила захтевао user.displayAddress() — промена би се тицала само User. LoD спречава каскадне исправке.

Истраживање Microsoft Research: „An Empirical Study of Law of Demeter in Practice” (2021) анализирало је 500 open-source пројеката и открило: у сваком 10. комиту јавља се исправка ланца позива сломљеног због промене модела. Притом 68% таквих исправки — у датотекама које нису повезане са измењеним моделом. Ланци шире промене по целој бази кода.

Користите LoD као правило code review: ако видите ланац од 3+ позива — захтевајте рефакторинг. Изузетак — Builder (конструктор), где ланац не крши LoD, јер сваки позив враћа исти builder.

Кршења LoD: практични примери

Класично кршење: транзитивни приступ пољима

Транзитивни приступ — најчешћи пример кршења LoD. Код добија објекат, затим кроз гетере продире унутар овог објекта, затим унутар следећег. Сваки гетер открива унутрашњу структуру и позива на кршење LoD.

kotlin
// Кршење LoD: ланац од 4 позива
val cityName = order
    .getUser()
    .getAddress()
    .getCity()
    .getName()

// Исправка: Tell, Don't Ask — нека Order сам пружи
class Order {
    fun getUserCityName(): String =
        user.address.city.name
}

У првој варијанти, OrderViewModel зна да Order има User, User има Address, Address има City, City има name. Ако City преименује name у title — сви позиви се ломе. Исправка додаје метод getUserCityName() у Order: ViewModel зна само Order, Order скрива унутрашњу структуру.

Кршење LoD у iOS: приступ subviews

iOS пројекти често крше LoD при раду са хијерархијом view-ова. Код приступа view.subviews.first?.subviews.last и модификује UILabel унутра. Ово — транзитивни приступ унутрашњој структури UI, који се ломи при најмањој промени хијерархије.

swift
// Кршење LoD: приступ унутрашњој хијерархији view-а
if let label = view
    .subviews.first?
    .subviews
    .compactMap({ $0 as? UILabel })
    .first {
    label.text = "Нови текст"
}

// Исправка: метод на UIView који скрива хијерархију
extension UIView {
    var titleLabel: UILabel? {
        subviews.first?.subviews.compactMap { $0 as? UILabel }.first
    }
}

Проширење UIView скрива навигацију кроз subviews. Спољни код добија titleLabel директно, не знајући унутрашњу структуру. Промена хијерархије view-а ће утицати само на проширење, а не на десетине места где се користи овај UILabel.

Како поправити кршење LoD у Android и iOS?

Широки интерфејс → уски интерфејс

Широки интерфејс (гетери на сва унутрашња поља) — главни узрок кршења LoD. Ако објекат открива све своје унутрашњости, клијенти ће неизбежно почети да транзитивно ходе по њима. Решење: замените гетере методама које извршавају смислене радње (Tell, Don't Ask).

Уместо user.address.city.name пружите user.getCityName(). Уместо order.items.getTotal() пружите order.getTotalPrice(). Свака таква метода — то је инкапсулација ланца која штити клијенте од промена унутрашње структуре. Према Martin Fowler: „Refactoring, 2nd Edition” (2019), замена транзитивног приступа методом посредником — један од најкориснијих рефакторинга по односу корист/напор.

Проверите све јавне гетере који враћају mutable објекте. Ако гетер враћа не примитив, већ сложени објекат — то је потенцијално кршење LoD. Додајте метод који извршава потребну радњу и ограничите приступ гетеру.

Фасада за сложене подсистеме

Фасада (Facade) — архитектонски образац који пружа једноставан интерфејс ка сложеном подсистему. У контексту LoD, Фасада је класа кроз коју клијент комуницира са групом објеката, не знајући њихову унутрашњу структуру. Repository у Android-у — класична Фасада која скрива ланце DataSource → API → cache.

kotlin
// Фасада: Repository скрива ланац извора података
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 не зна ни за api, ни за cache, ни за analytics
viewModel.processPayment(amount)

PaymentRepository — Фасада: ViewModel позива једну методу processPayment, а репозиторијум координира API, cache и аналитику унутар себе. ViewModel нема ланаца позива ка api.charge() или cache.save() — то би кршило LoD. Цела унутрашња структура је скривена иза једног позива.

Типичне грешке при праћењу LoD

Слепо праћење: прекомерне методе омотачи

Прекомерни омотачи — када програмер ствара десетине метода посредника које само делегирају позив из једне класе у другу. Order.getUserEmail() = user.email — бескорисни омотач. LoD не захтева омотаче за свако поље — захтева скривање ланаца, а не појединачних једноставних поља.

Критеријум: ако омотач само враћа поље без трансформације и без скривања ланца — није потребан. Order.getUserEmail() — лош омотач, јер user.email представља директан приступ пољу суседног објекта, а user је непосредно поље Order, што је дозвољено LoD. Кршење би било да Order враћа user.getEmail() кроз два корака: прво user, затим емаил.

Не креирајте омотаче за директна поља (приступ пољу свог објекта или непосредном пољу — дозвољено LoD). Креирајте омотаче када клијент почне да хода транзитивно: a.b().c().d() → a.b().d() или a.d().

Мешање LoD са Деметриним законом за податке

LoD се примењује на понашање, а не на податке. Data class (DTO — једноставни контејнери података) нису обавезни да поштују LoD: њихова сврха је да откривају податке. OrderDTO.items[0].price — није кршење LoD, јер је DTO по дефиницији структура података, а не објекат са понашањем. Мешање објеката и структура података — једна од најчешћих грешака.

Разлику је изнео Robert C. Martin: „Clean Code” (2008): „Објекти скривају податке и откривају понашање. Структуре података откривају податке и немају понашање”. LoD се односи на објекте са понашањем. За структуре података (DTO, JSON модели) ланци приступа су дозвољени. Чим структура добије методу са логиком — постаје објекат и мора да поштује LoD.

Разликујте: ако класа садржи само поља без метода (DTO) — LoD се не примењује на њу. Ако класа садржи методе са логиком — LoD је обавезан. На code review проверавајте: ово је data class (DTO) или објекат (са методама)?

Често постављана питања

Шта је Деметрин закон једноставним речима?

Деметрин закон (LoD): објекат може да комуницира само са блиским пријатељима — собом, својим пољима, параметрима својих метода и објектима које је сам створио. Не може се ићи кроз ланац: a.getB().getC().doSomething() — ово је кршење.

Чим се LoD разликује од Tell, Don't Ask?

LoD — говори о томе КОЈИМ објектима се може приступити (само непосредним суседима). Tell, Don't Ask — говори о томе КАКО приступити (не питај за податке, већ реци да уради). Они се допуњују: LoD ограничава круг комуникације, Tell Don't Ask — карактер приступа.

Када се може прекршити LoD?

LoD се може прекршити за DTO (Data Transfer Objects) и једноставне структуре података без логике. Такође Builder не сматра се кршењем, јер сваки позив враћа исти builder. Изузеци: ланци у Stream API (map, filter) — нису кршење LoD.

Како Detekt проверава LoD у Android?

Detekt има правило TooManyFunctions (индиректно), али за директну проверу ланаца користите правило DataClassShouldBeImmutable и прилагођене провере кроз bindingReference. Подесите CI: ланци дужи од 2 позива — упозорење, дужи од 3 — грешка изградње.

Како SwiftLint проверава LoD у iOS?

SwiftLint нема уграђено правило за LoD, али се може креирати прилагођено правило кроз regex: ланци облика \..+\.\..+\.\..+ (3+ позива кроз тачку). Алтернатива: користите правило nimble_operator и проширите га за откривање дугих ланаца.

Закључци

  • LoD (Law of Demeter / принцип најмањег знања) — правило: објекат интерагује само са непосредним пријатељима.
  • Ланци позива (train wrecks) — главно кршење LoD: a.b().c().d() ствара скривене зависности од целог ланца типова.
  • Tell, Don't Ask — близак принцип: делегирајте радњу објекту, а не захтевајте његове податке за спољну обраду.
  • Широки гетери — узрок кршења: ако објекат открива сва поља, клијенти почињу да транзитивно ходе по њима.
  • Фасада (Facade) — образац за поштовање LoD: јединствени интерфејс скрива сложени подсистем од клијента.
  • DTO и data class — изузетак: структуре података нису обавезне да поштују LoD, јер немају понашање.
  • Аутоматизација кроз Detekt (Android) или прилагођени SwiftLint (iOS) смањује број кршења LoD у бази кода.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође