Law of Demeter

$wikipedia

A method M of an object O may only invoke the methods of the following kinds of objects:

  1. O itself
  2. M's parameters
  3. any objects created/instantiated within M
  4. O's direct component objects

“use only one dot”

post.author.name

post.author_name

Two Problems

#1

Law of Demeter

#1

Law of Demeter

Object-Oriented Programming: An Objective Sense of Style

#2

NOT Law of Demeter

For all classes C, and all methods M attached to C, all objects to which M sends a message must be

  1. M's argument objects, including the self object or
  2. The instance variable objects of C.

(Objects created by M, or by functions or methods which M calls, and objects in global variables are considered as arguments of M.)

For all classes C, and all methods M attached to C, all objects to which M sends a message must be

  1. M's argument objects, including the self object or
  2. The instance variable objects of C.

(Objects created by M, or by functions or methods which M calls, and objects in global variables are considered as arguments of M.)

The Law prohibits the nesting of generic accessor function calls, which return objects that are not instance variable objects. It allows the nesting of constructor function calls. An accessor function is a function which returns an object which did exist before the function is called. A constructor function returns an object which did not exist before the function is called.

post.author.name

post.author.name

whatever.

#2

Separation of concerns

Writing programs which follow the Law of Demeter … increases the number of methods. [This] is related to the problem outlined in [12] which is that there can be too many operations on a type.

One way of correcting this problem is to organize all the methods associated with a particular functional (or algorithmic) task into “Modula-2 like” module structures as outlined in [11].

a module is a grouping of variables, constants, types, and procedures

http://www.modula2.org/tutor/chapter12.php

post.author_name

post.author.name

post.create_post_by_same_author

post.author.create_post

“author”

types don't matter

“author”

author

post.author.name

post.author.create_post

Common Sense

Have I Got A Bridge For You

  # Automatic "Demeter Transmogrifier"
  def method_missing(sym, *args, &block)
    parts = sym.to_s.split '_'
    (parts.size - 1).downto 1 do |i|
      prefix = parts[0...i].join '_'
      suffix = parts[i..-1].join '_'
      break send(prefix).send(suffix, *args, &block) if respond_to? prefix
    end
  end

  [".elur diputs a s'tI"].first_reverse

  # Automatic conversion to Demeter-style
  def require(base_name)
    if $".include? base_name
      false
    else
      file = $:.map {|dir| File.join dir, "#{base_name}.rb" }.find {|file| File.exist? file }
      code = IO.read file
      code.gsub! /\.(.+)\./, '.\1_'
      Object.module_eval code, file, 1
      true
    end
  end

  open('foo.rb', 'w') {|f| f.puts '[".esnes nommoc esu tsuJ"].first.reverse' }
  require 'foo'