Como posso planejar meu software para evitar reescritas e interdependências excessivas?

StackOverflow https://stackoverflow.com/questions/3472405

  •  28-09-2019
  •  | 
  •  

Pergunta

Estou escrevendo um controlador de motor que possui algumas interfaces (botões, Bluetooth, botões táteis), que é uma tarefa que está crescendo constantemente e se tornando maior do que eu imaginava.Eu tentei começar com módulos de baixo nível (por exemplo,escrever código para falar no barramento I2C), depois os acima (código para falar com um dispositivo específico no barramento I2C...), mas muitas vezes tenho que voltar aos módulos inferiores para lidar com peculiaridades que não acomodei.Isso leva muito tempo ou recebo um código realmente hackeado.

Meu alvo é um MCU de 8 bits, então de baixo para cima parece que posso explorar melhor o hardware.Se eu for de cima para baixo, não tenho nenhuma estrutura para construir ou testar/depurar.

Tentei elaborar alguns diagramas gerais e para níveis/drivers específicos, mas não tenho certeza de como estruturá-los para poder ser muito sistemático e evitar perder o sinal estranho que precisa subir de 2 a 3 camadas.

Eu acho que esse é o motivo de um diploma de CS?Sou engenheiro eletricista :P

Foi útil?

Solução

Parece que você esta no caminho certo.Às vezes, nenhum planejamento impedirá que você tenha que redesenhar ou refatorar partes de um sistema posteriormente.Experimente algumas das dicas a seguir:

  • Mantenha seu código em módulos separados por funções lógicas.
  • Não duplique o código; em vez disso, projete métodos reutilizáveis ​​para funcionalidade compartilhada.
  • Tente evitar a tentação de adicionar hacks para casos especiais.Eventualmente, isso se tornará insustentável.Em vez disso, ajuste e refatore pequenas seções o mais rápido possível.Tentar fazer uma grande reformulação no final será mais difícil.
  • Não tente projetar demais o sistema desde o início, pois você poderá estar desperdiçando seu tempo quando chegar à implementação real.
  • Mantenha os níveis mais baixos o mais simples possível e, em seguida, crie recursos mais avançados no topo.
  • Documente suas funções e escreva alguns testes unitários, especialmente depois de adicionar instruções condicionais complexas.
  • Tente detectar erros o mais alto possível na pilha.Por exemplo, fazendo validação de entrada e verificando valores de retorno.Isso tornará a depuração mais fácil.

Outras dicas

Ao trabalhar em código de várias camadas, é tentador mergulhar em uma camada mais baixa quando a API não permite que você faça exatamente o que deseja fazer. Isso é especialmente difícil ao escrever camadas mulitple ao mesmo tempo.

Aqui estão algumas sugestões:

  • Trate todas as outras camadas além da que você está trabalhando como se estivessem seladas. Criado por outra empresa, desenvolvedor, etc. Resista ao desejo de modificar outra camada para resolver um problema em sua camada atual.
  • Crie uma "camada de irmãos" para a que você está trabalhando. Isso é difícil de descrever em sentido abstrato, mas digamos que sua camada inferior seja uma camada de negócios, e o nível mais alto é uma interface do usuário, crie outra camada de interface do usuário para uma aplicação diferente. Agora, ter dois consumidores da mesma API pode ajudar a apontar o que deveria estar em cada camada.
  • Tente alternar a ordem em que você trabalha em camadas. Por exemplo, em alguns aplicativos, acho mais útil projetar a interface do usuário primeiro e depois descer a camada/banco de dados de negócios para fazer com que essa interface do usuário funcionasse como projetado. Outras vezes, faz melhor sentido Stat com o modelo de dados e trabalhar até uma interface do usuário. Mas o ponto é que você "pensa" de uma API de maneira diferente nesses dois cenários. E ter analisado o código de várias camadas de ambos os ângulos ajuda.
  • Contagem de experiências. Às vezes, apenas cometer o erro de código excessivamente acoplado é a única maneira de realmente aprender a evitá -lo. Em vez de planejar seu aplicativo ser perfeito, planeje ser imperfeito. Com isso, quero dizer, primeiro configure um ciclo rápido de desenvolvimento/teste/refattor, para que você possa se adaptar rapidamente aos erros que não verá até depois de cometê -los. Esta também é uma área em que a "prototipagem descartável" é útil. Faça um rascunho simples, aprenda com ele e jogue fora. A parte do arremesso é importante. Mesmo que seja incrível, comece a construir outro do zero. Você inevitável o tornará melhor (e na minha expiração, mais organizada) com base no que aprendeu com o protótipo.

É realmente mais uma questão de experiência do que seu diploma. Se você ainda está aprendendo coisas sobre como controlar o hardware, é claro que seu código vai mudar. Eu não iria agonizar por isso. No entanto, como o que você realmente está fazendo é prototipagem, você deve estar pronto para refatorar o código depois de funcionar. Remova redundâncias, compartimentalize dados e funcionalidades e organize suas interfaces para que faça sentido.

Minha experiência é que o código do driver do dispositivo precisa de design/implementação de cima para baixo e de baixo para cima, o que eu chamo de fora. Você sabe o que o usuário vai querer fazer e pode escrever essas interfaces, e você sabe o que o driver de baixo nível precisa fazer e escreve isso. Se eles não se encontrarem bem no meio, repense o layout do seu módulo.

