Qualsiasi regola empirica quando scegliere il metodo di concatenamento [duplicato]

2

Questa domanda mi infastidisce da anni, quindi immagina un codice che nel 99% assomiglia ad un elenco non sequenziale (ordine non importa) di alcune chiamate al metodo degli oggetti specifici - e quelle chiamate sono indipendenti l'una dall'altra, come :

conf.addCollection(...);
conf.addRule(...);
conf.registerEvent(...);

Posso implementare il metodo di concatenamento per avere qualcosa come:

 conf.addCollection(...)
     .addRule(...)
     .registerEvent(...);

ma dovrei? In altre parole - dovrei implementare il concatenamento dei metodi solo per non riferire più volte la stessa variabile? Se non dovessi, in questo caso particolare, ancora, quali sono le regole empiriche quando è meglio avere una catena?

    
posta shabunc 30.06.2017 - 11:27
fonte

1 risposta

1

Il problema con questo è che cambi il tipo di ritorno del metodo in un modo non ovvio. Il tipo di ritorno del metodo è Conf , ma in realtà , in realtà non restituisce un valore utile affatto . Qualcuno che guarda la documentazione dell'API potrebbe quindi chiedersi perché il metodo restituisce Conf , quando in realtà il metodo ha solo un effetto collaterale e nessun ritorno utile.

Questo mi porta al secondo problema: funziona solo se i metodi hanno effetti collaterali e nessun valore di ritorno utile. Ma dovresti sforzarti di scrivere metodi puramente funzionali, referenzialmente trasparenti senza effetti collaterali, quindi questo promuove un cattivo design.

Il primo problema può essere alleviato in una lingua con un sistema di tipi abbastanza preciso. Ad esempio, in Scala, puoi rendere il tipo restituito il tipo singleton this.type , il che significa che il metodo restituisce this e solo this (non può restituire un oggetto diverso dello stesso digita come this , può solo restituire this stesso). Quindi, definiresti i tuoi metodi in questo modo:

class Conf {
  def +=(coll: Collection): this.type = {
    // do stuff with 'coll'
    this
  }

  def +=(rule: Rule): this.type = {
    // do stuff with 'rule'
    this
  }

  def +=(event: Event): this.type = {
    // do stuff with 'event'
    this
  }
}

e usalo in questo modo:

conf += someCollection += someRule += someEvent

[Nota: ho scelto un metodo sovraccarico per i diversi tipi, potresti ovviamente usare nomi diversi, se vuoi, allora sarebbe qualcosa come conf addCollection someCollection addRule someRule registerEvent someEvent .]

Questo lascia comunque il secondo problema: ha senso solo con gli effetti collaterali.

Molte lingue contengono messaggi in cascata che consentono di inviare più messaggi allo stesso destinatario, ad es. in Smalltalk, il tuo esempio potrebbe essere scritto come:

conf 
  addCollection: aCollection;
  addRule:       aRule;
  registerEvent: anEvent.

Quindi, non c'è semplicemente bisogno di concatenare messaggi in questo modo.

In Ruby, potresti (ab) usare BasicObject#instance_eval , sebbene io consiglierei contro di esso:

conf.instance_eval do
  add_collection a_collection
  add_rule       a_rule
  register_event an_event
end

Eviterei principalmente di fare questo, poiché implica che gli effetti collaterali e gli effetti collaterali dovrebbero essere evitati in generale, a meno che non sia assolutamente necessario. Se lo fai, dovrebbe essere chiaramente documentato, idealmente tramite il sistema dei tipi.

Nota: il messaggio concatenato in cui ogni messaggio nella catena restituisce un oggetto nuovo ed è puramente funzionale e referenziale-trasparente senza effetti collaterali, è una questione completamente diversa. Finché non viola la legge di Demeter per le funzioni, è perfettamente ok.

In un Fluent DSL, dove è chiaro che i tipi di metodi restituiti non hanno senso da soli e devono essere compresi solo come parte della struttura DSL nel suo insieme, la situazione è diversa. Tuttavia, in un Fluent DSL, in genere si restituisce comunque un nuovo oggetto builder ogni volta, per facilitare l'inferenza del tipo, il completamento intelligente del codice, ecc. Ne ho fornito un esempio in questa risposta a una domanda sui Fluent DSL (più precisamente, Fluent Builders).

    
risposta data 30.06.2017 - 12:01
fonte

Leggi altre domande sui tag