LoD sa mobile development: ano ito, batas ng Demeter at kung paano ito ilalapat

May-akda: IT Sectr Nai-publish: 2026-05-13 Oras ng pagbabasa: 9 min

LoD (Law of Demeter), kilala rin bilang prinsipyo ng pinakamaliit na kaalaman — panuntunan sa disenyo na nag-uutos sa isang bagay na makipag-ugnayan lamang sa mga direktang “kaibigan”. Binubuo noong 1987 sa Northeastern University (Boston) sa loob ng proyektong Demeter. Ayon sa pag-aaral ng ACM Communications (1989), ang paglalapat ng LoD ay nagbabawas ng bilang ng mga pagbabago sa code kapag binago ang istruktura ng data ng 35%, dahil ang mga pagbabago ay hindi kumakalat sa mga chain ng tawag. LoD — hindi dogma, kundi proteksyon laban sa marupok na code.

Mga Pangunahing

  • LoD (Law of Demeter) — prinsipyo: ang bagay ay dapat lumapit lamang sa mga direktang kapitbahay nito, hindi sa kanilang mga interior.
  • Chain ng tawag na anyong a.b().c().d() — pangunahing sintomas ng paglabag sa LoD: alam ng bagay a ang buong istruktura ng b, c at d.
  • Malawak na interface ng klase na naglalantad ng mga panloob na bagay sa pamamagitan ng mga getter, nag-uudyok ng paglabag sa LoD.
  • Tell, Don't Ask — malapit na prinsipyo: huwag humingi ng data sa bagay upang isagawa ang lohika, kundi hilingin sa bagay na gawin ito mismo.
  • Facade — pattern ng arkitektura na nag-aalis ng paglabag sa LoD sa pamamagitan ng pinag-isang interface sa subsystem.

Ano ang LoD (batas ng Demeter)?

LoD (Law of Demeter), o prinsipyo ng pinakamaliit na kaalaman — panuntunan na naglilimita sa bilog ng mga bagay na maaaring makipag-ugnayan ang isang partikular na bagay. Ang pamamaraan ng bagay M ay maaari lamang tumawag ng mga pamamaraan ng: M mismo, mga parameter ng pamamaraan, mga bagay na nilikha sa loob ng M, mga direktang field ng M at mga pandaigdigang variable (sa konteksto — mga provider ng DI). Lahat ng iba pa — paglabag sa LoD.

Ang batas ay lumitaw sa proyektong Demeter (Northeastern University, 1987), na nakatuon sa pagbuo ng code batay sa mga pormal na spesipikasyon. Napansin ng mga mananaliksik: kapag nagbago ang istruktura ng data sa spesipikasyon, kailangang muling isulat ang code sa lahat ng lugar kung saan ang access chain ay dumaan sa binagong uri. LoD ay naging pormal na panuntunan na pumipigil sa problemang ito.

Ayon kay Karl Lieberherr: “The Art of Growing a System” (2017), ang mga proyektong sistematikong sumusuri ng LoD sa pamamagitan ng static analyzer ay gumagastos ng 22% mas kaunting oras sa refactoring kapag binabago ang mga modelo ng data. Ang auto-fix analyzer ng mga chain ng tawag ay nagmumungkahi ng tamang arkitektura. LoD — hindi estetika, kundi masusukat na pagbawas sa gastos ng mga pagbabago.

Ipatupad ang pagsusuri ng LoD sa CI sa pamamagitan ng Detekt (Android, panuntunang “TooManyFunctions” + kustom) o SwiftLint (iOS, extension na “nimble_operator”). I-configure ang fail sa mga babala na may chain na mas mahaba sa 2 tawag.

Pormal na depinisyon ng LoD

Pormal na sinasabi ng LoD: ang pamamaraan f ng klase C ay maaari lamang tumawag ng mga pamamaraan ng mga sumusunod na bagay: this (C mismo), mga argumento ng f, mga bagay na nilikha sa loob ng f, mga direktang field ng C at mga halaga ng pagbabalik ng mga tawag mula sa mga nakaraang hakbang — na may limitasyon na hindi magpapatuloy ang chain nang higit sa isang hakbang. Mas simple: object.getX().getY().doZ() — paglabag pagkatapos ng unang getX().

Ang pormal na panuntunan ay madaling i-automate: sinusuri ng static analyzer kung sa expression na anyong a.b().c().d() ay walang chain na mas mahaba sa 2. Detekt (Android) at Tailor (iOS) ay sumusuporta sa mga ganitong pagsusuri. Itakda ang threshold: maximum na 2 tawag sa pamamagitan ng tuldok sa isang expression.

