"Extends is evil" vs. OCP?

7

Per quanto ho capito (?), l'idea "Estendi è malvagia" è in diretta opposizione al principio di Open Closed?

Qui il concetto di OCP viene presentato come intrinsecamente usando Extends:
link

Mentre per esempio in questo articolo, l'atto di estendere è considerato un reato capitale?
link

Qual è il modo corretto per soddisfare entrambi gli OCP e non utilizzare Extends? Considera una semplice classe BulletinBoard con Post, che può essere NewsPosts o GuestbookPost con proprietà diverse, ma gli stessi metodi / comportamento.

    
posta Juha Untinen 04.09.2013 - 12:52
fonte

3 risposte

15

No, non lo è.

Principali principi aperti per invocare funzionalità incapsulanti nelle classi. Non dice se l'interfaccia debba essere definita dalla classe base o dall'interfaccia.

"extends is evil" ti dice di preferire l'implementazione di interfacce sull'estensione delle classi base.

Poiché il principio open-closed va bene con entrambi, non sono contrari.

Per quanto riguarda "extends is evil", ci sono diversi motivi per cui viene usata l'ereditarietà e Java non ti permette di distinguerli (C ++ ha un'eredità protetta e privata che aiuta qui):

  1. Implementazione di un'interfaccia comune per altro codice da utilizzare.
  2. Riutilizzo della funzionalità di base.
  3. Comportamento specializzato della classe per lo più generica.

Dovresti utilizzare le interfacce per la prima in modo da non legarti alla gerarchia dell'ereditarietà singola. Questo è il messaggio principale di "extends is evil".

Per il secondo il metodo preferito è la composizione, anche in questo caso non ti leghi alla gerarchia dell'ereditarietà singola.

Ma per i terzi, l'estensione (astratta) della classe base è appropriata. Il tuo esempio cade nel terzo caso.

Nota comunque che la classe base astratta dovrebbe probabilmente ancora implementare un'interfaccia e il codice dovrebbe usarla ogni volta che non ha bisogno della particolare classe base, che è la maggior parte delle volte.

Nota:

evil, adj.: Something you should avoid most of the time, but not something you should avoid all the time. For example, you will end up using these "evil" things whenever they are "the least evil of the evil alternatives." (C++ FAQ)

Questo vale in tutti i casi simili in cui qualcuno chiama una tecnica "cattiva". Ciò non significa che non devi usarlo, mai. Basta sottolineare che esiste un'alternativa preferita.

    
risposta data 04.09.2013 - 13:13
fonte
1

Il primo link usa effettivamente il modello di strategia (metodo astratto con implementazione in ogni sottoclasse di Shape), quindi non c'è nulla di male lì, perché come dice l'autore, non devi modificare GraphicEditor per supportare nuove forme. Tieni presente però che, in questo esempio, sei interessato a un'interfaccia comune condivisa (il metodo draw ()). Puoi ottenerlo usando un metodo astratto e l'ereditarietà, ma potresti ottenere la stessa cosa con le normali interfacce.
OCP ed estensioni non sono per definizione ortogonali tra loro; è solo che molte persone ricorrono istintivamente all'eredità per le proprietà comuni, non per un comportamento comune. Questo può spesso portare a tutti i tipi di codice spaghetti stravolto.
Come per il tuo esempio: non stai parlando di comportamento, solo di proprietà, quindi la domanda è: che tipo di logica c'è, e dove, che sarebbe diverso in base al tipo di Post?

    
risposta data 04.09.2013 - 13:11
fonte
1

Bene, il mio 2c è, seguendo le linee guida di Joshua Bloch come scritto nel suo libro Effective Java:

Preferisci interfacce a classi astratte (è il titolo del capitolo 4.18 nella seconda edizione di quel libro)

La parola chiave qui è "Preferisci" nello stesso modo in cui i principi che citi sono principi e non leggi. Sono linee guida, da utilizzare il più delle volte, ma da tenere sempre in considerazione se la situazione attuale è pertinente a un dato principio o a un altro.

Più precisamente, sì "extends" è male, dovremmo usare le interfacce per dichiarare i tipi di base. Come mai il punto del libro che cito (e che personalmente apprezzo molto) è che, data un'interfaccia, si ottengono benefici dal realizzare alcune implementazioni astratte di esso come classi di supporto per l'estensione di tale interfaccia. Immagina di creare un'interfaccia Shape, che, tra le altre cose, ha un metodo "setColor (Color)". Se sai che nel contesto in cui la tua interfaccia sarà usata (da te o da qualcun altro), c'è una grande possibilità che la maggior parte delle forme sia nera, puoi creare una classe astratta chiamata BlackShape (implementa Shape) che assegna Nero al colore della forma fin dall'inizio e lascia solo altri metodi astratti. La cosa importante in questo caso è documentare correttamente il fatto che l'interfaccia Shape dovrebbe essere la base di qualsiasi implementazione Shape concreta in generale, e solo coloro che hanno bisogno di forme nere dovrebbero usare la classe astratta BlackShape se lo preferiscono. In questo modo ti assicurerai che nessuno inavvertitamente pensi che BlackShape sia la base di tutte le forme, e eviterai le violazioni del Principio di sostituzione di Liskov (ad esempio) dove viene creata una forma rossa estendendo la classe BlackShape e sovrascrivendo il setColor (Colore ) metodo.

    
risposta data 04.09.2013 - 13:23
fonte

Leggi altre domande sui tag