La Ley de Demeter

Esta ley o regla de buena practica de diseño, fue enunciada en la Universidad de Northeastern en 1987, por un grupo de programadores que trabajaban en un proyecto orientado a objectos, llamado Demeter, de alli su nombre. Esta ley tiene su definicion consisa y formal, pero se resume bajo el enunciado “Solo juntate con amigos conocidos”. ¿Que quiere decir esto?. Bueno si le estamos hablando a un objeto A, basicamente le estamos diciendo que si necesita un metodo, es preferible que lo busque solo entre sus metodos y que evite llamar a otro objeto B para conseguirlo, ya que pedirselo al objecto B significaria un conocimiento explicito de su estructura. Esta tambien es llamada a veces la ley de los tres puntos, y cumplirla nos ayuda al encapsulamiento de objectos y mejor mantenimiento de codigo. Voy a poner ejemplos:
Supongamos que un Cliente tiene una direccion y a su vez muchas facturas:

Class Cliente < ActiveRecord::Base
    has_one :direccion
    has_many :facturas
end
Class Direccion < ActiveRecord::Base
    belongs_to :cliente
end
Class Factura < ActiveRecord::Base
    belongs_to :cliente
end

Ahora si quisieramos mostrar los datos de un cliente en una factura, se haria algo como esto:

<%= @factura.cliente.direccion.calle %>
<%= @factura.cliente.direccion.municipio %>

Este codigo claramente rompe la ley de Demeter. Porque @factura tiene que navegar por distintos objetos para obtener la direccion.
Lo ideal seria tener un metodo llamado cliente_calle o cliente_municipio propio del modelo factura. Para lograr esto en algunos lenguajes se recurre a los a veces llamados Demeter Transmogrifiers, que son una serie de pequeños metodos que llaman a otros metodos. Una Refactorizacion clasica para cumplir la ley de Demeter seria la siguiente

Class Cliente < ActiveRecord::Base
    has_one :direccion
    has_many :facturas
    
    def calle
        self.direccion.calle  
        #OJO aqui podria ser solo direccion.calle.
    end
    def municipio
        self.direccion.municipo
    end
end

Class Factura < ActiveRecord::Base
    belongs_to :cliente
    
    def cliente_calle
        self.cliente.calle
    end
    def cliente_municipio
        self.cliente.municipio
    end    
end

Class Direccion < ActiveRecord::Base
    belongs_to :cliente
end

Ahora ya tenemos nuestro metodo cliente_calle en Factura que llama a su vez al metodo calle en Cliente que a su vez llama a calle en Direccion. Ahora cuando queramos informacion del cliente en una factura, solo hacemos esto:

<%= @factura.cliente_calle  %> 
# ...Y cumplimos con la ley de Demeter.!!!

Pero esperen un momento, ¿valdra la pena los costos de crear tantos metodos en los modelos? Pues la cuestion es una discusion, porque hay quienes prefieren un codigo mas encapsulado y menos acoplado y por lo tanto mas facil de mantener y hay quienes simplemente no, porque no les gusta engordar tanto el modelo. Al final elegir cumplir esta ley es cuestion de gusto y diseño. AHH.. un momento y ¿Rails tiene algo que decirnos al respecto?. Por supuesto, Rails como excelente hijo de las buenas practicas tiene en su haber el metodo “delegate” que forma parte del API Active Support. Vamos a refactorizar el codigo de los modelos y veamos como funciona este metodo:

Class Cliente < ActiveRecord::Base
    has_one :direccion
    has_many :facturas
    
    delegate :calle, :municipio, :to => :direccion
end

Class Factura < ActiveRecord::Base
    belongs_to :cliente
    
    delegate :calle, :municipio, :to => :cliente, :prefix => true
end

Class Direccion < ActiveRecord::Base
    belongs_to :cliente
end

tambien ahora podemos cumplir la ley y hacer algo como esto:

<%= @factura.cliente_calle %> 
#Lo mismo pero con menos lineas en los modelos.

Hermoso ¿verdad?… Aunque quizas obviemos la ley de Demeter y prefiramos el acoplamiento de objectos, el hecho de Rails tenga este metodo pone de manifiesto todo el amor y buen diseño que han puesto en el. Razon que me hace un usuario de Rails muy feliz =)…

Volviendo al punto. el metodo delegate pide como argumentos los metodos que se quieren pasar y el objeto al que se le piden dichos metodos con el symbolo :to, como ven en el ejemplo. Si se pone el simbolo :prefix y se pasa true (ejm. en la Clase Factura) entonces la llamada a dichos metodos se hace con el prefijo del objecto en :to.

delegate :metodo, :to => :modelo :prefix => :true
#===> .modelo_metodo

Al modificador :prefix tambien se le puede un simbolo o String para personalizar la llamada al metodo.

delegate :metodo, to: => :modelo :prefix => :miprefijo
#===> .miprefijo_metodo

Ademas delegate tiene un ultimo modificador :allow_nil => true/false que permite o no disparar una excepcion cuando se envia un metodo no existente, si esta configurado en true, devuelve nil, y en false, dispara la excepcion NoMethodError.

Para mas buenas practicas orientadas a Rails, les recomiendo los libros Rails Antipatterns y por supuesto el Rails Way, de los cuales esta basado la mayor de lo anterior.

Esta entrada fue publicada en Uncategorized. Guarda el enlace permanente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s