Para melhorar, sujeite seu design e código a revisar por pessoas com mais experiência. Deixe o ego fora disso e apenas obtenha a opinião deles sobre o problema. Você também pode ler um livro sobre análise e design orientados a objetos (eu costumava gosta de livros de Peter Coad. Não sei quem os está escrevendo agora). Um bom mostrará exemplos de como particionar um problema em objetos com papéis e responsabilidades claras.

A outra peça, depois de terminar a prototipagem dos drivers e saber como controlar o hardware, é garantir que você tenha requisitos detalhados. Nada torce o código mais do que descobrir requisitos enquanto você está escrevendo. Você também pode tentar aprender UML e projetar com diagramas antes de escrever código. Isso não funciona para todos. Observe também que você não precisa codificar em um idioma que suporta construções orientadas a objetos para usar o OOD.

Se o seu problema é como criar abstrações adequadas, o que parece ser o caso, acho que a coisa mais importante que você pode fazer para aprender (além de pedir críticas de código de design/leitura de livros/código de leitura) é Pensar muito antes de começar a escrever código.

Geralmente acontece que você começa com uma idéia aproximada do que deseja e como deve ser feito e depois escreve o código. Mais tarde, você descobre que não pensou nas coisas e tem vários buracos que agora, porque investiu tempo em escrever o código, é difícil de corrigir, o que leva a um código de tempo ou hacky desperdiçado.

Pense muito em como criar um design que possa lidar facilmente com as alterações. Por exemplo, encapsule os dados que precisam viajar entre as camadas; portanto, se você descobrir mais tarde que perdeu um parâmetro crucial, poderá adicioná -los facilmente à estrutura sem precisar modificar o código em todos os lugares.

Tente "executar o design em sua cabeça", várias vezes, até ter certeza de que você considerou mais importantes detalhes e pode dizer (essa é a parte mais importante, porque sempre perderá as coisas ou os requisitos) que, se você perdeu algo, pode ajustar os módulos com relativa facilidade.

Uml pode ajudá -lo a estruturar o caminho para pensar no design. Certamente não é panacéia, mas mostra os diferentes critérios a serem considerados ao criar um design de software.

É um pouco como o conselho clássico do professor de xadrez: "Sente -se em suas mãos" :-)

Os motoristas são passíveis de uma abordagem de camada.

Seus motoristas podem ter várias "classes":

  • Apenas entrada
  • Somente saída
  • Eu e O.

Eles devem ter uma interface padronizada, por exemplo:

GetKnobLevel()
GetNextKeyboardButton

Ou, outra abordagem é ter algo como

syscall(DRIVERNUMBER, command)

colocando parâmetros/resultados em registros especificados.

É uma abordagem simples e utilizável. Uma variante mais complexa pode ser ter filas circulares entre seu código de comunicação de hardware e seu código de comunicação de software, em vez de registros.

Aqui está o modelo mental que estou usando:

---
Application
---
OS
---
Driver communicators
---
drivers
---
hardware

Há uma interface bem definida e sem variação entre cada camada (continuo imaginando um bolo de camada com cobertura espessa entre camadas ...). O sistema operacional pode não existir para você, é claro.

Se o seu MCU suportar software e hardware interromper como o X86 CPU, você poderá usá -los para isolar os drivers dos comunicadores do motorista.

Esta é uma solução um pouco de "excesso de engenharia", para ser honesto. Mas, em uma situação em que sua complexidade está ficando significativa, é mais fácil ter engenharia apertada do que ter engenharia frouxa.

Se você estiver se comunicando entre camadas, poderá usar uma variável global para cada "canal" de comunicação e acessá -la de maneira disciplinada, usando apenas funções para acessá -la.

Normalmente, você deseja fazer designs em papel em algum nível, algum trabalho exploratório e redesenhar antes de realmente se preparar para codificar seu projeto. Fluxogramas e diagramas de transição de ônibus funcionam bem aqui.

Essa é a abordagem que favoreci nos meus sistemas incorporados funcionam e funciona bem para mim.

Além disso - esse espaço de problema não é bem explorado pelos currículos tradicionais de ciência da computação. É muito menos perdoador do que a Web ou os sistemas operacionais modernos.

Na minha opinião, os aspectos mais importantes do código bem arquiteturado são baixo grau de acoplamento e separação de efeitos colaterais do estado.

Além disso - o código é um ofício. Não pense que você pode fazer a solução perfeita desde o início. Esteja preparado para alterar seu código à medida que aprende coisas novas. De fato - certifique -se de abraçar mude -se como parte do seu trabalho.

Não ser muito glib, mas uma citação de O mítico homem-mês vem à mente: "Planeje jogar um fora; você vai de qualquer maneira".

Cujo corolário é "faça funcionar. Faça certo. Faça -o rápido".

Suponho que isso me faça um defensor de fazer algum design inicial, mas não ficar paralisado por ele. Não precisa ser perfeito da primeira vez. Planeje refatorar. Espero que você tenha escrito coisas de tal maneira que você não está realmente jogando muito código fora, mas reorganizando as coisas de uma maneira mais agradável.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top