Perdon .. pero --> jjajajaja nada mas feo que tener que estarse corrigiendo a cada rato porque mandas sin darte cuenta.

Nicolás Sanguinetti wrote:
2009/9/15 Nicolás Sanguinetti <[email protected]>:
2009/9/15 Nicolás Sanguinetti <[email protected]>:
2009/9/14 Sebastian A. Espindola <[email protected]>:
Gente,
 Estaba tirando código para un proyecto personal (una implementación
en Ruby de
 un server del protocolo OpenDMTP) y me encontre con la necesidad de
que una clase
 interprete si los datos que recibe desde un socket estan en un
formato binario o texto
 y genere una instancia que actue sobre esa codificación.
Hola, me llamo Nicolás y no entiendo nada de diplomacia. Tu solucion
es horrible.

Sin saber bien lo que estas haciendo (lease, no se que es OpenDMTP) lo
que yo haría (y después, abajo, comento tu código parte por parte.
Eh, por cierto, FAIL mio, tendría que ser algo como esto:

module Packet
 BINARY_PACKET_TYPE = 224
 ASCII_PACKET_TYPE = 36

  def self.read(message)
    case message
    when BINARY_PACKET_TYPE; Packet::Binary.new(message)
    when ASCII_PACKET_TYPE;  Packet::Text.new(message)
    else raise ArgumentError, "Wrong packet type: #{message}"
  end

  class Binary
  end

  class Text
  end
end

Ta, doble FAIL: me falto el `end` que cierra el `case message`.

Pero ta, se entiende la idea :)

O bueno, los nombres capaz que no son los mejores (de nuevo, ni idea
que es OpenDMTP :)), pero no esta bueno tirar numeros "al azar" en el
medio del codigo :)

-f

Packet.read(...)

Fijate como no tenés que andar haciendo aliases ni mapeando strings a
clases, y como me ahorro varios niveles de indirección.

 Leyendo varios articulos encontrados en google respecto a la
implementación del patron
 factory en ruby y jugando un poco con metaprogramación, me quedó el
siguiente codigo:

 class Packet

   class << self
     alias :nuevo :new

     def inherited(subclass)
       class << subclass
         alias :new :nuevo
       end
     end
Sobre esto:

1) (esta es a título personal) por favor, POR FAVOR, no escriban
nombres de métodos en español. Queda horrible leer código con pedazos
en otros idiomas.

Imaginate si mañana buscás una librería que soluciona un problema X, y
empezás a usarla, y cuando te encontrás con un error y querés revisar
el código, los comentarios y la mitad de los métodos están escritos en
ruso? :-/

(ojo, digo ruso como podría decir cualquier idioma, si justo sabés
ruso cambialo por otro :P)

2) En mi opinion personal, no esta bueno redefinir Class#new (en este
caso particular Packet.new). Prefiero mil veces agregar otro metodo de
clase que llame new. Pero ta, ponele que queres igual usar new, porque
te gusto, esto alcanza:

class Packet
 class << self
   alias_method :new_without_factory, :new
 end

 def self.new(*args)
   # haces cosas
   new_without_factory(*args)
 end
end

No tenes que usar self.inherited ni dar esas vueltas que te hacen leer
como 3 veces el código para saber que querés lograr.

     def new(*args)
       msg = *args
       encoding = get_packet_encoding(msg)
       Kernel.const_get(encoding).new unless encoding == "Unknown"
     end
Tiene sentido que Packet.new reciba más de un argumento? Porque
después cuando llamas BinaryPacket.new, o TextPacket.new, no les estás
pasando los args.

No se si te falto un new(*args) o simplemente querés usar el primer
argumento para deducir el tipo de clase a usar. Si es lo segundo,
entonces usar splat-args no tiene sentido. Definí simplemente 'msg'
como parametro.

     def get_packet_encoding(msg)
       encoding = case msg[0]
         when 224 then "BinaryPacket"
         when 36 then "TextPacket"
         else "Unknown"
       end
     end
   end
Aca viene la parte que creo es más fea. Para qué querés devolver
strings y hacer const_get de esos strings, cuando podés devolver
directamente la clase que querés? Estás agregandole niveles de
indirección totalmente innecesarios.

Y por último, "Unknown". Si yo hago Packet.new(42) con tu codigo,
devuelve nil. Eso es feo. Tener que andar chequeando por nil es Feo
(con f mayúscula).

Vos querés que *no funcione*, entonces lo ideal es que el usuario
reciba una notificación de que lo que hace, *no funciona*. Ergo, una
excepción me parece una mejor idea. Cuál? Depende, si estás haciendo
una libreria capaz que definir tu propia excepción tipo class
WrongPacketTypeProvided < ArgumentError; end tiene sentido. Si no, con
tirar un ArgumentError está bien (en mi opinión, es debatible :))

   def initialize
     puts "hola desde #{self.class}"
   end
 end

 class TextPacket < Packet
 end

 class BinaryPacket < Packet
 end
Y ta, después, lo de hacer Packet::Text en lugar de TextPacket <
Packet es puramente un tema de estilo personal, dado lo fácil que es
en ruby de pisar cosas sin darte cuenta, prefiero poner todo en
namespaces.

Si TextPacket y BinaryPacket heredaban de Packet por algún tema de
funcionalidad compartida, es fácil de cambiar. O directamente, definí
la funcionalidad compartida en el módulo Packet, y hace include Packet
adentro de las child classes.

 No conocia la posibilidad de definir alias de metodos, la
posibilidad de hacer modificaciones
 temporales en runtime "backupeando" el metodo con un alias es alucinante.

 Si uno le muestra esto a un programador que no conozca ruby, cuando
entienda como
 funciona se le vuela el peluquín. :)

 Calculo que en un par de meses voy a tener una implementacion
cliente-servidor de
 OpenDMTP decente basada en EventMachine, cuando la termine la voy a liberar.

 En fin, queria compartir mi "descubrimiento" con ustedes.
Y ta, por último, no te lo tomes a mal, si el lenguaje te parece
violento o algo, creeme que no es intencional, cuando digo que no
entiendo nada de diplomacia, es cierto, simplemente te digo lo que me
parece haría a tu código mejor :)

Y ta, obviamente, todo el mundo está invitado a demostrarme como
mejorar mi ejeplo :D

<chivo> Los code reviews son divertidos, tu equipo puede mejorar su
calidad de código usando http://howsmycode.com :P </chivo>

-foca

_______________________________________________
Ruby mailing list
[email protected]
http://lista.rubyargentina.com.ar/listinfo.cgi/ruby-rubyargentina.com.ar

_______________________________________________
Ruby mailing list
[email protected]
http://lista.rubyargentina.com.ar/listinfo.cgi/ruby-rubyargentina.com.ar

Responder a