Por que Bertrand Meyer acha que a subclasse é a única maneira de estender um módulo "fechado"?

19

Na Construção de Software Orientada a Objetos de Meyer (1988), ele define o princípio aberto / fechado da seguinte forma:

  • Um módulo será considerado aberto se ainda estiver disponível para extensão. Por exemplo, deve ser possível adicionar campos às estruturas de dados que ele contém ou novos elementos ao conjunto de funções que ele executa.
  • Diz-se que um módulo está fechado se estiver disponível para uso por outros módulos. Isso pressupõe que o módulo tenha recebido uma descrição estável e bem definida (a interface no sentido de ocultar informações).

Ele continua dizendo:

Se você reabrir um módulo, também deverá reabrir todos os seus clientes para atualizá-los, pois eles dependem da versão antiga. … [Este problema] surge sempre que um módulo deve ser estendido por uma nova função ou elemento de dados, provocando alterações em clientes diretos e indiretos. ... Com abordagens clássicas de design e programação, não há como escrever módulos abertos e fechados.

A solução de Meyer para esse dilema é: nunca estenda um módulo de biblioteca modificando as classes existentes; em vez disso, escreva um novo módulo que subclasse as classes existentes e faça com que novos clientes dependam desse novo módulo.

Agora, em 1988, eu estava escrevendo programas de brinquedo (processuais) no Turbo Pascal e Blankenship Basic, e minha experiência profissional do século XXI é na JVM, no CLR e em linguagens dinâmicas, então não sei o que Meyer quis dizer por "abordagens clássicas de design e programação".

O exemplo concreto de Meyer de por que os módulos clientes devem ser reabertos (uma instrução switch em uma enumeração que agora possui mais membros, exigindo mais casos) parece bastante razoável, mas ele quase não justifica a afirmação de que toda vez que você adiciona funcionalidade a uma biblioteca módulo, você precisa atualizar todos os seus clientes .

Existe uma razão histórica para que essa afirmação parecesse evidente em 1988? Por exemplo, adicionar funções ou estruturas de dados a uma biblioteca estática C alterou o layout de forma que, mesmo com APIs compatíveis com versões anteriores, os clientes precisavam ser recompilados? Ou Meyer está realmente apenas falando de um mecanismo para reforçar a compatibilidade com versões anteriores da API?

David Moles
fonte
3
Pergunta interessante! Tenho a sensação de que a resposta estará de alguma forma relacionada à diferença fundamental entre Tipos de Dados Abstratos e Abstração de Dados Orientada a Objetos , que são os dois mecanismos dominantes de abstração de dados na Programação Modular (a que Betrand Meyer se refere como "abordagens clássicas ") e Programação Orientada a Objetos (leia os comentários!), respectivamente.
Jörg W Mittag
Isso é estranho. Parece flagrantemente contradito pela realidade (mesmo em 1988). Além disso, sua abordagem defendida resultaria em uma proliferação inútil de módulos.
@ dan1111: A abordagem da Eiffel à herança, incluindo, mas não se limitando à sua abordagem à herança múltipla, é diferente de C ++, Java, C # etc. etc., portanto, não é de surpreender que a abordagem seja diferente. Ele desenvolveu Eiffel especificamente para apoiar seus pontos de vista sobre OO, afinal.
Jörg W Mittag

Respostas:

18

Até onde eu sei, essa pergunta foi respondida pelo próprio Bertrand Meyer, e a resposta é que essa afirmação não é precisa. Com abordagens clássicas de design e programação, de fato pode haver uma maneira de escrever módulos abertos e fechados.

Para descobrir isso, você precisa estudar a segunda edição deste livro (publicada nove anos depois, em 1997). De acordo com o Prefácio da segunda edição , é

não uma atualização, mas o resultado de uma reformulação completa. Nenhum parágrafo da versão original foi deixado intocado. (Dificilmente uma única linha, na verdade.)

