La presenza di molti metodi duplicati e gerarchie di ereditarietà nelle API della GUI (Java Swing, Android SDK) viola i principi di progettazione SOLID?

5

Per coloro che non conoscono i principi SOLIDI puoi iniziare a leggere qui: articolo di Wikipedia . Tuttavia, la maggior parte della mia comprensione deriva da: link

Indipendentemente da ciò, sembra che ogni interfaccia della GUI in Java (anche quella di Adobe Flex onestamente) sia piena di dozzine di metodi che sono duplicati in molte classi. Le gerarchie di ereditarietà profonde sono la norma e la composizione non è decisamente preferita all'ereditarietà . Questa sembra essere una chiara violazione di SRP e ISP .

Quello che mi piacerebbe sapere è, ho ragione nel ritenere che queste strutture stiano violando i principi di progettazione SOLID? Se è così, allora perché?

Risposta a Snowman

Non sono d'accordo che SWING sia un esempio di SOLID. Consente di utilizzare l'esempio JButton:

1) SRP: il metodo isDoubleBuffered va oltre la responsabilità di JButton di rappresentare un pulsante dell'interfaccia utente. Questo metodo rappresenta un dettaglio di implementazione che non ha nulla a che fare con il concetto astratto di un pulsante dell'interfaccia utente.

2) OCP: se voglio cambiare il modo in cui JButton gestisce gli eventi, devo sostituire completamente la classe JButton.

3) LSP: l'implementazione di JButton non può essere facilmente sostituita con altre implementazioni. JButton eredita la maggior parte dei dettagli di implementazione direttamente. Se voglio cambiare il modo in cui JButton crea tooltip, assegna i listener di azioni, o accedo al contesto grafico, devo buttare via tutto di SWING. Non posso semplicemente rilasciare una nuova classe che gestisce quelle cose poiché JButton eredita direttamente da JComponent.

4) ISP: l'interfaccia pubblica JButton è disseminata di dozons di metodi da ogni classe da cui eredita. È letteralmente un gruppo di dumping di metodi pubblici da JComponent, Container, Compound, ecc. Se l'interfaccia di JButton aderisce all'ISP, saranno disponibili solo 10 metodi pubblicamente esposti.

5) DIP: JButton espone pubblicamente i metodi dei suoi dettagli di implementazione concreti. Se applicasse la vera inversione di dipendenza, elementi come icone, layout, gestione degli eventi, ecc. Sarebbero delegati a classi separate che l'utente potrebbe facilmente cambiare per ottenere nuove funzionalità.

    
posta Bernard Igiri 09.10.2014 - 15:25
fonte

2 risposte

3

