¿Es un olor a código si un objeto conoce mucho a su propietario?

9

En nuestra aplicación Delphi 2007 estamos usando muchas de las siguientes construcciones

FdmBasic:=TdmBasicData(FindOwnerClass(AOwner,TdmBasicData));

FindOwnerClass recorre la jerarquía de Propietario del componente actual hacia arriba para encontrar una clase específica (en el ejemplo TdmBasicData). El objeto resultante se almacena en la variable de campo FdmBasic. Usamos esto principalmente para pasar módulos de datos.

Ejemplo: al generar un informe, los datos resultantes se comprimen y almacenan en un campo Blob de una tabla a la que se accede a través de un módulo de datos TdmReportBaseData. En un módulo separado de nuestra aplicación, hay una funcionalidad para mostrar los datos del informe en un formulario Paged usando ReportBuilder. El código principal de este módulo (TdmRBReport) utiliza una clase TRBTempdatabase para convertir los datos de blobs comprimidos en diferentes tablas que pueden utilizarse en el diseñador de informes de tiempo de ejecución de Reportbuilder. TdmRBReport tiene acceso a TdmReportBaseData para todo tipo de datos relacionados con el informe (tipo de informe, configuración de cálculo de informes, etc.). TRBTempDatabase está construido en TdmRBReport pero tiene que tener acceso a TdmReportBasedata. Entonces esto se hace usando la construcción anterior:

constructor TRBTempDatabase.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);

  FdmReportBaseData := TdmRBReport(FindOwnerClass(Owner, TdmRBReport)).dmReportBaseData;
end;{- .Create }

Mi sensación es que esto significa que TRBTempDatabase conoce mucho a su propietario, y me preguntaba si esto es algún tipo de olor a código o Anti-patrón.

¿Qué piensas sobre esto? ¿Es este un olor a código? Si es así, ¿cuál es una mejor manera?

Bascy
fuente
1
Si supuestamente supiera tanto sobre otra clase, se le habría proporcionado una forma más fácil de hacerlo.
Loren Pechtel

Respuestas:

13

Este tipo de patrón se parece a un patrón de localización de servicios que fue descrito por primera vez por Martin Fowler (que se identificó como un antipatrón común).

La inyección de dependencia basada en la construcción es preferible a un localizador de servicios, ya que promueve la visibilidad de los parámetros requeridos y promueve pruebas unitarias más simples.

El problema con el uso de un Localizador de servicios no es que dependas de una implementación particular del Localizador de servicios (aunque eso también puede ser un problema), sino que es un antipatrón de buena fe. Brindará a los consumidores de su API una experiencia de desarrollador horrible, y empeorará su vida como desarrollador de mantenimiento porque necesitará usar cantidades considerables de poder mental para comprender las implicaciones de cada cambio que realice.

El compilador puede ofrecer tanta ayuda a los consumidores como a los productores cuando se utiliza la inyección de constructor, pero nada de esa asistencia está disponible para las API que dependen del localizador de servicios.

También más notablemente también viola la Ley de Demeter

La Ley de Demeter (LoD) o Principio de Menos Conocimiento es una guía de diseño para desarrollar software, particularmente programas orientados a objetos. En su forma general, el LoD es un caso específico de acoplamiento flojo.

La ley de Demeter para funciones requiere que un método M de un objeto O solo invoque los métodos de los siguientes tipos de objetos:

  1. O mismo
  2. Parámetros de M
  3. cualquier objeto creado / instanciado dentro de M
  4. Objetos componentes directos de O
  5. Una variable global, accesible por O, en el ámbito de M

En particular, un objeto debe evitar invocar métodos de un objeto miembro devuelto por otro método. Para muchos lenguajes modernos orientados a objetos que usan un punto como identificador de campo, la ley puede establecerse simplemente como "usar solo un punto". Es decir, el código abMethod () infringe la ley donde a.Method () no. Como un simple ejemplo, cuando uno quiere pasear a un perro, sería una locura ordenarle a las patas del perro que caminen directamente; en cambio, uno le ordena al perro y le deja cuidar sus propias patas.

La mejor manera

Efectivamente, la mejor manera es eliminar la llamada del localizador de servicios dentro de la clase y pasar el propietario correcto como parámetro dentro de su constructor. Incluso si esto significa que tiene una clase de servicio que realiza una búsqueda del propietario y luego la pasa al constructor de la clase

constructor TRBTempDatabase.Create(aOwner: TComponent, ownerClass: IComponent);
begin
  inherited Create(aOwner);

  FdmReportBaseData := TdmRBReport(ownerClass).dmReportBaseData;
end;{- .Create }
Justin Shield
fuente
3
Gran respuesta y apoyo a quien se le ocurrió esta maravillosa analogía:As a simple example, when one wants to walk a dog, it would be folly to command the dog's legs to walk directly; instead one commands the dog and lets it take care of its own legs.
Andy Hunt
3

Una de las dificultades para que los objetos secundarios sepan demasiado sobre los padres es que terminas implementando patrones que pueden (y con frecuencia lo hacen) acoplarse demasiado, lo que crea grandes dolores de cabeza de dependencia y a menudo se vuelve muy difícil de modificar y mantener de manera segura mas tarde.

Dependiendo de cuán profundamente estén conectadas sus dos clases, parece un poco como la descripción de Fowler de la Envidia de características o los olores inapropiados del código de intimidad son evidentes.

Parece que es necesario cargar o leer una clase con datos, en cuyo caso podría usar una serie de patrones alternativos para romper la dependencia entre el niño y su cadena de padres, y parece que necesita delegar la tarea de acceder su clase de datos en lugar de hacer que la clase de acceso a datos sea responsable de hacer todo por sí mismo.

S.Robins
fuente