Perché (/ ha) Bertrand Meyer pensa che la sottoclasse sia l'unico modo per estendere un modulo "chiuso"?


19

In Meyer Object-Oriented Software Construction (1988) definisce il principio aperto / chiuso come segue:

  • Si dirà che un modulo sarà aperto se è ancora disponibile per l'estensione. Ad esempio, dovrebbe essere possibile aggiungere campi alle strutture dati che contiene o nuovi elementi all'insieme di funzioni che esegue.
  • Si dirà che un modulo sarà chiuso se è disponibile per l'uso da parte di altri moduli. Questo presuppone che al modulo sia stata data una descrizione ben definita e stabile (l'interfaccia nel senso di nascondere le informazioni).

Continua dicendo:

Se riapri un modulo, devi anche riaprire tutti i suoi client per aggiornarli, poiché fanno affidamento sulla versione precedente. ... [Questo problema] si presenta ogni volta che un modulo deve essere esteso da una nuova funzione o elemento dati, innescando cambiamenti nei client diretti e indiretti. ... Con approcci classici alla progettazione e alla programmazione, non c'è modo di scrivere moduli sia aperti che chiusi.

La soluzione di Meyer a questo dilemma è: non estendere mai un modulo di biblioteca modificando le classi esistenti; scrivere invece un nuovo modulo che subclasse le classi esistenti e fare in modo che i nuovi client dipendano da quel nuovo modulo.

Ora, nel 1988, stavo scrivendo programmi giocattolo (procedurali) in Turbo Pascal e Blankenship Basic, e la mia esperienza professionale del 21 ° secolo è sulla JVM, il CLR e in linguaggi dinamici, quindi non so cosa significasse Meyer con "approcci classici alla progettazione e alla programmazione".

Un esempio concreto di Meyer del motivo per cui i moduli client devono essere riaperti (un'istruzione switch su un'enumerazione che ora ha più membri, che richiedono più casi) sembra abbastanza ragionevole, ma non giustifica quasi l'asserzione che ogni volta che si aggiunge funzionalità a una libreria modulo, è necessario aggiornare tutti i suoi client .

C'è una ragione storica per cui questa affermazione sembrava evidente nel 1988? Ad esempio, l'aggiunta di funzioni o strutture di dati a una libreria statica C ha cambiato il layout in modo tale che, anche con le API compatibili con le versioni precedenti, i client dovevano essere ricompilati? O Meyer sta davvero parlando di un meccanismo per imporre la retrocompatibilità delle API?


3
Domanda interessante! Ho la sensazione che la risposta sarà in qualche modo correlata alla differenza fondamentale tra i tipi di dati astratti e l' astrazione dei dati orientata agli oggetti , che sono i due meccanismi di astrazione dei dati dominanti nella programmazione modulare (ciò che Betrand Meyer chiama "approcci classici" ") e Programmazione orientata agli oggetti (leggi i commenti!), rispettivamente.
Jörg W Mittag,

Questo è strano. Sembra palesemente contraddetto dalla realtà (anche nel 1988). Inoltre, il suo approccio sostenuto comporterebbe una proliferazione inutile di moduli.

@ dan1111: l'approccio di Eiffel all'ereditarietà, incluso, ma non limitato al suo approccio all'ereditarietà multipla, è diverso da C ++, Java, C #, ecc., quindi non sorprende che l'approccio sia diverso. Dopotutto, ha sviluppato Eiffel appositamente per supportare le sue opinioni su OO.
Jörg W Mittag,

Risposte:


18

Per quanto ne so, Bertrand Meyer ha risposto a questa domanda e la risposta è che questa affermazione non è precisa. Con approcci classici alla progettazione e alla programmazione, può effettivamente esserci un modo per scrivere moduli sia aperti che chiusi.

Per scoprirlo, devi studiare la seconda edizione di questo libro (pubblicato nove anni dopo, nel 1997). Secondo Foreword alla seconda edizione , lo è

non un aggiornamento ma il risultato di una profonda rielaborazione. Non un paragrafo della versione originale è rimasto intatto. (Difficilmente una sola riga, in realtà.)

