LoD Principle

The Law of Demeter (LoD) or principle of least knowledge says a module should not know about the internals of the objects it manipulates.

An object should not expose its internal structure through accessors because to do so is to expose, rather than to hide, its internal structure.

The advantage of following the Law of Demeter is that the resulting software tends to be more maintainable and adaptable. Since objects are less dependent on the internal structure of other objects, object containers can be changed without reworking their callers.

LoD Rules

The Law of Demeter says that a method f of a class C should only call the methods of these:

  1. C

  2. An object created by f

  3. An object held in an instance variable of C

  4. An object passed as an argument to f

public class UserService {

    private UserRepository userRepository;

    public List<User> getUsers(UserFilter filter) {
        QueryBuilder queryBuilder = new QueryBuilder();
        Query query = queryBuilder.build(filter); // rule 2

        List<User> users = userRepository.getUsers(query); // rule 3
        
        String direction = filter.getSort(); // rule 4
        
        this.sort(users, direction); // rule 1
    }

    private void sort(List<User> users, String direction) {}
}

Examples

Example 1:

private OrderService orderService;

public void getOrderDetail() {
    Oder order = orderService.getOrder();
    Customer customer = orderService.getCustomerService().getCustomer(customerId); // Violate LoD
}
private OrderService orderService;
private CustomerService customerService;

public void getOrderDetail() {
    Oder order = orderService.getOrder();
    Customer customer = customerService.getCustomer(customerId);
}

Example 2:

class Company {

    private List<Department> departments;

    Map<DepartmentCode, BigDecimal> costPerDepartment() {
        return departments.stream()
                          .filter(this::costCenter)
                          .collect(Collectors.toMap(Department::getCode, Department::cost));
    }

    private boolean costCentre(Department department) {
        return department.getType() instanceof CostCentre; // Violate LoD
    }
}

Instead of digging in Department details, it is better to ask, if a department is a cost centre.

class Company {

    private List<Department> departments;

    Map<DepartmentCode, BigDecimal> costPerDepartment() {
        return departments.stream()
                          .filter(this::costCentre)
                          .collect(Collectors.toMap(Department::getCode, Department::cost));
    }

    private boolean costCentre(Department department) {
        return department.isCostCentre();
    }
}

Last updated