Law of Demeter non impedisce il passaggio di oggetti nei costruttori di classi. Tuttavia, impedisce di recuperare lo stesso oggetto in un secondo momento e di chiamare un metodo su di esso per ottenere un valore scalare. Invece, si suppone che venga creato un metodo proxy che restituisca invece il valore scalare. La mia domanda è: perché è accettabile passare un oggetto in un costruttore di classi, ma inaccettabile per ottenere lo stesso oggetto in seguito e ricavarne un valore?Law of Demeter and Class Constructor
risposta
Poiché la legge di Demeter dice che non è necessario progettare l'interfaccia esterna di un oggetto per farlo apparire come se fosse composto da alcuni altri oggetti con interfacce conosciute, che i client possono semplicemente afferrare e accedere.
Passa un oggetto nel costruttore per dire al tuo nuovo oggetto come comportarsi, ma non è affar tuo se l'oggetto mantiene quell'oggetto parametro o ne conserva una copia, o lo guarda solo una volta e dimentica che sia mai esistito. Avendo un metodo getMyParameterBack, hai impegnato tutte le future implementazioni per essere in grado di produrre l'intero oggetto su richiesta e tutti i client devono accoppiarsi con due interfacce invece di una.
Ad esempio, se si passa un parametro URL al costruttore dell'oggetto HTTPRequest, ciò non significa che HTTPRequest deve avere un metodo getURL che restituisce un oggetto URL sul quale si prevede che il chiamante chiamerà getProtocol, getQueryString, ecc. Se qualcuno che ha un oggetto HTTPRequest potrebbe voler conoscere il protocollo della richiesta, dovrebbe (lo dice la legge) scoprirlo chiamando getProtocol sull'oggetto che ha, non su qualche altro oggetto che capita di sapere che HTTPRequest sta memorizzando internamente.
L'idea è di ridurre l'accoppiamento - senza la legge di Demetra, l'utente deve conoscere l'interfaccia per HTTPRequest e URL per ottenere il protocollo. Con la legge, hanno solo bisogno dell'interfaccia per HTTPRequest. E HTTPRequest.getProtocol() può chiaramente restituire "http" senza bisogno di qualche oggetto URL per essere coinvolto nella discussione.
Il fatto che a volte l'utente dell'oggetto richiesta sia quello che lo ha creato e che pertanto utilizza anche l'interfaccia URL per passare il parametro, non è né qui né li. Non tutti gli utenti di oggetti HTTPRequest li avranno creati loro stessi. Quindi i clienti che hanno diritto in base alla legge di accedere all'URL perché lo hanno creato da soli, possono farlo in quel modo piuttosto che ritirarlo dalla richiesta. I clienti che non hanno creato l'URL non possono.
Personalmente penso che la Legge di Demetra, come di solito indicato in forma semplice, sia incrinata. Dicono seriamente che se il mio oggetto ha un campo Nome stringa, e voglio sapere se il Nome contiene caratteri non ASCII, allora devo definire un metodo NameContainsNonASCIICharacters sul mio oggetto invece di guardare la stringa stessa, oppure aggiungi una funzione visitName alla classe prendendo una funzione di callback per aggirare la restrizione assicurando che la stringa sia un parametro per una funzione che ho scritto? Ciò non modifica affatto l'accoppiamento, sostituisce semplicemente i metodi getter con i metodi dei visitatori. Se ogni classe che restituisce un intero ha un set completo di operazioni aritmetiche, nel caso in cui io voglia manipolare il valore di ritorno? getPriceMultipliedBy (int n)? Sicuramente no.
Ciò che è utile, è che quando lo rompi puoi chiedertelo perché lo infrangi e se puoi progettare un'interfaccia migliore senza romperla. Spesso puoi, ma in realtà dipende da che tipo di oggetti stai parlando. Alcune interfacce possono essere tranquillamente accoppiate a vaste fette di codice, ad esempio interi, string e persino URL, che rappresentano concetti ampiamente utilizzati.
Vedi, un wrapper Uri è esattamente il tipo di classe ubiquitaria che escluderei da questa regola, proprio come una stringa. Visto come un mezzo per un fine, questa è una buona regola empirica, ma non è fine a se stessa. L'obiettivo è nascondere i dettagli interni dell'implementazione, non giocare. –
Sì, non avrei alcuna discussione se si chiamasse la Linea guida di Demetra, ma non è utile essere timidi quando si prova a greggi di gatti avvisare i programmatori. Non ho dubbi che, come esercizio accademico, applicarlo come una legge è interessante e fruttuoso e offre molte idee per migliorare la progettazione dell'interfaccia. IMO disegna la linea non appena installi di routine un'interfaccia in un'altra. A volte una X in realtà ha solo una Y, e tutti lo sanno :-) –
Ben detto. La mia preoccupazione per i gatti insani è che alcuni hanno la tendenza a vedere le buone idee come magiche. L'esempio stereotipato è il neolaureato che legge il libro GoF su Design Patterns e poi se ne va per cambiare TUTTO in un Singleton. –
L'idea è che parli solo con i tuoi amici più stretti. Quindi, non si esegue questa operazione ...
var a = new A();
var x = a.B.doSomething();
Invece di fare questo ...
var a = new A();
var x = a.doSomething(); // where a.doSomething might call b.doSomething();
Ha i suoi vantaggi, come le cose diventano più semplici per chi chiama (Car.Start() rispetto Car.Engine.Start()), ma ottieni molti piccoli metodi di avvolgimento. È inoltre possibile utilizzare Mediator pattern per mitigare questo tipo di "violazione".
Non ho mai sentito parlare di Law of Demeter, quindi ... è perfettamente logico che siano richieste le funzioni di comodità che delegano a B, ma "B getB() const" è proibito? Se è così, perché? –
Questa è l'idea della legge ... potrebbe essere riformulata "Parli solo con i tuoi amici più stretti". Qual è il vantaggio? Questa idea è stata intorno a lungo. In questi giorni stiamo scrivendo applicazioni molto liberamente accoppiate. –
La risposta di JP è abbastanza buona, quindi questo è solo un supplemento, non un disaccordo o altro sostituto.
Il modo in cui comprendo questa euristica è che una chiamata ad A non deve rompersi a causa del cambio di classe B. Quindi se si concatenano le chiamate con a.b.foo(), l'interfaccia di A diventa dipendente da B, violando la regola. Invece, dovresti chiamare a.BFoo(), che chiama b.foo() per te.
Questa è una buona regola empirica, ma può portare a un codice scomodo che non affronta realmente la dipendenza tanto quanto la racchiude. Ora A deve offrire BFoo per sempre, anche quando B non offre più Foo. Non molto di miglioramento e sarebbe probabilmente meglio in almeno alcuni casi se le modifiche a B hanno rotto il chiamante che vuole Foo, non B stesso.
Vorrei anche aggiungere che, in senso stretto, questa regola viene interrotta costantemente per un certo gruppo di classe ubiquitaria, come ad esempio lo spago. Forse è accettabile decidere quali classi sono ugualmente onnipresenti all'interno di un particolare livello di un'applicazione e ignorare liberamente la "Regola" di Demetra per loro.
Dovrei fare +1 su di te qui dato che hai detto lo stesso di me solo più breve e più veloce :-) –
Amd Ho restituito il favore poiché hai incluso un buon esempio. –
Potete fornire un collegamento alla "Legge del diametro"? – gahooa