Entendo o que o SOLID deve realizar e o uso regularmente em situações em que a modularidade é importante e seus objetivos são claramente úteis. No entanto, duas coisas me impedem de aplicá-lo consistentemente na minha base de código:
Eu quero evitar abstrações prematuras. Na minha experiência, desenhar linhas de abstração sem casos de uso concretos (do tipo que existe agora ou no futuro previsível ) os leva a serem desenhados nos lugares errados. Quando tento modificar esse código, as linhas de abstração atrapalham, em vez de ajudar. Portanto, tenho a tendência de errar ao não desenhar nenhuma linha de abstração até ter uma boa idéia de onde elas seriam úteis.
Acho difícil justificar o aumento da modularidade por si só, se isso torna meu código mais detalhado, mais difícil de entender etc. e não elimina nenhuma duplicação. Acho que o código processual simples, com procedimentos bem acoplados ou de objeto de Deus, às vezes é mais fácil de entender do que o código ravioli muito bem fatorado, porque o fluxo é simples e linear. Também é muito mais fácil escrever.
Por outro lado, essa mentalidade geralmente leva a objetos de Deus. Geralmente refatoro-as de maneira conservadora, adicionando linhas de abstração claras apenas quando vejo padrões claros emergindo. O que há de errado com os objetos de Deus e com o código fortemente associado se você não precisar claramente de mais modularidade, não possui duplicação significativa e o código é legível?
EDIT: No que diz respeito aos princípios individuais do SOLID, eu quis enfatizar que a Substituição de Liskov é IMHO uma formalização do senso comum e deve ser aplicada em todos os lugares, pois as abstrações não fazem sentido se não forem. Além disso, todas as classes devem ter uma única responsabilidade em algum nível de abstração, embora possa ser um nível muito alto, com os detalhes da implementação compactados em uma enorme classe de 2.000 linhas. Basicamente, suas abstrações devem fazer sentido onde você escolhe abstrair. Os princípios que eu questiono nos casos em que a modularidade não é claramente útil são de abertura aberta, segregação de interface e especialmente inversão de dependência, já que se trata de modularidade, e não apenas abstrações que fazem sentido.
fonte
Respostas:
A seguir, são princípios simples que você pode aplicar para ajudá-lo a entender como equilibrar o design do seu sistema:
S
no SOLID. Você pode ter um objeto muito grande em termos de número de métodos ou quantidade de dados e ainda assim manter esse princípio. Tomemos, por exemplo, o objeto Ruby String. Esse objeto tem mais métodos do que você pode usar, mas ainda tem apenas uma responsabilidade: mantenha uma sequência de texto para o aplicativo. Assim que seu objeto começar a assumir uma nova responsabilidade, comece a pensar muito sobre isso. O problema de manutenção está se perguntando "onde eu esperaria encontrar esse código se tivesse problemas com ele mais tarde?"Em essência, você está tentando fazer o possível para não dar um tiro no pé quando chegar a hora de manter o software. Se seus objetos grandes são abstrações razoáveis, há poucas razões para dividi-los apenas porque alguém criou uma métrica que diz que uma classe não deve ter mais que X linhas / métodos / propriedades / etc. Se alguma coisa, essas são diretrizes e não regras rígidas e rápidas.
fonte
Eu acho que na maioria das vezes você respondeu sua própria pergunta. O SOLID é um conjunto de diretrizes sobre como refatorar seu código, quando os conceitos precisam subir níveis de abstração.
Como todas as outras disciplinas criativas, não há absolutos - apenas trocas. Quanto mais você o faz, mais fácil se torna decidir quando é suficiente para o seu domínio ou requisitos atuais.
Dito isto - abstrair é o cerne do desenvolvimento de software -, se não houver uma boa razão para não fazê-lo, a prática o fará melhor e você terá uma ideia das vantagens e desvantagens. Então favor, a menos que se torne prejudicial.
fonte
Isso está parcialmente certo e parcialmente errado.
Errado
Este não é um motivo para impedir a aplicação dos princípios OO / SOLID. Apenas impede a aplicação muito cedo.
Qual é...
Direito
Não refatorar o código até que seja refatorável; quando os requisitos estiverem completos. Ou, quando os "casos de uso" estiverem todos lá, como você diz.
Ao trabalhar sozinho ou em um silo
O problema de não fazer OO não é imediatamente óbvio. Depois de escrever a primeira versão de um programa:
3/4 dessas coisas boas morrem rapidamente em pouco tempo.
Finalmente, você reconhece que possui objetos de Deus (objetos com muitas funções) e reconhece funções individuais. Encapsular. Coloque-os em pastas. Use interfaces de vez em quando. Faça um favor a si mesmo para manutenção a longo prazo. Especialmente porque os métodos não-refatorados tendem a inchar infinitamente e se tornar métodos de Deus.
Em uma equipe
Os problemas em não fazer OO são imediatamente perceptíveis. Um bom código OO é pelo menos um pouco auto-documentável e legível. Especialmente quando combinado com um bom código verde. Além disso, a falta de estrutura dificulta a separação de tarefas e integração muito mais complexa.
fonte
Não há nada errado com objetos grandes e códigos fortemente acoplados quando eles são apropriados e quando nada de melhor engenharia é necessário . Esta é apenas outra manifestação de uma regra prática que se transforma em dogma.
O acoplamento frouxo e objetos pequenos e simples tendem a fornecer benefícios específicos em muitos casos comuns; portanto, é uma boa ideia usá-los em geral. O problema está nas pessoas que não entendem a lógica por trás dos princípios tentando cegamente aplicá-los universalmente, mesmo onde eles não se aplicam.
fonte
Eu costumo abordar isso mais do ponto de vista Você não vai precisar. Esta postagem se concentrará em um item em particular: herança.
No meu design inicial, crio uma hierarquia de coisas que sei que haverá duas ou mais delas. Provavelmente, eles precisarão muito do mesmo código, por isso vale a pena projetá-lo desde o início. Depois que o código inicial está em vigor e preciso adicionar mais recursos / funcionalidades, olho para o que tenho e penso: "Alguma coisa que eu já implementei a mesma ou semelhante função?" Nesse caso, é mais provável que uma nova abstração implore para ser lançada.
Um bom exemplo disso é uma estrutura MVC. Começando do zero, você cria uma página grande com um código por trás. Mas então você deseja adicionar outra página. No entanto, o único código por trás já implementa grande parte da lógica que a nova página exige. Então, você abstrai uma
Controller
classe / interface que implementa a lógica específica dessa página, deixando as coisas comuns no código "deus" original.fonte
Se você sabe como desenvolvedor quando NÃO aplicar os princípios por causa do futuro do código, é bom para você.
Espero que o SOLID permaneça no fundo de sua mente para saber quando as coisas precisam ser abstraídas para evitar a complexidade e melhorar a qualidade do código, se começar a se degradar nos valores que você declarou.
Mais importante ainda, considere que a maioria dos desenvolvedores está fazendo o trabalho diário e não se importa tanto com você. Se você inicialmente definiu métodos de execução longa, quais são os outros desenvolvedores que entram no código quando pensam em mantê-lo ou estendê-lo? ... Sim, você adivinhou, MAIOR funções, maiores classes de execução e MAIS objetos de deus, e eles não têm os princípios em mente para poder capturar o código crescente e encapsular corretamente. Seu código "legível" agora se torna uma bagunça podre.
Portanto, você pode argumentar que um desenvolvedor ruim fará isso independentemente, mas pelo menos você terá uma vantagem melhor se as coisas forem mantidas simples e bem organizadas desde o início.
Não me importo particularmente com um "Mapa de Registro" global ou um "Objeto Divino" se eles forem simples. Realmente depende do design do sistema, às vezes você pode se safar e permanecer simples, às vezes não.
Também não devemos esquecer que grandes funções e objetos de Deus podem ser extremamente difíceis de testar. Se você deseja permanecer ágil e se sentir "seguro" ao refazer e alterar o código, precisará de testes. É difícil escrever testes quando funções ou objetos fazem muitas coisas.
fonte
O problema com um objeto divino é que você geralmente pode dividi-lo de maneira lucrativa em pedaços. Por definição, faz mais de uma coisa. Todo o objetivo de dividi-lo em classes menores é para que você possa ter uma classe que faça uma coisa bem (e também não deve esticar a definição de "uma coisa"). Isso significa que, para qualquer classe, depois de saber a única coisa que ela deve fazer, você poderá lê-la com bastante facilidade e dizer se está fazendo ou não essa coisa corretamente.
Eu acho que é essa coisa a como tendo muito modularidade e muita flexibilidade, mas que é mais de um problema de excesso de desenho e excesso de engenharia, onde você está contabilização de requisitos que você nem sequer sabem o cliente quer. Muitas idéias de design são orientadas para facilitar o controle de alterações na forma como o código é executado, mas se ninguém espera a alteração, é inútil incluir a flexibilidade.
fonte
Queryer
que otimiza as consultas ao banco de dados, consulta o RDBMS e analisa os resultados em objetos a serem retornados. Se houver apenas uma maneira de fazer isso no seu aplicativo eQueryer
for hermeticamente selado e encapsular dessa maneira, isso fará apenas uma coisa. Se houver várias maneiras de fazer isso e alguém puder se importar com os detalhes de uma parte, isso fará várias coisas e você poderá dividir.Eu uso uma diretriz mais simples: se você pode escrever testes de unidade e não possui duplicação de código, é abstrato o suficiente. Além disso, você está em uma boa posição para refatoração posterior.
Além disso, você deve manter o SOLID em mente, mas mais como orientação do que como regra.
fonte
Se o aplicativo for pequeno o suficiente, tudo poderá ser mantido. Em aplicações maiores, os objetos de Deus rapidamente se tornam um problema. Eventualmente, implementar um novo recurso requer a modificação de 17 objetos de Deus. As pessoas não se saem muito bem seguindo os procedimentos de 17 etapas. Os objetos de Deus estão sendo constantemente modificados por vários desenvolvedores, e essas mudanças precisam ser mescladas repetidamente. Você não quer ir para lá.
fonte
Partilho suas preocupações sobre abstração excessiva e inadequada, mas não estou necessariamente tão preocupado com abstração prematura.
Isso provavelmente parece uma contradição, mas é improvável que o uso de uma abstração cause um problema, desde que você não o comprometa muito cedo - ou seja, desde que você esteja disposto e seja capaz de refatorar, se necessário.
O que isso implica é algum grau de prototipagem, experimentação e retorno no código.
Dito isto, não há uma regra determinística simples a seguir que resolva todos os problemas. A experiência conta muito, mas você precisa adquiri-la cometendo erros. E sempre há mais erros para cometer que os erros anteriores não o tenham preparado.
Mesmo assim, trate os princípios do livro didático como ponto de partida e aprenda muito a programar e ver como esses princípios funcionam. Se fosse possível fornecer diretrizes melhores, mais precisas e confiáveis do que coisas como o SOLID, alguém provavelmente o faria agora - e mesmo que o tivesse, esses princípios aprimorados ainda seriam imperfeitos e as pessoas perguntariam sobre as limitações desses .
Bom trabalho também - se alguém pudesse fornecer um algoritmo claro e determinístico para projetar e codificar programas, haveria apenas um último programa para humanos escreverem - o programa que escreveria automaticamente todos os programas futuros sem intervenção humana.
fonte
Desenhar as linhas de abstração apropriadas é algo que se extrai da experiência. Você cometerá erros ao fazê-lo, o que é inegável.
O SOLID é uma forma concentrada dessa experiência que é entregue a você por pessoas que adquiriram essa experiência da maneira mais difícil. Você acertou em cheio quando diz que se abstém de criar abstração antes de perceber a necessidade. Bem, a experiência que o SOLID fornece ajuda a resolver problemas que você nunca existiu . Mas confie em mim ... existem muito reais.
O SOLID é sobre modularidade, modularidade é sobre manutenção e manutenção é sobre permitir que você mantenha um cronograma de trabalho humanizado quando o software chegar à produção e o cliente começar a perceber os erros que você não possui.
O maior benefício da modularidade é a testabilidade. Quanto mais modular o seu sistema, mais fácil é testar e mais rápido você pode construir bases sólidas para lidar com os aspectos mais difíceis do seu problema. Portanto, seu problema pode não exigir essa modularidade, mas apenas o fato de permitir que você crie códigos de teste melhores com mais rapidez é um acéfalo para lutar pela modularidade.
Ser ágil é tentar alcançar o delicado equilíbrio entre o envio rápido e o fornecimento de boa qualidade. Agile não significa que devemos cortar custos; de fato, os projetos ágeis mais bem-sucedidos com os quais me envolvi são os que prestaram muita atenção a essas diretrizes.
fonte
Um ponto importante que parece ter sido esquecido é que o SOLID é praticado junto com o TDD. O TDD tende a destacar o que é "apropriado" em seu contexto específico e ajuda a aliviar muitos dos equívocos que parecem estar nos conselhos.
fonte