In particolare, l'affermazione che ti confonde è sparita. C'è ancora un capitolo di principio aperto-chiuso in "§3.3 Cinque principi", e c'è un'ulteriore discussione approfondita di questo argomento in "§14.7 Introduzione all'ereditarietà", ma la dichiarazione della prima edizione non è più lì.

Quello che c'è invece si concentra su come sia più conveniente e idiomatico nell'approccio OO rispetto ai modi precedenti,

Grazie all'ereditarietà, gli sviluppatori OO possono adottare un approccio allo sviluppo del software molto più incrementale di quanto fosse possibile con metodi precedenti ... (§3.3)

Questo doppio requisito (aperto e chiuso) sembra un dilemma e le strutture dei moduli classici non offrono alcun indizio. Ma l'eredità lo risolve. Una classe è chiusa, poiché può essere compilata, archiviata in una libreria, baseline e utilizzata dalle classi client. Ma è anche aperto, dal momento che qualsiasi nuova classe può usarlo come genitore, aggiungendo nuove funzionalità e ridichiando le funzionalità ereditate; in questo processo non è necessario cambiare l'originale o disturbare i suoi clienti ... (§14.7)

Dal momento che sembri anche chiederti cosa significasse "approcci classici", Meyer qui, puoi trovare una spiegazione in §4.7 Strutture modulari tradizionali . Questa sezione spiega che questi significano "librerie di routine" e "pacchetti" (per quest'ultimo, l'autore dice che il termine è preso da Ada e menziona altre lingue che hanno questa caratteristica - cluster in CLU e moduli in Modula).

Se ci pensate, nessuno di questi approcci era originariamente destinato a facilitare la scrittura di codice che aderisce al principio aperto-chiuso. Ciò potrebbe condurre l'autore a una valutazione un po 'prematura che è stata successivamente corretta nella seconda edizione.


Quanto a ciò che l'autore ha fatto cambiare idea su quella frase tra la prima e la seconda edizione, penso che si possa trovare una risposta, ancora una volta, nel libro stesso, vale a dire nella parte F: applicare il metodo in varie lingue e ambienti " . questo capitolo, l'autore discute come i metodi orientati agli oggetti possano essere usati nelle lingue più vecchie:

Le lingue classiche come Fortran non sono affatto OO, ma le persone che devono ancora usarle ... potrebbero voler applicare quante più idee OO possibili entro i limiti di questi approcci più vecchi.

In particolare, in questa parte Meyer spiega in dettaglio come sarebbe possibile implementare l'ereditarietà (con alcuni avvertimenti e limitazioni, ma ancora) in C e persino in Fortran.

Vedete, questo richiede davvero di rivedere quella dichiarazione della prima edizione. Sembra praticamente impossibile spiegare come conciliare "con gli approcci classici ... non c'è modo" con esempi realistici su come si possa fare esattamente .


Interessante, e dovrò sicuramente provare a conoscere la seconda edizione, ma non mi è ancora chiaro perché nemmeno una libreria "classica" non OO non possa aggiungere (almeno alcuni tipi di) funzionalità senza disturbare la sua clienti.
David Moles,

La cosa di @DavidMoles è, potrebbe , e l'ultima parte della mia risposta lo spiega, e che lo stesso Meyer lo ha realizzato (quando ha rielaborato per la 2a Edizione) e ha persino fornito esempi di come si può fare. "Quanto a ciò che ha fatto cambiare idea all'autore ..." ecc.
moscerino del

Hmm. Non vedo "la versione 2 di questa libreria, che sostituisce la versione 1 ed è retrocompatibile con essa, aggiunge le seguenti funzioni ..." come "eredità" tranne nel modo concettuale più ampio possibile.
David Moles,

(L'ereditarietà, per me, implica che la versione 1 sia ancora in circolazione e chiamata dalla versione 2.)
David Moles,

@DavidMoles sostituisce con la versione 2 (come in, cambia codice sorgente e ricompila ) non si qualificherebbe come "chiuso per modifica", puoi semplicemente verificarlo nell'articolo di Wikipedia : "l'entità può consentire l'estensione del suo comportamento senza modificare il suo codice sorgente ... "
moscerino il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.