È un odore di codice se un oggetto conosce molto del suo proprietario?


9

Nella nostra applicazione Delphi 2007 stiamo usando molti dei seguenti costrutti

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

FindOwnerClass sposta la gerarchia del proprietario 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 trasmettere moduli di dati.

Esempio: quando si genera un report, i dati risultanti vengono compressi e memorizzati in un campo BLOB di una tabella a cui si accede tramite un modulo di dati TdmReportBaseData. In un modulo separato della nostra applicazione, è disponibile una funzionalità per mostrare i dati dal report in un modulo Paged utilizzando ReportBuilder. Il codice principale di questo modulo (TdmRBReport) utilizza una classe TRBTempdatabase per convertire i dati BLOB compressi in diverse tabelle utilizzabili nel reportdesigner di runtime di Reportbuilder. TdmRBReport ha accesso a TdmReportBaseData per tutti i tipi di dati relativi al rapporto (tipo di rapporto, impostazioni di calcolo del rapporto, 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 }

La mia sensazione è che ciò significhi che TRBTempDatabase conosce molto il suo proprietario e mi chiedevo se si tratta di una sorta di odore di codice o Anti-pattern.

Quali sono le tue idee riguardo a questo? È un odore di codice? In tal caso, qual è il modo migliore?


1
Se dovesse sapere così tanto di un'altra classe, sarebbe stato fornito un modo più semplice per farlo.
Loren Pechtel,

Risposte:


13

Questo tipo di modello di Service Locator è stato descritto per la prima volta da Martin Fowler (che è stato identificato come un anti-pattern comune).

L'iniezione di dipendenza basata sulla costruzione è preferita rispetto a un Service Locator in quanto promuove la visibilità dei parametri richiesti e promuove il test dell'unità più semplice.

Il problema con l'uso di un Service Locator non è che si prende una dipendenza da una particolare implementazione di Service Locator (anche se questo può anche essere un problema), ma che si tratta di un anti-modello in buona fede. Darà ai consumatori della tua API un'esperienza orribile per gli sviluppatori e peggiorerà la tua vita come sviluppatore di manutenzione perché dovrai usare considerevoli quantità di energia cerebrale per comprendere le implicazioni di ogni cambiamento che fai.

Il compilatore può offrire tanto aiuto sia ai consumatori che ai produttori quando viene utilizzata l'iniezione del costruttore, ma nessuna di tale assistenza è disponibile per le API che si basano su Service Locator.

Inoltre, in particolare, infrange anche la Legge di Demetra

La Legge di Demetra (LoD) o Principio della conoscenza minima è una linea guida di progettazione per lo sviluppo di software, in particolare programmi orientati agli oggetti. Nella sua forma generale, il LoD è un caso specifico di accoppiamento libero.

La legge di Demetra per le funzioni richiede che un metodo M di un oggetto O possa invocare solo i metodi dei seguenti tipi di oggetti:

  1. O stesso
  2. Parametri di M.
  3. qualsiasi oggetto creato / istanziato all'interno di M
  4. Oggetti componenti diretti di O.
  5. una variabile globale, accessibile da O, nell'ambito di M

In particolare, un oggetto dovrebbe evitare di invocare metodi di un oggetto membro restituito da un altro metodo. Per molti linguaggi moderni orientati agli oggetti che usano un punto come identificatore di campo, la legge può essere dichiarata semplicemente come "usa solo un punto". Cioè, il codice abMethod () infrange la legge dove a.Method () no. Come semplice esempio, quando si vuole portare a spasso un cane, sarebbe folle comandare alle zampe del cane di camminare direttamente; invece si comanda al cane e si lascia prendere cura delle proprie gambe.

Il modo migliore

In effetti, il modo migliore è rimuovere la chiamata del localizzatore di servizio all'interno della classe e passare il proprietario corretto come parametro all'interno del suo costruttore. Anche se ciò significa che hai una classe di servizio che esegue una ricerca del proprietario e quindi la passa al costruttore della classe

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

  FdmReportBaseData := TdmRBReport(ownerClass).dmReportBaseData;
end;{- .Create }

3
Ottima risposta, e oggetti di scena a chiunque abbia escogitato questa meravigliosa analogia: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 delle difficoltà con avere oggetti figlio che sanno troppo del genitore è che si finiscono per implementare modelli che possono (e molto spesso lo fanno) accoppiarsi troppo strettamente, il che crea forti mal di testa da dipendenza e spesso in seguito diventa molto difficile modificare e mantenere in sicurezza più tardi.

A seconda della profondità con cui le tue due classi sono collegate, sembra che la descrizione di Fowler dell'invidia della caratteristica o degli odori del codice di intimità inappropriata sia evidente.

Sembra che sia necessario caricare o leggere una classe con i dati, nel qual caso è possibile utilizzare una serie di schemi alternativi per interrompere la dipendenza tra il bambino e la sua catena di genitori e sembra che sia necessario delegare l'attività di accesso la tua classe di dati piuttosto che rendere la classe di accesso ai dati responsabile di fare tutto da sola.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.