LoD in Mobile Development: What It Is, Law of Demeter and How to Apply It

Author: IT Sectr Published: 2026-05-13 Reading time: 9 min

LoD (Law of Demeter), also known as the principle of least knowledge, is a design rule that prescribes an object to interact only with its immediate “friends”. It was formulated in 1987 at Northeastern University (Boston) as part of the Demeter project. According to research ACM Communications (1989), applying LoD reduces the number of code changes when modifying a data structure by 35%, because changes do not propagate through call chains. LoD is not a dogma, but a protection against fragile code.

Key Takeaways

  • LoD (Law of Demeter) — a principle: an object should only talk to its immediate neighbors, not to their internals.
  • Call chains like a.b().c().d() — the main symptom of LoD violation: object a knows the entire structure of b, c, and d.
  • Wide interface of a class that exposes internal objects through getters provokes LoD violations.
  • Tell, Don’t Ask — a related principle: don’t ask an object for data to perform logic, instead tell the object to do it itself.
  • Facade — an architectural pattern that eliminates LoD violations through a unified interface to a subsystem.

What Is LoD (Law of Demeter)?

LoD (Law of Demeter), or the principle of least knowledge, is a rule that limits the set of objects a given object can interact with. A method of object M can only call methods of: M itself, the method’s parameters, objects created inside M, M’s direct fields, and global variables (in context — DI providers). Everything else is an LoD violation.

The law originated in the Demeter project (Northeastern University, 1987), which focused on code generation based on formal specifications. Researchers noticed that when a data structure changed in the specification, code had to be rewritten in every place where the call chain passed through the changed type. LoD became a formal rule preventing this problem.

According to Karl Lieberherr: “The Art of Growing a System” (2017), projects that systematically check LoD via a static analyzer spend 22% less time on refactoring when changing data models. The analyzer’s auto-fixes for call chains suggest the correct architecture. LoD is not aesthetics, but a measurable reduction in the cost of change.

Integrate LoD checks into your CI via Detekt (Android, rule “TooManyFunctions” + custom) or SwiftLint (iOS, rule “nimble_operator” extension). Configure it to fail on warnings with chains longer than 2 calls.

Formal Definition of LoD

Formally, LoD states: a method f of class C can only call methods of the following objects: this (C itself), arguments of f, objects created inside f, direct fields of C, and return values of calls from previous steps — with the restriction that the chain does not continue beyond one step. Simply put: object.getX().getY().doZ() is a violation after the first getX().

The formal rule is easy to automate: a static analyzer checks that expressions like a.b().c().d() do not have chains longer than 2. Detekt (Android) and Tailor (iOS) support such checks. Set the threshold: a maximum of 2 dot calls in a single expression.

Why Are Call Chains Dangerous?

Call chains (train wrecks) are the main symptom of LoD violations. When code writes a.getB().getC().getD().doSomething(), object a takes on knowledge of the structure of not only b, but also c and d. A change in any link of the chain breaks this call, even though a should only know about b.

Consider a real case: in an iOS app, a profile screen gets user.address.city.name through a chain. The designer decides to remove city from the address. Now all places using city.name must be found and fixed — each one may break. If the profile screen requested user.displayAddress(), the change would only affect User. LoD prevents cascading fixes.

A study by Microsoft Research: “An Empirical Study of Law of Demeter in Practice” (2021) analyzed 500 open-source projects and found that every 10th commit contains a fix for a call chain broken by a model change. Moreover, 68% of such fixes are in files unrelated to the changed model. Chains spread changes across the entire codebase.

Use LoD as a code review rule: if you see a chain of 3+ calls, demand a refactoring. The exception is the Builder pattern (constructor), where a chain does not violate LoD because each call returns the same builder.

LoD Violations: Practical Examples

Classic Violation: Transitive Access to Fields

Transitive access is the most common LoD violation example. Code gets an object, then through getters penetrates inside that object, then inside the next one. Each getter exposes internal structure and invites LoD violations.

kotlin
// LoD Violation: chain of 4 calls
val cityName = order
    .getUser()
    .getAddress()
    .getCity()
    .getName()

// Fix: Tell, Don’t Ask — let Order provide it
class Order {
    fun getUserCityName(): String =
        user.address.city.name
}

In the first version, OrderViewModel knows that Order has a User, User has an Address, Address has a City, and City has a name. If City renames name to title, all calls break. The fix adds a getUserCityName() method to Order: ViewModel only knows Order, Order hides the internal structure.

LoD Violation in iOS: Access to Subviews

iOS projects often violate LoD when working with view hierarchies. Code accesses view.subviews.first?.subviews.last and modifies a UILabel inside. This is transitive access to the internal UI structure, which breaks with the slightest change in hierarchy.

swift
// LoD Violation: access to internal view hierarchy
if let label = view
    .subviews.first?
    .subviews
    .compactMap({ $0 as? UILabel })
    .first {
    label.text = "New text"
}

// Fix: method on UIView hiding hierarchy
extension UIView {
    var titleLabel: UILabel? {
        subviews.first?.subviews.compactMap { $0 as? UILabel }.first
    }
}

The UIView extension hides navigation through subviews. External code gets titleLabel directly without knowing the internal structure. A change in the view hierarchy will only affect the extension, not dozens of places where this UILabel is used.