Em particular, a declaração que confunde você se foi. Ainda existe um capítulo de princípio Aberto-Fechado em "§3.3 Cinco Princípios", e há uma discussão mais aprofundada sobre esse tópico em "§14.7 Introdução à herança", mas a declaração da primeira edição não existe mais.

Em vez disso, o que está focado é como é mais conveniente e idiomático na abordagem OO, em oposição às formas anteriores,

Graças à herança, os desenvolvedores de OO podem adotar uma abordagem muito mais incremental ao desenvolvimento de software do que era possível com os métodos anteriores ... (§3.3)

Esse duplo requisito (aberto e fechado) parece um dilema, e as estruturas clássicas dos módulos não oferecem pistas. Mas a herança resolve. Uma classe é fechada, pois pode ser compilada, armazenada em uma biblioteca, em linha de base e usada por classes de clientes. Mas também é aberto, pois qualquer nova classe pode usá-la como pai, adicionando novos recursos e redeclarando os recursos herdados; Nesse processo, não há necessidade de alterar o original ou incomodar seus clientes ... (§14.7)

Como você também parece se perguntar sobre o que "abordagens clássicas" Meyer quis dizer aqui, você pode encontrar uma explicação delas em §4.7 Estruturas modulares tradicionais . Esta seção explica que essas significam "bibliotecas de rotinas" e "pacotes" (para o último, o autor diz que o termo é retirado do Ada e menciona outras línguas com esse recurso - clusters no CLU e módulos no Modula).

Se você pensar bem, nenhuma dessas abordagens foi originalmente planejada para ajudar na escrita de código que adere ao princípio de aberto-fechado. Isso poderia levar o autor a uma avaliação um tanto prematura que foi corrigida posteriormente na segunda edição.


Quanto ao que especificamente fez o autor mudar de idéia sobre essa afirmação entre a primeira e a segunda edição, acho que podemos encontrar uma resposta, novamente, no próprio livro, ou seja, na Parte F: Aplicação do método em vários idiomas e ambientes " . Neste capítulo, o autor discute como métodos orientados a objetos podem ser usados ​​em idiomas mais antigos:

Linguagens clássicas como o Fortran não são OO, mas as pessoas que ainda precisam usá-las ... podem aplicar o máximo possível de idéias OO dentro das limitações dessas abordagens mais antigas.

Em particular, nesta parte, Meyer explica em detalhes como seria possível implementar a herança (com algumas ressalvas e limitações, mas ainda) em C e até em Fortran.

Veja bem, isso realmente exige uma revisão dessa declaração da primeira edição. Parece praticamente impossível explicar como conciliar "com abordagens clássicas ... não há como" com exemplos realistas de como exatamente isso pode ser feito.

mosquito
fonte
Interessante, e com certeza vou ter que tentar obter a segunda edição, mas ainda não está claro para mim por que mesmo uma biblioteca "clássica" não OO não poderia adicionar (pelo menos certos tipos de) recursos sem perturbar sua clientes.
David Moles
@DavidMoles coisa é, ele poderia , e última parte da minha resposta explica que, e que Meyer-se conta de que (quando ele reformulado para 2nd Edition) e até deu exemplos de como isso pode ser feito. "Quanto ao que especificamente fez o autor mudar de idéia ..." etc
gnat
Hmm. Não vejo "a versão 2 desta biblioteca, que substitui a versão 1 e é compatível com ela, adiciona as seguintes funções ..." como "herança", exceto da maneira conceitual mais ampla possível.
David Moles
(Inheritance, para mim, implica que a versão 1 ainda está por aí e chamou pela versão 2.)
David Moles
O @DavidMoles substituir pela versão 2 (como alterar código-fonte e recompilar ) não se qualificaria como "fechado para modificação", basta verificar isso no artigo da Wikipedia : "entidade pode permitir que seu comportamento seja estendido sem modificar seu código-fonte ... "
gnat