È un odore di codice se un oggetto ne conosce molto il proprietario?

9

Nella nostra applicazione Delphi 2007 utilizziamo molti dei seguenti costrutti

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

FindOwnerClass percorre la gerarchia di proprietari del componente corrente verso l'alto per trovare una classe specifica (nell'esempio TdmBasicData). L'oggetto risultante è memorizzato nella variabile Field FdmBasic. Lo usiamo principalmente per passare i datamoduli.

Esempio: Quando si genera un report, i dati risultanti vengono compressi e archiviati in un campo Blob di una tabella a cui si accede tramite un datamodule TdmReportBaseData. In un modulo separato della nostra applicazione, c'è la funzionalità per mostrare i dati dal report in un modulo Paged usando ReportBuilder. Il codice principale di questo modulo (TdmRBReport), utilizza una classe TRBTempdatabase per convertire i dati del blob compresso in diverse tabelle utilizzabili nel runtime di Reportbuilder runtimedesigner. TdmRBReport ha accesso a TdmReportBaseData per tutti i tipi di dati relativi ai report (tipo di report, impostazioni di calcolo dei report, ecc.). TRBTempDatabase è costruito in TdmRBReport ma deve avere accesso a TdmReportBasedata. Quindi questo è ora fatto usando la costruzione sopra:

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

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

Il mio sentimento è che questo significa che TRBTempDatabase conosce molto del suo proprietario e mi chiedevo se si tratta di una sorta di odore di codice o Anti-pattern.

Cosa ne pensi di questo? È un odore di codice? Se sì, qual è il modo migliore?

    
posta Bascy 12.08.2011 - 09:22
fonte

2 risposte

13

Questo tipo di assetto sembra un pattern di localizzazione del servizio che è stato descritto per la prima volta da Martin Fowler (che è stato identificato come anti-pattern comune).

L'Iniezione delle Dipendenze basata sulla costruzione è preferita rispetto a un Localizzatore di servizio in quanto promuove la visibilità dei parametri richiesti e promuove il Test unitario più semplice.

The problem with using a Service Locator isn’t that you take a dependency on a particular Service Locator implementation (although that may be a problem as well), but that it’s a bona-fide anti-pattern. It will give consumers of your API a horrible developer experience, and it will make your life as a maintenance developer worse because you will need to use considerable amounts of brain power to grasp the implications of every change you make.

The compiler can offer both consumers and producers so much help when Constructor Injection is used, but none of that assistance is available for APIs that rely on Service Locator.

Inoltre, in particolare, infrange anche la legge di Demeter

The Law of Demeter (LoD) or Principle of Least Knowledge is a design guideline for developing software, particularly object-oriented programs. In its general form, the LoD is a specific case of loose coupling.

Law of Demeter for functions requires that 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
  5. a global variable, accessible by O, in the scope of M

In particular, an object should avoid invoking methods of a member object returned by another method. For many modern object oriented languages that use a dot as field identifier, the law can be stated simply as "use only one dot". That is, the code a.b.Method() breaks the law where a.Method() does not. 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.

The Better Way

Il modo migliore è quello di rimuovere la chiamata del servizio locator all'interno della classe e passare il proprietario corretto come parametro all'interno del costruttore. Anche se ciò significa che hai una classe di servizio che esegue una ricerca del proprietario e poi la passa al costruttore della classe

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

  FdmReportBaseData := TdmRBReport(ownerClass).dmReportBaseData;
end;{- .Create }
    
risposta data 12.08.2011 - 10:46
fonte
3

Una delle difficoltà con il fatto che gli oggetti figlio sanno troppo del genitore è che si finisce per implementare schemi che possono (e molto spesso lo fanno) diventare troppo strettamente accoppiati, il che crea grossi problemi di dipendenza e spesso diventa molto difficile modificarli & mantenere sicuro in seguito.

A seconda di quanto profondamente le tue due classi sono connesse, sembra un po 'come la descrizione di Fowler degli odori di codice di Feature Envy o Intimacy Inappropriate sono evidenti.

Sembra che sia necessario caricare o leggere una classe con i dati, nel qual caso potresti usare una serie di pattern alternativi per rompere la dipendenza tra il bambino e la sua catena di genitori, e sembra che tu debba delegare il compito di accedere alla tua classe di dati piuttosto che rendere la classe accessor di dati responsabile di fare tutto da solo.

    
risposta data 06.02.2012 - 03:33
fonte

Leggi altre domande sui tag