C'è qualche ben nota preoccupazione sull'avere classe astratta con pochi metodi astratti? [chiuso]

3

Mi è stato chiesto di spiegare perché può essere brutto avere una classe astratta che implementa la maggior parte dei metodi (come un caso estremo, solo un metodo astratto) e ha molte sottoclassi.

Oltre a dover guardare un sacco di codice se si modifica la firma di quel metodo non potrei pensare a nient'altro. La mia risposta non sembra soddisfare l'interrogante.

C'è qualche inconveniente documentato nell'usare le classi astratte in questo modo?

UPDATE: sto cambiando il titolo della domanda per renderlo meno basato sull'opinione pubblica.

    
posta algiogia 13.04.2016 - 13:01
fonte

2 risposte

4

Alcuni svantaggi con il tuo approccio:

  1. Stai accoppiando un gran numero di sottoclassi a quella singola classe base.
  2. Stai rischiando di imbatterti nel problema della classe base fragile in quanto sarà difficile essere certi che cambiando la classe base non interromperà una classe derivata senza controllarli tutti.
  3. Stai diffondendo la funzionalità su più classi, mettendo a rischio "la responsabilità frammentata".

Potresti indirizzare tutti quelli avendo una singola classe che accetta un oggetto funzione / metodo / delegato / comando (usa come appropriato per la tua lingua specifica) come parametro per il costruttore. Quindi, anziché avere un numero elevato di classi, hai una classe, una raccolta di funzioni (potenzialmente raggruppate insieme) e poi usi ad esempio una fabbrica per creare oggetti con diverse funzioni iniettate come richiesto.

    
risposta data 13.04.2016 - 13:22
fonte
1

Non c'è niente di male in questo. In realtà, è un modello abbastanza potente e ampiamente utilizzato. L'ho sentito chiamare "leva API", perché implementando solo un metodo (di solito piuttosto semplice), si ottiene in cambio una grande quantità di funzionalità. In altre parole, proprio come con una leva, puoi trasformare un piccolo sforzo in un sacco di risultati.

Alcuni esempi di dove è utilizzato in natura sono:

  • API Enumerable di Ruby: a partire da Ruby 2.3, Enumerable ha 48 metodi concreti ( map , flat_map , find , reduce , ... tutti i metodi che ci si aspetterebbe da un oggetto iterabile ), tutti implementati in termini di un unico metodo astratto: each . Sottoclassi, come Array , Hash , Set , Enumerator , ecc, devono solo implementare un modo per iterare su se stessi (cioè each ) e ottenere gratuitamente tutti gli altri metodi. Praticamente qualsiasi cosa che sia anche lontanamente simile alla collezione eredita da Enumerable .
  • Comparable API di Ruby: le sottoclassi devono solo implementare <=> (l'operatore di confronto combinato) e guadagnare < , <= , > , >= e between? gratuitamente. Praticamente qualsiasi cosa eredita da Comparable .
  • L'API FunctionN di Scala: devi solo implementare apply e il tuo oggetto può essere trattato come una funzione. Molte cose ereditano da FunctionN , ad esempio Set eredita da Function1[T, Boolean] e Array eredita da Function1[Int, T] . FunctionN fornisce una serie di metodi concreti per la composizione e la trasformazione di funzioni, tutte basate su apply .
  • L'API Traversable di Scala: simile a Enumerable di Ruby, devi solo implementare foreach e ottenere gratuitamente quasi 100 metodi concreti. Praticamente tutte le raccolte implementano Traversable .
  • Lo stesso vale per Iterable : implementa iterator e ottieni tutto il resto gratuitamente. (Infatti: Iterable eredita da Traversable e implementa foreach anche in termini di iterator .)

E, naturalmente, in generale, una classe con un singolo metodo astratto è isomorfo a un tipo di funzione, ed è spesso usata in questo modo. Per es.

  • in Python, ogni oggetto con un metodo __call__ è considerato un "callable" (cioè simile a una funzione) e il metodo __call__ può essere chiamato con lo zucchero della sintassi come foo() ,
  • in Ruby, ogni oggetto con un metodo call è considerato come funzione e il metodo call può essere chiamato con lo zucchero della sintassi come foo.() ,
  • in Scala, ogni oggetto con un metodo apply è considerato come funzione e il metodo apply può essere chiamato con lo zucchero della sintassi come foo() , e
  • in Java, ogni tipo con un singolo metodo astratto è considerato un tipo di funzione e le istanze di quel tipo possono essere costruite utilizzando la sintassi letterale lambda
risposta data 13.04.2016 - 14:31
fonte

Leggi altre domande sui tag