Eu estava lendo este wiki no Stable Abstractions Principle (SAP) .
O SAP afirma que, quanto mais estável um pacote, mais abstrato ele deve ser. Isso implica que, se um pacote for menos estável (com maior probabilidade de alteração), deverá ser mais concreto. O que realmente não entendo é por que esse deveria ser o caso. Certamente, em todos os casos, independentemente da estabilidade, devemos depender de abstrações e ocultar a implementação concreta?
design
design-patterns
architecture
object-oriented-design
design-principles
SteveCallender
fonte
fonte
Respostas:
Pense nos seus pacotes como uma API, para pegar o exemplo do artigo, use definições para
Reader
withstring Reader.Read()
eWriter
withvoid Writer.Write(string)
como sua API abstrata.Você pode criar uma classe
Copy
com um métodoCopier.Copy(Reader, Writer)
e a implementaçãoWriter.Write(Reader.Read())
e talvez algumas verificações de sanidade.Agora, você faz implementações concretas, por exemplo
FileReader
,FileWriter
,KeyboardReader
eDownloadThingsFromTheInternetReader
.E se você quiser alterar sua implementação
FileReader
? Não tem problema, basta alterar a classe e recompilar.E se você quiser alterar a definição de sua abstração
Reader
? Opa, você não pode simplesmente mudar isso, mas você também vai ter que mudarCopier
,FileReader
,KeyboardReader
eDownloadThingsFromTheInternetReader
.Esta é a lógica por trás do Princípio da Abstração Estável: Torne suas concretizações menos estáveis que as abstrações.
fonte
Por causa de YAGNI .
Se você atualmente possui apenas uma implementação de uma coisa , por que se preocupar com uma camada extra e inútil? Isso só levará a complexidade desnecessária. Pior ainda, às vezes você fornece uma abstração pensando no dia em que uma segunda implementação chegará ... e esse dia nunca acontece. Que desperdício de trabalho!
Também acho que a verdadeira pergunta a se fazer não é "Preciso depender de abstrações?" mas sim "Preciso de modularidade?". E a modularidade nem sempre é necessária, veja abaixo.
Na empresa em que estou trabalhando, alguns dos softwares que desenvolvo estão fortemente ligados a algum dispositivo de hardware com o qual ele deve se comunicar. Esses dispositivos são desenvolvidos para cumprir objetivos muito específicos e são praticamente modulares. :-) Uma vez que o primeiro dispositivo produzido sai da fábrica e está instalado em algum lugar, tanto o seu firmware e hardware nunca pode mudar, nunca .
Portanto, posso ter certeza de que algumas partes do software nunca evoluirão. Essas partes não precisam depender de abstrações, pois existe apenas uma implementação e essa nunca será alterada. Declarar abstrações nessas partes do código apenas confundirá todos e levará mais tempo (sem produzir nenhum valor).
fonte
Acho que você talvez esteja confuso com a palavra estável escolhida por Robert Martin. Aqui é onde eu acho que a confusão começa:
Se você ler o artigo original , verá (grifo meu):
Eu sempre lutei com a escolha do autor da palavra estável , pois eu (como você) tendem a pensar no aspecto "provável" da estabilidade, ou seja, improvável que mude . A dificuldade implica que a alteração desse módulo quebrará muitos outros módulos e será muito trabalho para corrigir o código.
Martin também usa as palavras independente e responsável , que para mim transmitem muito mais significado. Em seu seminário de treinamento, ele usou uma metáfora sobre os pais de crianças em crescimento e como eles deveriam ser "responsáveis", porque seus filhos dependem deles. Divórcio, desemprego, encarceramento etc. são ótimos exemplos do mundo real do impacto negativo que as mudanças nos pais terão sobre os filhos. Portanto, os pais devem ser "estáveis" para o benefício de seus filhos. A propósito, essa metáfora de filhos / pais não está necessariamente relacionada à herança no POO!
Então, seguindo o espírito de "responsável", criei significados alternativos para difícil mudar (ou não deveria mudar ):
Então, inserindo essas definições na instrução
Vamos citar o Princípio de Abstrações Estáveis (SAP), enfatizando as palavras confusas estável / instável:
Esclarecendo-o sem estas palavras confusas:
TL; DR
O título da sua pergunta pergunta:
Eu acho que se você criar as abstrações corretamente (por exemplo, elas existem porque muito código depende delas), não haverá desvantagens significativas.
fonte
Abstrações são coisas difíceis de mudar no software porque tudo depende delas. Se o seu pacote for alterado com frequência e fornecer abstrações, as pessoas que dependem dele serão forçadas a reescrever grande parte do código delas quando você alterar alguma coisa. Mas se o seu pacote instável fornecer algumas implementações concretas, muito menos código terá que ser reescrito após as alterações.
Portanto, se seu pacote for alterado com frequência, ele deve fornecer concretos, não abstrações. Caso contrário ... quem diabos vai usá-lo? ;)
fonte
Lembre-se da métrica de estabilidade de Martin e do que ele entende por "estabilidade":
Ou:
Ou seja, um pacote é considerado completamente instável se todas as suas dependências forem de saída: ele usa outras coisas, mas nada o usa. Nesse caso, só faz sentido que isso seja concreto. Também será o tipo de código mais fácil de alterar, já que nada mais o usa e, portanto, nada mais pode quebrar se esse código for modificado.
Enquanto isso, quando você tem o cenário oposto de "estabilidade" completa com um pacote usado por uma ou mais coisas, mas não usa nada por si só, como um pacote central usado pelo software, é quando Martin diz que isso deve ser feito. abstrato. Isso também é reforçado pela parte DIP do SOLI (D), o Princípio de Inversão de Dependências, que basicamente afirma que as dependências devem fluir uniformemente em direção a abstrações para códigos de baixo e alto nível.
Ou seja, as dependências devem fluir uniformemente em direção à "estabilidade" e, mais precisamente, as dependências devem fluir em direção a pacotes com mais dependências de entrada do que as dependências de saída e, além disso, as dependências devem fluir em direção a abstrações. A essência da lógica por trás disso é que as abstrações fornecem espaço para substituir um subtipo por outro, oferecendo esse grau de flexibilidade para que as partes concretas que implementam a interface sejam alteradas sem interromper as dependências recebidas nessa interface abstrata.
Bem, na verdade, eu discordo de Martin aqui pelo menos no meu domínio, e aqui preciso introduzir uma nova definição de "estabilidade", como "falta de motivos para mudar". Nesse caso, eu diria que as dependências devem fluir para a estabilidade, mas as interfaces abstratas não ajudam se as interfaces abstratas forem instáveis (pela minha definição de "instável", como propenso a ser repetidamente alterado, e não o de Martin). Se os desenvolvedores não conseguirem corrigir as abstrações e os clientes mudarem de idéia repetidamente de maneiras que tornem as tentativas abstratas de modelar o software incompletas ou ineficazes, não seremos mais beneficiados pela flexibilidade aprimorada das interfaces abstratas para proteger o sistema contra alterações em cascata que quebram a dependência . No meu caso pessoal, encontrei mecanismos ECS, como os encontrados em jogos AAA,mais concreta : em relação aos dados brutos, mas esses dados são altamente estáveis (como em "é improvável que precise ser alterado"). Eu sempre achei a probabilidade de algo que requer mudanças futuras ser uma métrica mais útil do que a proporção de acoplamentos eferentes para o total para orientar as decisões de SE.
Então, eu alteraria um pouco o DIP e apenas diria: "as dependências devem fluir para componentes com a menor probabilidade de exigir alterações adicionais", independentemente de esses componentes serem interfaces abstratas ou dados brutos. Tudo o que importa para mim é a probabilidade de que eles possam exigir mudanças diretas no design. As abstrações são úteis apenas neste contexto de estabilidade se algo, por ser abstrato, reduzir essa probabilidade.
Para muitos contextos que podem ser o caso de engenheiros e clientes decentes que antecipam as necessidades do software antecipadamente e projetam abstrações estáveis (como imutáveis), enquanto essas abstrações oferecem a eles todo o espaço necessário para trocar implementações concretas. Mas em alguns domínios, as abstrações podem ser instáveis e propensas a serem inadequadas, enquanto os dados exigidos pelo mecanismo podem ser muito mais fáceis de antecipar e estabilizar com antecedência. Portanto, nesses casos, pode ser realmente mais benéfico do ponto de vista de manutenção (a facilidade de alterar e estender o sistema) para que as dependências fluam em direção aos dados, em vez de abstrações. Em um ECS, as partes mais instáveis (como as partes mais frequentemente alteradas) são geralmente a funcionalidade que reside nos sistemas (
PhysicsSystem
, por exemplo), enquanto as partes mais estáveis (como é menos provável que sejam alteradas) são os componentes que consistem apenas em dados brutos (MotionComponent
por exemplo) que todos os sistemas usam.fonte