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

18

Nella costruzione software orientata agli oggetti di Meyer (1988) definisce il principio aperto / chiuso come segue:

  • A module will be said to be open if it is still available for extension. For example, it should be possible to add fields to the data structures it contains, or new elements to the set of functions it performs.
  • A module will be said to be closed if it is available for use by other modules. This assumes that the module has been given a well-defined, stable description (the interface in the sense of information hiding).

Continua dicendo:

If you reopen a module, you must also reopen all its clients to update them, since they rely on the old version. … [This problem] arises every time a module must be extended by a new function or data element, triggering changes in direct and indirect clients. ... With classical approaches to design and programming, there is no way to write modules that are both open and closed.

La soluzione di Meyer a questo dilemma è: non estendere mai un modulo di libreria modificando le classi esistenti; invece, scrivi un nuovo modulo che sottoclassi le classi esistenti e che i nuovi client dipendano da quel nuovo modulo.

Ora, nel 1988, stavo scrivendo programmi giocattolo (procedurale) in Turbo Pascal e Blankenship Basic, e la mia esperienza professionale del 21 ° secolo è sulla JVM, sul CLR e in lingue dinamiche, quindi non so cosa intendeva Meyer con "approcci classici al design e alla programmazione".

L'esempio concreto di Meyer dei perché i moduli client devono essere riaperti (un'istruzione switch su un'enumerazione che ora ha più membri, che richiede più casi) sembra abbastanza ragionevole, ma non giustifica quasi la asserzione che ogni volta aggiungi funzionalità a un modulo di libreria, devi aggiornare tutti i suoi clienti .

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

    
posta David Moles 02.10.2015 - 23:56
fonte

1 risposta

17

Per quanto posso dire, a questa domanda è stata data risposta da Bertrand Meyer stesso, e la risposta è, questa affermazione non è accurata. Con gli approcci classici alla progettazione e alla programmazione, infatti può essere un modo per scrivere moduli aperti e chiusi.

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

not an update but the result of a thorough reworking. Not a paragraph of the original version has been left untouched. (Hardly a single line, actually.)

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

Ciò che invece è focalizzato su come è più conveniente e più idiomatico nell'approccio OO rispetto ai modi precedenti,

Thanks to inheritance, O-O developers can adopt a much more incremental approach to software development than used to be possible with earlier methods...(§3.3)

This double requirement (open and closed) looks like a dilemma, and classical module structures offer no clue. But inheritance solves it. A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as a parent, adding new features and redeclaring inherited features; in this process there is no need to change the original or to disturb its clients... (§14.7)

Visto che anche tu ti stai chiedendo su quali "approcci classici" intendesse Meyer, puoi trovare una spiegazione di questi in §4.7 Strutture Modulari Tradizionali . Questa sezione spiega che queste significano "librerie di routine" e "pacchetti" (per quest'ultimo, l'autore dice che il termine è tratto 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 inteso a facilitare la scrittura di codice che aderisce al principio di open-closed. Ciò potrebbe portare l'autore alla loro valutazione un po 'prematura che è stata successivamente corretta nella seconda edizione.

Per quanto riguarda ciò che l'autore ha specificamente fatto cambiare idea su questa affermazione tra la prima e la seconda edizione, penso che si possa trovare una risposta, ancora una volta, nel libro stesso, vale a dire in Parte F: Applicare il metodo in vari lingue e ambienti ". In questo capitolo, l'autore discute su come i metodi orientati agli oggetti possono essere usati nelle lingue più vecchie:

Classical languages such as Fortran are not O-O at all, but people who must still use them... may want to apply as many O-O ideas as feasible within the limitations of these older approaches.

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

Vedi, questo richiede davvero di rivedere questa affermazione dalla prima edizione. Sembra praticamente impossibile spiegare come riconciliare "con approcci classici ... non c'è modo" con esempi realistici su come esattamente può essere fatto.

    
risposta data 03.10.2015 - 23:56
fonte