In generale, non sono d'accordo che i framework della GUI violano SOLID . Potrebbero esserci delle eccezioni, ma questo avvolge la mia esperienza con più framework:

  • SRP: una classe GUI ha in genere una sola responsabilità. Forse sta rispondendo a un evento, rendendo un oggetto, o forse è "rappresentare questo elemento dell'interfaccia utente". Quest'ultimo potrebbe sembrare che non sia una responsabilità "singola" perché quella responsabilità è legare tutto insieme (ad esempio JButton o JPanel ). La chiave da cercare è che questi oggetti "grandi" spesso delegano ad altri oggetti specializzati e gestiscono in dettaglio un aspetto dell'oggetto (gestione degli eventi, rendering, ecc.).

  • Apri / Chiuso: il tuo esempio mostra come le classi UI sono definitivamente aperte per l'estensione: c'è molta eredità in corso. Si potrebbe sostenere che le classi UI violano l'aspetto "closed for modification" poiché sono in genere molto configurabili e possono essere modificate per fare molte cose in modo diverso, ma questo è uno degli scopi di queste classi per cominciare.

  • Sostituzione di Liskov: in quasi tutti i framework che ho visto, le sottoclassi aggiungono più funzionalità e consentono comunque il comportamento della classe genitore. Non ha sempre senso, ma è supportato. Ad esempio, puoi annidare gli elementi dell'interfaccia all'interno di un pulsante se estende un frame (forse si potrebbe aggiungere un'icona a un pulsante in questo modo, un elemento dell'immagine annidato all'interno di un pulsante nonostante il "pulsante" non venga comunemente pensato come elemento principale ). Questo principio è fondamentale per la natura nidificata dei framework dell'interfaccia utente.

  • Segregazione dell'interfaccia: la mia esperienza principale qui è con Java, dove ci sono molte piccole interfacce per vari ascoltatori e gestori. Sembra una buona forma.

  • Inversione di dipendenza: insieme all'argomento Liskov, molti framework fanno affidamento sulle astrazioni. Ad esempio, potrebbe esserci un'interfaccia di alto livello o una classe astratta per rappresentare tutti i componenti dell'interfaccia utente. I componenti principali possono quindi accettare qualsiasi componente di alto livello come un bambino, che consente layout arbitrariamente complessi tramite la nidificazione.

Nel complesso, penso che ci sia un argomento strong per per i framework GUI che aderiscono a SOLID.

    
risposta data 09.10.2014 - 20:10
fonte
2

In generale, i principi SOLID di solito sono soddisfatti (vedi risposta di Snowman )

Ma dal momento che hai menzionato l'SDK di Android, farò un piccolo dettaglio su come i principi SOLID risultino troppo brevi in questa particolare API.

Principio di responsabilità singola . La classe View è responsabile per

  • posizionamento
  • disegno
  • elaborazione dell'evento
  • gestire un carico enorme di altri callback.

Quindi, se consideri l'SRP come "Una classe dovrebbe avere solo una ragione per cambiare" - hai già perso l'argomento. L'hanno provato creando ViewGroup e facendo in modo che ListView utilizzi Adapter per i dati effettivi. Queste cose funzionano davvero molto bene.

Apri principio chiuso . Sebbene sia fondamentalmente aperto per estensione, sfortunatamente solo per via ereditaria. Non è possibile in alcun modo modificare il comportamento (ad es. Qualcosa nel disegno o nel processo di layout) tramite un modello strategico o qualcosa di simile. Tutto è una costante, quindi devi scegliere tra le opzioni esistenti.

Il principio di sostituzione di Liskov è ovviamente soddisfatto tramite i requisiti del linguaggio java. Tuttavia, ricordo che abbiamo avuto problemi con TextView o EditText in passato, se il metodo ereditato ha restituito lo stesso tipo e lo stesso nome è stato assegnato; comportato in modo diverso comunque. La post-condizione non è stata soddisfatta. Peccato che non ricordi esattamente di cosa si trattasse. Qualcosa con il Focus o le stesse proprietà con risultati diversi o qualcosa del genere.

Segregazione interfaccia . Cosa dovrei dire? L'interfaccia di base View è inquinata con metodi su metodi. Perché lo scorrimento è implementato (e accessibile) tramite la classe View ? Ricorda i richiami che ho menzionato prima? Ma in generale, se stai cercando di "implementare" l'interfaccia "Vista" (estendendo la classe) hai solo bisogno di sovrascrivere i metodi di cui hai veramente bisogno, ma questo è più simile a una funzione di linguaggio in java. Se lo vedi più pedante, realizzerai lo scrolling ignorando la classe anche se non ne hai bisogno.

L'inversione delle dipendenze sembra essere ok in Android. Ma sono sicuro che se la cerchi davvero, troverai abbastanza punti nel quadro in cui qualcosa dipende da un'implementazione invece che da un'astrazione. Non sono sicuro che il 34% di chiamate diinstanceof nella classe View siano un segno di buone astrazioni.

In generale, Android SDK fa del suo meglio per soddisfare tutti i principi. So anche che è abbastanza grande e deve anche tenere conto delle prestazioni, quindi è più difficile farlo soddisfare tutti i principi.

Dovrei aggiungere, che probabilmente non sarei in grado di fare di meglio.

    
risposta data 09.10.2014 - 23:08
fonte

Leggi altre domande sui tag