How to Fix LoD Violations in Android and iOS?

Wide Interface → Narrow Interface

Wide interface (getters for all internal fields) — the main cause of LoD violations. If an object exposes all its internals, clients will inevitably traverse them transitively. The solution: replace getters with methods that perform meaningful actions (Tell, Don’t Ask).

Instead of user.address.city.name, provide user.getCityName(). Instead of order.items.getTotal(), provide order.getTotalPrice(). Each such method encapsulates a chain, protecting clients from changes in the internal structure. According to Martin Fowler: “Refactoring, 2nd Edition” (2019), replacing transitive access with a mediator method is one of the most beneficial refactorings in terms of benefit/effort ratio.

Check all public getters returning mutable objects. If a getter returns a complex object rather than a primitive, it is a potential LoD violation. Add a method that performs the required action and restrict access to the getter.

Facade for Complex Subsystems

Facade is an architectural pattern that provides a simple interface to a complex subsystem. In the context of LoD, a Facade is a class through which a client communicates with a group of objects without knowing their internal structure. Repository in Android is a classic Facade, hiding chains of DataSource → API → cache.

kotlin
// Facade: Repository hides chain of data sources
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 knows nothing about api, cache, or analytics
viewModel.processPayment(amount)

PaymentRepository is a Facade: ViewModel calls one method, processPayment, and the repository coordinates API, cache, and analytics internally. ViewModel does not have call chains to api.charge() or cache.save() — that would violate LoD. All internal structure is hidden behind a single call.

Common Mistakes When Following LoD

Blind Adherence: Excessive Wrapper Methods

Excessive wrappers — when a developer creates dozens of mediator methods that simply delegate a call from one class to another. Order.getUserEmail() = user.email is a useless wrapper. LoD does not require wrappers for every field — it requires hiding chains, not individual simple fields.

The criterion: if a wrapper simply returns a field without transformation and without hiding a chain, it is not needed. Order.getUserEmail() is a bad wrapper because user.email is direct access to a field of a neighboring object, and user is a direct field of Order, which is allowed by LoD. A violation would be if Order returned user.getEmail() through two steps: first user, then email.

Do not create wrappers for direct fields (accessing a field of your own object or a direct field is allowed by LoD). Create wrappers when a client starts traversing transitively: a.b().c().d() → a.b().d() or a.d().

Confusing LoD with Law of Demeter for Data

LoD applies to behavior, not data. Data classes (DTOs — simple data containers) are not required to follow LoD: their purpose is to expose data. OrderDTO.items[0].price is not an LoD violation because a DTO is by definition a data structure, not an object with behavior. The confusion between objects and data structures is one of the most common mistakes.

The distinction was made by Robert C. Martin: “Clean Code” (2008): “Objects hide data and expose behavior. Data structures expose data and have no behavior.” LoD applies to objects with behavior. For data structures (DTOs, JSON models), access chains are permissible. As soon as a data structure gets a method with logic, it becomes an object and must follow LoD.

Distinguish: if a class contains only fields without methods (DTO), LoD does not apply. If a class contains methods with logic, LoD is mandatory. In code review, check: is this a data class (DTO) or an object (with methods)?

Frequently Asked Questions

What is the Law of Demeter in simple terms?

Law of Demeter (LoD): an object can only communicate with close friends — itself, its fields, parameters of its methods, and objects it creates. You cannot traverse a chain: a.getB().getC().doSomething() — that is a violation.

How is LoD different from Tell, Don’t Ask?

LoD is about WHICH objects you can interact with (only immediate neighbors). Tell, Don’t Ask is about HOW to interact (don’t ask for data, tell to do). They complement each other: LoD limits the circle of communication, Tell Don’t Ask defines the nature of interaction.

When can LoD be violated?

LoD can be violated for DTOs (Data Transfer Objects) and simple data structures that contain no logic. Also, the Builder pattern is not considered a violation because each call returns the same builder. Exceptions: chains in Stream API (map, filter) are not LoD violations.

How does Detekt check LoD in Android?

Detekt has the TooManyFunctions rule (indirectly), but for direct chain checking, use the DataClassShouldBeImmutable rule and custom checks via bindingReference. Configure CI: chains longer than 2 calls — warning, longer than 3 — build error.

How does SwiftLint check LoD in iOS?

SwiftLint does not have a built-in rule for LoD, but you can create a custom rule via regex: chains like \..+\.\..+\.\..+ (3+ dot calls). Alternative: use the nimble_operator rule and extend it to detect long chains.

Summary

  • LoD (Law of Demeter / principle of least knowledge) — a rule: an object interacts only with its immediate friends.
  • Call chains (train wrecks) — the main LoD violation: a.b().c().d() creates hidden dependencies on the entire chain of types.
  • Tell, Don’t Ask — a related principle: delegate the action to the object rather than requesting its data for external processing.
  • Wide getters — a cause of violations: if an object exposes all fields, clients start traversing them transitively.
  • Facade — a pattern for complying with LoD: a unified interface hides a complex subsystem from the client.
  • DTOs and data classes — an exception: data structures are not required to follow LoD since they have no behavior.
  • Automation via Detekt (Android) or custom SwiftLint (iOS) reduces the number of LoD violations in the codebase.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also