Ci sono dei veri inconvenienti al concatenamento del metodo autoreferenziale?

14

Recentemente ho suggerito di implementare un metodo di concatenamento per una certa classe in un determinato progetto, in modo da poter migliorare la leggibilità del codice. Ho avuto un "interfacce fluenti non dovrebbe essere implementato solo per comodità, ma per la semantica" risposta e ha avuto il mio suggerimento abbattuto. Ho risposto che non stavo suggerendo un'interfaccia fluente ma un metodo che si concatenasse (entrambi possono essere confusi l'uno con l'altro, leggi in fondo) per migliorare la leggibilità e il comfort di codifica, il suggerimento è stato abbattuto di nuovo.

Comunque, questo mi ha fatto pensare che forse potrei incorrere in una cattiva pratica restituendo sempre "questo" in metodi che non dovrebbero restituire nulla (ad es. setter).

La mia domanda è: l'applicazione della convenzione precedente può essere considerata una cattiva pratica o un abuso? Perché? Non penso che ci siano dei difetti di rendimento, o ci sono?.

    
posta dukeofgaming 30.05.2011 - 10:36
fonte

4 risposte

9

No

Come sottolineato Kent Beck , il codice viene letto molto più spesso di quanto non sia scritto.

Se il concatenamento dei metodi rende il tuo codice più leggibile, usa il metodo di concatenamento.

    
risposta data 30.05.2011 - 18:24
fonte
8

Sì, ci sono degli svantaggi

Il codice che è facile da leggere è buono, ma fa anche attenzione a ciò che il codice comunica . Quando i metodi di un oggetto restituiscono sempre l'oggetto, comunica un paio di cose:

  1. Ho bisogno di una configurazione avanzata che non sia necessariamente ovvia in quale ordine le cose dovrebbero essere impostate o configurate
  2. Ogni successiva chiamata al metodo costruisce sull'ultimo

Caso di utilizzo valido: query del database ad hoc

Le librerie di classi esistono nella maggior parte delle lingue che consentono di eseguire query su un database senza ricorrere a codice SQL codificato. Prendi l'Entity Framework per .NET come esempio:

DBContext db = new DBContext();
List<Post> posts = db.Posts
    .Where(post => post.Title.Contains("Test"))
    .OrderBy(post => post.DateCreated)
    .ToList();

Questa è un'interfaccia fluente dove ogni successiva chiamata al metodo si basa sulla precedente. La lettura di queste chiamate ha senso logico nel contesto dell'interrogazione di un database.

Caso di utilizzo non valido: zucchero sintattico per l'impostazione delle proprietà

Ora utilizziamo lo stesso modello con la classe Post :

public class Post
{
    public string Title { get; set; }
    public DateTime DateCreated { get; set; }
    public string Body { get; set; }

    public Post SetTitle(string title)
    {
        Title = title;

        return this;
    }

    public Post SetDateCreated(DateTime created)
    {
        DateCreated = created;

        return this;
    }

    public Post SetBody(string body)
    {
        Body = body;

        return this;
    }
}

Ora vediamo come useresti questa classe:

Post post = new Post()
    .SetTitle("Test")
    .SetDateCreated(DateTime.Now)
    .SetBody("Just a test");

Quando vedo questo codice, faccio subito questa domanda: "Dopo aver chiamato SetBody , interroga il database? Devo chiamare un altro metodo per dire" Ho finito? ""

Che cosa il metodo concatenato chiama comunica con il codice utilizzando la classe Post ?

  1. Ho una configurazione complicata
  2. Ogni chiamata di metodo crea sul precedente

Questo in realtà è vero? No. La classe Post non ha una configurazione complicata. Impostando il titolo, la data creata e il corpo non si costruiscono l'un l'altro verso un obiettivo finale più complicato. Hai schiacciato un piolo quadrato in un buco rotondo.

Lo svantaggio del concatenamento del metodo autoreferenziale è che comunichi che sono necessarie più chiamate al metodo per fare qualcosa e che ogni chiamata si basa sull'ultima. Se ciò non è vero, il concatenamento dei metodi potrebbe comunicare la cosa sbagliata agli altri programmatori.

Quando i tuoi colleghi hanno detto:

Fluent interfaces should not be implemented just for convenience, but for semantics

Erano assolutamente corretti. Un'interfaccia fluente, o metodo di concatenamento, comunica qualcosa di per sé che potrebbe non essere vero.

    
risposta data 16.12.2014 - 14:53
fonte
1

Lo svantaggio principale è una perdita di chiarezza. Ad esempio:

x.foo();
x.bar();
x.baz();

Poiché nessuna di queste istruzioni restituisce un valore (o se lo fa, è stato ignorato), possono essere utili solo se producono effetti collaterali. Contrasto a:

x.foo()
 .bar()
 .baz();

Innanzitutto, non è chiaro che bar e baz operino su oggetti dello stesso tipo di x , a meno che tu non sappia che la classe di x è l'unica classe con questi metodi. Anche se questo è il caso, non è chiaro se i metodi funzionano su x , o nuovi oggetti.

Evidenziare le parti del codice che hanno effetti collaterali è utile, dal momento che è necessario tenere traccia di tali effetti collaterali sulla ragione del programma. Devi valutare la potenziale perdita di chiarezza rispetto al guadagno potenziale nella facilità di scrivere il codice, ma considera anche che il codice viene letto più di quanto non sia scritto.

    
risposta data 16.12.2014 - 17:53
fonte
0

Considera tutti gli elementi stilistici dei linguaggi di programmazione (al contrario del linguaggio della macchina per scrivere a mano) e indebolisce l'argomento "semantica non convenienza".

Un sacco di ciò che facciamo come ingegneri è fornire convenienza intorno alla semantica.

    
risposta data 16.12.2014 - 17:32
fonte