Bakit mapanganib ang mga chain ng tawag?

Mga chain ng tawag (chain calls, train wrecks) — pangunahing sintomas ng paglabag sa LoD. Kapag ang code ay nagsusulat ng a.getB().getC().getD().doSomething(), ang bagay a ay kumukuha ng kaalaman tungkol sa istruktura hindi lamang ng b, kundi pati ng c at d. Pagbabago ng anumang kawing ng chain ay sumisira sa tawag na ito, kahit na a ay dapat lamang malaman ang tungkol sa b.

Isaalang-alang ang isang tunay na kaso: sa iOS application ang screen ng profile ay kumukuha ng user.address.city.name sa pamamagitan ng chain. Nagpasya ang taga-disenyo na alisin ang city mula sa address. Ngayon kailangang hanapin ang LAHAT ng lugar kung saan ginagamit ang city.name at ayusin — bawat isa ay maaaring masira. Kung ang screen ng profile ay humingi ng user.displayAddress() — ang pagbabago ay makakaapekto lamang sa User. LoD ay pumipigil sa mga cascading correction.

Ang pag-aaral ng Microsoft Research: “An Empirical Study of Law of Demeter in Practice” (2021) ay nagsuri ng 500 open-source na proyekto at natuklasan: sa bawat ika-10 commit ay may pag-aayos ng chain ng tawag na nasira dahil sa pagbabago ng modelo. Samantala, 68% ng mga ganitong pag-aayos — sa mga file na hindi nauugnay sa binagong modelo. Mga chain ay nagpapakalat ng mga pagbabago sa buong codebase.

Gamitin ang LoD bilang panuntunan sa code review: kung makakita ka ng chain na 3+ tawag — humingi ng refactoring. Exception — Builder (constructor), kung saan ang chain ay hindi lumalabag sa LoD, dahil ang bawat tawag ay nagbabalik ng parehong builder.

Mga paglabag sa LoD: praktikal na halimbawa

Klasikong paglabag: transitive access sa mga field

Transitive access — pinakakaraniwang halimbawa ng paglabag sa LoD. Ang code ay kumukuha ng bagay, pagkatapos sa pamamagitan ng mga getter ay pumapasok sa loob ng bagay na ito, pagkatapos ay sa loob ng susunod. Bawat getter ay naglalantad ng panloob na istruktura at nag-aanyaya sa paglabag ng LoD.

kotlin
// Paglabag sa LoD: chain ng 4 na tawag
val cityName = order
    .getUser()
    .getAddress()
    .getCity()
    .getName()

// Pag-aayos: Tell, Don't Ask — hayaan ang Order mismo ang magbigay
class Order {
    fun getUserCityName(): String =
        user.address.city.name
}

Sa unang variant, alam ng OrderViewModel na ang Order ay may User, ang User ay may Address, ang Address ay may City, ang City ay may name. Kung palitan ng City ang name sa title — lahat ng tawag ay masisira. Pag-aayos ay nagdaragdag ng pamamaraang getUserCityName() sa Order: ang ViewModel ay alam lamang ang Order, ang Order ay nagtatago ng panloob na istruktura.

Paglabag sa LoD sa iOS: access sa subviews

Ang mga proyekto sa iOS ay madalas na lumalabag sa LoD kapag nagtatrabaho sa hirarkiya ng view. Ang code ay uma-access sa view.subviews.first?.subviews.last at binabago ang UILabel sa loob. Ito — transitive access sa panloob na istruktura ng UI, na nasisira sa pinakamaliit na pagbabago ng hirarkiya.

swift
// Paglabag sa LoD: access sa panloob na hirarkiya ng view
if let label = view
    .subviews.first?
    .subviews
    .compactMap({ $0 as? UILabel })
    .first {
    label.text = "Bagong teksto"
}

// Pag-aayos: pamamaraan sa UIView na nagtatago ng hirarkiya
extension UIView {
    var titleLabel: UILabel? {
        subviews.first?.subviews.compactMap { $0 as? UILabel }.first
    }
}

Ang extension ng UIView ay nagtatago ng nabigasyon sa pamamagitan ng subviews. Ang panlabas na code ay direktang nakakakuha ng titleLabel, nang hindi alam ang panloob na istruktura. Pagbabago ng hirarkiya ng view ay makakaapekto lamang sa extension, hindi sa dose-dosenang lugar kung saan ginagamit ang UILabel na ito.

