2010-03-11 22 views
5

Después de un par de semanas leyendo en este foro, pensé que era hora de hacer mi primera publicación.Código completo 2ed, composición y delegación

Actualmente estoy releyendo Code Complete. Creo que es 15 años desde la última vez, y me parece que todavía no puedo escribir el código ;-)

De todos modos en la página 138 en Code Complete encontrará este ejemplo de horror de codificación. (He eliminado parte del código)

class Emplyee { 
public: 
FullName GetName() const; 
Address GetAddress() const; 
PhoneNumber GetWorkPhone() const; 
... 

bool IsZipCodeValid(Address address); 
... 

private: 
    ... 
} 

Lo que Steve cree que es malo es que las funciones están poco relacionadas. O ha escrito "No hay una conexión lógica entre los empleados y las rutinas que verifican los códigos postales, números de teléfono o clasificaciones de trabajos"

Ok Estoy totalmente de acuerdo con él. Quizás algo como el siguiente ejemplo sea mejor.

class ZipCode 
{ 
public: 
bool IsValid() const; 
    ... 
} 

class Address { 
public: 
    ZipCode GetZipCode() const; 
    ... 
} 

class Employee { 
public: 
Address GetAddress() const; 
    ... 
} 

Cuando Verificando la postal es válido lo que tendría que hacer algo como esto.

employee.GetAddress().GetZipCode().IsValid(); 

Y eso no es bueno con respecto al Law of Demeter.

Por lo tanto, si desea eliminar dos de los tres puntos, debe usar delegación y un par de funciones de envoltura como esta.

class ZipCode 
{ 
public: 
bool IsValid(); 
} 

class Address { 
public: 
    ZipCode GetZipCode() const; 
    bool IsZipCodeValid() {return GetZipCode()->IsValid()); 
} 

class Employee { 
public: 
FullName GetName() const; 
Address GetAddress() const; 
bool IsZipCodeValid() {return GetAddress()->IsZipCodeValid()); 
PhoneNumber GetWorkPhone() const; 
} 

employee.IsZipCodeValid(); 

Pero, una vez más, tiene rutinas que no tienen una conexión lógica.

Personalmente creo que los tres ejemplos en esta publicación son malos. ¿Es de alguna otra manera en la que no he pensado?

+0

Sé que muchos programadores adoran el código completo, pero sinceramente nunca lo hice. Fue una lectura muy aburrida. – JonH

+0

Eso depende de cuándo lo leyó. Si eres un desarrollador junior, es una buena lectura.Si eres un desarrollador experimentado, creo que las cosas escritas en el libro tienen sentido, sin ser algo extraordinario. –

+1

@JonH Estoy de acuerdo, su mejor libro es en realidad "Desarrollo rápido", que pocas personas parecen haber leído, es genial. –

Respuesta

1

Es pagar ahora frente a pagar más tarde.

Puede escribir las funciones de delegación y envoltura por adelantado (pagar ahora) y luego tener menos trabajo cambiando las entrañas de employee.IsZipCodeValid() más adelante. O bien, puede hacer un túnel a IsZipCodeValid escribiendo

employee.GetAddress().GetZipCode().IsValid();
donde lo necesite en el código, pero pague más adelante si decide cambiar el diseño de su clase de una manera que rompa este código.

Tienes que elegir tu veneno. ;)

+0

Esta es probablemente la mejor respuesta. Porque no es una solución elegante al problema. – Arlukin

7

Le falta la conexión lógica:

class ZipCode 
{ 
public: 
bool IsValid(); 
} 

class Address { 
public: 
    ZipCode GetZipCode() const; 
    bool IsAddressValid(); 
    bool IsValid() {return GetZipCode()->IsValid() && IsAddressValid()); 
} 

class Employee { 
public: 
FullName GetName() const; 
Address GetAddress() const; 
bool IsEmployeeValid(); 
bool IsValid() {return GetAddress()->IseValid() && IsEmployeeValid()); 
PhoneNumber GetWorkPhone() const; 
} 

employee.IsValid(); 
+0

Aún tendría que profundizar en 'ZipCode :: IsValid()' con el tiempo. Llama a 'employee.IsValid()' y devuelve falso. OK, ¿por qué el empleado no es válido? Entonces llama a 'employee.GetAddress(). IsValid()', y devuelve falso. OK, ¿por qué la dirección no es válida? Entonces llamas a 'employee.GetAddress(). GetZipCode(). IsValid()' y vuelves al problema original. – indiv

+0

Además, es posible que desee llamar a GetValue() en ZipCode, para mostrar en un cuadro de diálogo o página web. Eso te dará el mismo problema. – Arlukin

+0

@indiv, si va a tomar este ejemplo al pie de la letra, entonces sí, debería profundizar de esa manera, pero probablemente tenga un método que lo haga por usted y presente datos/mensajes de error para los datos pantalla de entrada. Si este estado no válido es el resultado de datos incorrectos en la aplicación, entonces creo que la respuesta es buscar limpiar los datos antes de inyectarlos en su modelo. – Lazarus

0

Dado que no existe una conexión lógica entre la clase Employee y la validación de código postal, puede poner la validación del código postal en la clase Address a la que pertenece más lógicamente. Luego puede pedirle a la clase de Dirección que valide el código postal por usted.

class Address 
{ 
    public: 
     static IsZipValid(ZipCode zip) { return zip.isValid(); } 
}; 

entonces no

Address::IsZipValid(employee.GetAddress().GetZipCode()); 

creo que esto es satisfactorio en sus limitaciones de asociación lógica y la Ley de Demeter.

+0

¿Pero realmente ganas algo con eso? Probablemente sea una cuestión de gusto personal, pero creo que es más fácil leer más puntos. Dirección :: IsZipValid (employee.GetAddress(). GetZipCode()); vs employee.GetAddress(). GetZipCode(). IsZipValid(); – Arlukin

+0

@Arlukin: No, no creo que ganes nada. Estaba respondiendo la pregunta como estaba planteada. Demeter no impone ninguna ley sobre mí, así que lo implementaría como employee.GetAddress(). GetZipCode.IsZipValid(). De esa manera me parece natural. – indiv

+0

@Arlukin, no se trata de ganar o perder, se trata de construir un modelo que tenga sentido. Si, al final del día, una de las formas se siente más natural para usted que otra y usted no es parte de un equipo (si es así, obtenga la guía del equipo), entonces haga lo que le parezca lógico. Estoy seguro de que si consigue dos programadores en una sala obtendrá seis opiniones sobre cómo codificar una determinada solución;) – Lazarus