I metodi di supporto possono essere sovrautilizzati?

4

Nello spirito di divide and conquer , ho preso l'abitudine di usare metodi di supporto quasi ovunque, anche per le attività più semplici.

Come esempio molto semplice, supponiamo di avere una sorta di programma di libreria scritto in Java 8 che si occupa di libri, e ha il metodo pubblico filterByAuthor che accetta come parametri un elenco di libri e il nome di un autore . quando si progetta l'implementazione, lo pseudo codice potrebbe apparire in questo modo:

filter the list of books by
    comparing the book's author to the specified one

e quando si implementa effettivamente il codice, avrò qualcosa di simile

public static List<Book> filterByAuthor(List<Book> books, String author) {
    return filterBooks(books, book -> isAuthor(book, author));
}

private static List<Book> filterBooks(List<Book> books, Predicate<Book> predicate) {
    List<Book> filtered = books.stream()
            .filter(predicate)
            .collect(Collectors.toList());
    return filtered;
}

private static boolean isAuthor(Book book, String author) {
    return book.getAuthor().equals(author);
}

Secondo me questo è buono perché rende il codice più semplice per

  • leggi
  • debug
  • cambia

Ma lo stesso metodo potrebbe essere facilmente scritto come

public static List<Book>(List<Book> books, String author) {
    List<Book> filtered = books.stream()
            .filter(book -> book.getAuthor().equals(author))
            .collect(Collectors.toList());
    return filtered;
}

Sono preoccupato che se questo tipo di progettazione viene utilizzato in sistemi più grandi potrebbe essere un uso eccessivo dei metodi di supporto. (ad esempio ingombrando lo spazio dei nomi). Ci sono degli svantaggi in questo "uso eccessivo"?

    
posta Josie Thompson 24.10.2016 - 02:40
fonte

2 risposte

6

I metodi di supporto risolvono i due problemi di leggibilità e riusabilità . I metodi di supporto possono rendere il codice più leggibile incapsulando alcune logiche sotto un nome migliore e più diretto. I metodi di supporto possono rendere il codice più riutilizzabile dividendo una parte di logica che potrebbe essere riutilizzata altrove. Attraverso questi obiettivi gemelli, possiamo valutare il valore di un metodo di supporto.

Il tuo metodo filterBooks non aggiunge un'enorme quantità in termini di leggibilità aggiuntiva rispetto al metodo filterByAuthor . Stai ancora filtrando un elenco di libri. Tuttavia, può aggiungere valore in termini di riusabilità. Se devi scrivere un metodo filterByPublisher , ad esempio, puoi farlo abbastanza facilmente e senza molta duplicazione.

Il tuo metodo isAuthor aggiunge valore alla leggibilità e può anche aggiungere valore in riusabilità. Piuttosto che dover analizzare che un libro è stato creato da qualcuno se la stringa dell'autore del libro è uguale al nome dato, leggiamo solo ciò che intendiamo.

La questione se ciascuno di questi fornisce un valore sufficiente dipenderà dalla situazione, dagli standard del team e dal valore potenziale di leggibilità e riusabilità. Se è probabile che i metodi di supporto vengano riutilizzati, il valore è lì. Se i metodi di supporto migliorano la manutenibilità attraverso la semplicità e la leggibilità, il valore è lì.

I metodi che non aggiungono abbastanza valore possono iniziare a ingombrare il codice base. Potrebbero esserci più linee di codice. Il numero di metodi su una classe sarà maggiore, anche se molti di essi sono privati. Quando esegui il debug di un bit di codice, le tracce dello stack saranno più profonde perché devono discendere in tutti questi metodi di supporto e trovare dove un particolare bit di logica errata potrebbe richiedere uno sforzo maggiore. Poiché molti metodi di supporto sono metodi privati sulla stessa classe, il test potrebbe risultare un po 'più complicato in quanto i test dovranno soddisfare i minimi di copertura sugli helper privati oltre ai metodi pubblici testati.

I metodi di supporto possono fornire ulteriori vantaggi oltre alla semplice leggibilità e riusabilità. Mentre estrapoliamo elementi di logica, possiamo vedere le opportunità per il refactoring.

Ad esempio, il metodo isAuthor ottiene il mio "Tell Do not Ask" avverte il formicolio. Sembra un metodo che appartiene meglio alla classe Book , come ad esempio un metodo come isAuthoredBy(String author) . Il metodo filterBooks sembra essere un metodo per cercare una classe su cui vivere. Può avere più senso come un metodo di istanza filterBy(Predicate<Book> predicate) su una classe Library o Bookshelf che incapsula un insieme di Book s.

    
risposta data 24.10.2016 - 04:48
fonte
3

I metodi di estrazione sono più difficili di quanto pensiate. Negli IDE moderni è facile avere l'IDE che estrae un metodo. Ma questo non rende facile estrarre bene un metodo.

In primo luogo, l'estrazione di espressioni banali non migliora necessariamente la leggibilità.

isAuthor(book, author)
book.getAuthor().equals(author)

Penso che il secondo sia migliore. Quando lo guardo, immediatamente vedo cosa fa. D'altra parte, devo indovinare a cosa sta facendo la funzione isAuthor . Affinché abbia senso usare una funzione, la chiamata funzionale dovrebbe essere più chiara dell'espressione, e non penso che sia vero qui.

In secondo luogo, l'estrazione ovvia non è necessariamente quella corretta. In questo caso, penso che un modo migliore per scrivere la tua funzione sarebbe:

public static List<Book> filterByAuthor(List<Book> books, String author) {
    return filterBooks(books, Book::getAuthor, author);
}

private static <T> List<Book> filterBooks(List<Book> books, Function<Book, T> getter, T value) {
    List<Book> filtered = books.stream()
            .filter(book -> getter(book).equals(value))
            .collect(Collectors.toList());
    return filtered;
}

Questa estrazione riesce a tirare più della logica di filtraggio comune nella funzione filtro, migliorando la riusabilità.

Ora, potresti non essere d'accordo con le mie valutazioni soggettive qui. Ma penso che sia vero che la creazione di metodi di aiuto senza mente può portare a un codice subottimale. Ogni estrazione deve essere attentamente considerata, non semplicemente estratta ogni volta che esiste un'espressione non atomica.

    
risposta data 24.10.2016 - 07:49
fonte

Leggi altre domande sui tag