Paano ayusin ang paglabag sa LoD sa Android at iOS?

Malawak na interface → makitid na interface

Malawak na interface (mga getter sa lahat ng panloob na field) — pangunahing dahilan ng paglabag sa LoD. Kung ang bagay ay naglalantad ng lahat ng panloob na bahagi nito, ang mga kliyente ay hindi maiiwasang magsimulang maglakbay nang transitively sa mga ito. Solusyon: palitan ang mga getter ng mga pamamaraan na nagsasagawa ng makabuluhang aksyon (Tell, Don't Ask).

Sa halip na user.address.city.name, magbigay ng user.getCityName(). Sa halip na order.items.getTotal(), magbigay ng order.getTotalPrice(). Bawat ganitong pamamaraan — ay inkapsulasyon ng chain na nagpoprotekta sa mga kliyente mula sa mga pagbabago sa panloob na istruktura. Ayon kay Martin Fowler: “Refactoring, 2nd Edition” (2019), ang pagpapalit ng transitive access ng pamamaraang tagapamagitan — isa sa mga pinakakapaki-pakinabang na refactoring sa ratio ng benepisyo/pagsisikap.

Suriin ang lahat ng pampublikong getter na nagbabalik ng mutable na mga bagay. Kung ang getter ay nagbabalik hindi primitibo, kundi kumplikadong bagay — ito ay potensyal na paglabag sa LoD. Magdagdag ng pamamaraan na nagsasagawa ng kinakailangang aksyon at limitahan ang access sa getter.

Facade para sa kumplikadong subsystems

Facade — pattern ng arkitektura na nagbibigay ng simpleng interface sa isang kumplikadong subsystem. Sa konteksto ng LoD, ang Facade ay isang klase kung saan nakikipag-ugnayan ang kliyente sa isang grupo ng mga bagay, nang hindi alam ang kanilang panloob na istruktura. Repository sa Android — klasikong Facade na nagtatago ng mga chain na DataSource → API → cache.

kotlin
// Facade: Repository ay nagtatago ng chain ng mga pinagmumulan ng data
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 ay hindi alam ang tungkol sa api, ni cache, ni analytics
viewModel.processPayment(amount)

PaymentRepository — Facade: ang ViewModel ay tumatawag ng isang pamamaraang processPayment, at ang repository ay nagko-coordinate ng API, cache at analytics sa loob nito. ViewModel ay walang mga chain ng tawag sa api.charge() o cache.save() — ito ay lalabag sa LoD. Ang buong panloob na istruktura ay nakatago sa likod ng isang tawag.

Mga karaniwang pagkakamali sa pagsunod sa LoD

Bulag na pagsunod: labis na wrapper na pamamaraan

Labis na wrapper — kapag ang programmer ay lumikha ng dose-dosenang tagapamagitang pamamaraan na simpleng nagde-delegate ng tawag mula sa isang klase patungo sa isa pa. Order.getUserEmail() = user.email — walang silbing wrapper. LoD ay hindi nangangailangan ng wrapper para sa bawat field — nangangailangan ito ng pagtatago ng mga chain, hindi ng mga indibidwal na simpleng field.

Kriteria: kung ang wrapper ay simpleng nagbabalik ng field nang walang transpormasyon at walang pagtatago ng chain — hindi ito kailangan. Order.getUserEmail() — masamang wrapper, dahil ang user.email ay direktang access sa field ng katabing bagay, at ang user ay direktang field ng Order, na pinapayagan ng LoD. Paglabag ay kung ang Order ay nagbabalik ng user.getEmail() sa pamamagitan ng dalawang hakbang: una user, pagkatapos email.

Huwag gumawa ng wrapper para sa direktang field (access sa field ng sariling bagay o direktang field — pinapayagan ng LoD). Gumawa ng wrapper kapag ang kliyente ay nagsimulang maglakbay nang transitively: a.b().c().d() → a.b().d() o a.d().

Pagkalito ng LoD sa batas ng Demeter para sa data

LoD ay inilalapat sa pag-uugali, hindi sa data. Ang data class (DTO — simpleng lalagyan ng data) ay hindi obligadong sumunod sa LoD: ang layunin nila ay maglantad ng data. OrderDTO.items[0].price — hindi paglabag sa LoD, dahil ang DTO ay sa depinisyon ay istruktura ng data, hindi bagay na may pag-uugali. Pagkalito sa pagitan ng mga bagay at istruktura ng data — isa sa mga pinakakaraniwang pagkakamali.

Ang pagkakaiba ay ginawa ni Robert C. Martin: “Clean Code” (2008): “Ang mga bagay ay nagtatago ng data at naglalantad ng pag-uugali. Ang mga istruktura ng data ay naglalantad ng data at walang pag-uugali.” Ang LoD ay nauukol sa mga bagay na may pag-uugali. Para sa mga istruktura ng data (DTO, mga modelo ng JSON) ang mga chain ng access ay pinapayagan. Sa sandaling ang isang istruktura ay magkaroon ng pamamaraan na may lohika — ito ay nagiging bagay at dapat sumunod sa LoD.

Pag-ibahin: kung ang isang klase ay naglalaman lamang ng mga field na walang pamamaraan (DTO) — ang LoD ay hindi nalalapat dito. Kung ang isang klase ay naglalaman ng mga pamamaraan na may lohika — ang LoD ay sapilitan. Sa code review suriin: ito ba ay data class (DTO) o bagay (may mga pamamaraan)?

Mga Madalas Itanong

Ano ang batas ng Demeter sa simpleng salita?

Batas ng Demeter (LoD): ang bagay ay maaari lamang makipag-usap sa malalapit na kaibigan — sarili nito, mga field nito, mga parameter ng mga pamamaraan nito at mga bagay na ito mismo ang lumikha. Hindi maaaring dumaan sa chain: a.getB().getC().doSomething() — ito ay paglabag.

Ano ang pagkakaiba ng LoD sa Tell, Don't Ask?

LoD — tungkol sa ALING mga bagay ang maaaring lapitan (mga direktang kapitbahay lamang). Tell, Don't Ask — tungkol sa PAANO lumapit (huwag humingi ng data, kundi sabihing gawin). Sila ay nagpupuno sa isa't isa: nililimitahan ng LoD ang bilog ng komunikasyon, tinutukoy ng Tell Don't Ask — ang karakter ng paglapit.

Kailan maaaring labagin ang LoD?

LoD ay maaaring labagin para sa DTO (Data Transfer Objects) at simpleng istruktura ng data na walang lohika. Gayundin ang Builder ay hindi itinuturing na paglabag, dahil ang bawat tawag ay nagbabalik ng parehong builder. Exceptions: mga chain sa Stream API (map, filter) — hindi paglabag sa LoD.

Paano sinusuri ng Detekt ang LoD sa Android?

Detekt ay may panuntunang TooManyFunctions (hindi direkta), ngunit para sa direktang pagsusuri ng mga chain gamitin ang panuntunang DataClassShouldBeImmutable at mga kustom na pagsusuri sa pamamagitan ng bindingReference. I-configure ang CI: mga chain na mas mahaba sa 2 tawag — babala, mas mahaba sa 3 — error sa build.

Paano sinusuri ng SwiftLint ang LoD sa iOS?

SwiftLint ay walang built-in na panuntunan para sa LoD, ngunit maaaring gumawa ng kustom na panuntunan sa pamamagitan ng regex: mga chain na anyong \..+\.\..+\.\..+ (3+ tawag sa pamamagitan ng tuldok). Alternatibo: gamitin ang panuntunang nimble_operator at palawakin ito para sa pagtuklas ng mahabang chain.

Buod

  • LoD (Law of Demeter / prinsipyo ng pinakamaliit na kaalaman) — panuntunan: ang bagay ay nakikipag-ugnayan lamang sa mga direktang kaibigan.
  • Mga chain ng tawag (train wrecks) — pangunahing paglabag sa LoD: a.b().c().d() ay lumilikha ng nakatagong dependencies mula sa buong chain ng mga uri.
  • Tell, Don't Ask — malapit na prinsipyo: italaga ang aksyon sa bagay, huwag hilingin ang data nito para sa panlabas na pagproseso.
  • Malalawak na getter — sanhi ng mga paglabag: kung ang bagay ay naglalantad ng lahat ng field, ang mga kliyente ay nagsisimulang maglakbay nang transitively sa mga ito.
  • Facade — pattern para sa pagsunod sa LoD: pinag-isang interface ay nagtatago ng kumplikadong subsystem mula sa kliyente.
  • DTO at data class — exception: ang mga istruktura ng data ay hindi obligadong sumunod sa LoD, dahil wala silang pag-uugali.
  • Automation sa pamamagitan ng Detekt (Android) o kustom na SwiftLint (iOS) ay nagbabawas ng bilang ng mga paglabag sa LoD sa codebase.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din