Princípios De Desenho

2,682 views

Published on

Anti-padrões e princípios de desenho aplicados a desenvolvimento de software.

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,682
On SlideShare
0
From Embeds
0
Number of Embeds
13
Actions
Shares
0
Downloads
2
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Princípios De Desenho

  1. 1. À Equipe ComunIP<br />Princípios de desenho<br />
  2. 2. O que tem de errado no software?<br />O desenho de muitos softwares começa com uma imagem vívida na cabeça de seus criadores.<br />No início tudo é muito limpo, elegante e convincente.<br />A beleza da simplicidade faz os designers e os desenvolvedores desejarem vê-lo funcionando.<br />Tudo vai bem até a primeira release.<br />Mas então alguma coisa acontece...<br />
  3. 3. O que tem de errado no software?<br />O software começa a apodrecer.<br />No começo nem parece tão ruim.<br />Uma verruga feia aqui, uma gambiarra ali, mas tudo ainda parece bonito.<br />A podridão continua e o software vira uma massa grudenta.<br />Pequenas alterações ficam tão difíceis de fazer que os desenvolvedores começam a clamar por um redesenho.<br />
  4. 4. O que tem de errado no software?<br />Tais redesenhos raramente são bem sucedidos.<br />Apesar dos designers iniciarem com boas intenções, eles percebem que estão tentando atirar num alvo em movimento.<br />O sistema antigo continua a evoluir e mudar, e o novo desenho precisa ser mantido.<br />As verrugas e ulcerações acumulam no novo desenho mesmo antes de sua primeira release.<br />Num belo dia, o novo desenho precisa de um redesenho...<br />
  5. 5. Sintomas de Desenhos Podres:Rigidez<br />Rigidez é a tendência do software ser difícil de ser estendido.<br />Uma mudança causa um efeito cascata de alterações em módulos dependentes.<br />Quando isso acontece, os gerentes passam a ter medo de permitir correções de BUGs não-críticos.<br />Essa relutância vem do fato de que eles não sabem, com nenhuma confiança, quando a correção estará pronta.<br />
  6. 6. Sintomas de Desenhos Podres:Fragilidade<br />Fragilidade é a tendência do software quebrar em muitos lugares sempre que alguma coisa é alterada.<br />Aquilo que quebra geralmente não tem nenhuma relação com a área que foi alterada.<br />Quando isso acontece , os gerentes começam a ter maus presságios.<br />A cada vez que uma alteração é autorizada, o medo de que alguma coisa quebre aparece.<br />
  7. 7. Sintomas de Desenhos Podres:Imobilidade<br />Imobilidade é inabilidade de reusar software de outros projetos ou de partes do mesmo projeto.<br />Freqüentemente um desenvolvedor descobre que ele precisa de um módulo que se parece com um já desenvolvido.<br />Porém, existe uma bagagem de dependências extras. Depois de muito trabalho, descobre-se que o trabalho e o risco de separar as partes desejadas é muito grande.<br />E o código é reescrito no lugar de reusado.<br />
  8. 8. Sintomas de Desenhos Podres:Viscosidade<br />Viscosidade vem em duas formas: no desenho, e no ambiente.<br />No desenho, torna-se evidente quando preservar o desenho se torna mais difícil que fazer um puxadinho (hack).<br />No ambiente, acontece quando as ferramentas estão ineficientes: se o tempo de compilação está muito alto, as mudanças são feitas de forma a evitar recompilações; se o gerenciador de versões está lento, as mudanças são feitas a evitar muitos checkins, etc.<br />
  9. 9. Causas do Apodrecimento:Alterações nos Requisitos<br />Os requisitos mudam de maneira que o desenho inicial não antecipa.<br />Essas alterações precisam eventualmente ser feitas rapidamente, e por quem não conhece bem a filosofia do desenho inicial.<br />Porém, não podemos atacar as mudanças nos requisitos.<br />As pessoas mudam, o mercado muda, e o software, bem,muda ou morre!<br />
  10. 10. Causas do Apodrecimento:Ingerência de Dependências<br />Mesmo com um sistema bem desenhado, se novas mudanças não forem planejadas em relação a dependências, o futuro pode ser um fracasso.<br />O gerenciamento de dependências consiste-se na criação de firewalls: além deles, as dependências não passam.<br />O desenho orientado a objetos está repleto de técnicas para construir tais firewalls, e gerenciar dependências dos módulos.<br />
  11. 11. Alta Coesão e Baixo Acoplamento<br />Coesão é uma medida de quão intra-relacionadas e focadas as várias responsabilidades de um módulo são.<br />Acoplamento é o grau de dependência de cada módulo do programa com outros módulos.<br />Quanto mais o módulo é coeso, menos acoplado ele é.<br />
  12. 12. Alta Coesão e Baixo Acoplamento:Isso funciona?<br />Baixo acoplamento:<br />Os módulos não precisam conhecer muito sobre os outros módulos para interagir<br />Baixo acoplamento permite que futuras modificações sejam mais simples.<br />Alta Coesão:<br />O conteúdo de cada módulo é fortemente inter-relacionado.<br />Alta coesão faz com que um módulo seja mais fácil de entender.<br />
  13. 13. Acoplamento e Complexidade:Potencial de Catástrofe<br />Complexidade<br />Simples<br />Complexo<br />Alto<br />Cabum!<br />Acoplamento<br />Baixo<br />- Charles Perrow, Normal Accidents<br />
  14. 14. Princípios de Projeto Orientado a Objetos:O Princípio Aberto-Fechado (OCP)<br />Um módulo deve ser aberto a extensões mas fechado a modificações<br />Bertrand Meyer, ObjectOriented Software Construction, 1988<br />
  15. 15. class Shape {<br />public:<br />enum ShapeType {circle, square};<br /> virtual ShapeType getType()=0;<br />};<br />class Circle : public Shape {<br />public:<br /> virtual ShapeType getType() { return circle; }<br /> virtual double radius();<br /> Point getCenter();<br />};<br />class Square : public Shape {<br />public:<br /> virtual ShapeType getType() { return square; }<br /> virtual double getSide();<br /> Point getTopLeft();<br />};<br />Um módulo deve ser aberto a extensões mas fechado a modificações<br />(1) Violação do OCP<br />C++<br />Formas geométricas<br />
  16. 16. void DrawSquare(Square *square);<br />void DrawCircle(Circle *circle);<br />void DrawShapes(Shape *shapes[], int n)<br />{<br /> for (int i = 0; i < n; i++) {<br /> switch (shape[i]->getType()) {<br /> case Shape::square:<br />DrawSquare((Square*) shape[i]);<br /> break;<br /> case Shape::circle:<br />DrawCircle((Circle*) shape[i]);<br /> break;<br /> }<br /> }<br />}<br />Um módulo deve ser aberto a extensões mas fechado a modificações<br />(1)Violação do OCP<br />C++<br />A função DrawShapes viola o princípio Aberto-Fechado porque ela necessitará de mudanças sempre que um novo tipo de Shape for declarado. E pior: devido à enumeração, ainda necessitará que todo o código seja recompilado para cada novo tipo de Shape.<br />
  17. 17. class Shape {<br />public:<br /> virtual void draw() const = 0;<br />};<br />class Circle : public Shape {<br />public:<br /> virtual void draw() const;<br />};<br />class Square : public Shape {<br />public:<br /> virtual void draw() const;<br />};<br />void DrawShapes(Shape *shapes[], int n)<br />{<br /> for (int i = 0; i < n; i++)<br />shape[i]->draw();<br />}<br />Um módulo deve ser aberto a extensões mas fechado a modificações<br />(1)Violação do OCP (Uma Solução)<br />C++<br />Formas geométricas: uma solução<br />
  18. 18. struct Modem {<br />enum Type {hayes, courrier, ernie) type;<br />};<br />struct Hayes {<br /> Modem::Type type;<br /> // Hayes related stuff<br />};<br />struct Courrier {<br /> Modem::Type type;<br /> // Courrier related stuff<br />};<br />struct Ernie {<br /> Modem::Type type;<br /> // Ernie related stuff<br />};<br />Um módulo deve ser aberto a extensões mas fechado a modificações<br />(2)Violação do OCP<br />Tipos diferentes de modems: otimizações locais<br />
  19. 19. void LogOn(Modem& m, string& pno,<br /> string& user, string& pw)<br />{<br /> if (m.type == Modem::ernie)<br />DialErnie((Ernie&) m, pno)<br /> else<br />DialHayes((Hayes&) m, pno); // local optimization<br /> // ...you get the idea<br />}<br />Um módulo deve ser aberto a extensões mas fechado a modificações<br />(2)Violação do OCP<br />A função LogOn viola o princípio Aberto-Fechado porque ela necessitará ser alterada sempre que um novo tipo de Modem for adicionado ao código. E pior: o programador usou otimizações locais que escondem a estrutura de seleção de tipos; uma extensão de código desatenta nesse código pode causar sérios danos. Imagine enviar comandos Hayes inadvertidamente para um novo tipo de Modem que não suporta tais comandos!<br />
  20. 20. class Modem {<br />public:<br /> virtual void dial() = 0;<br />};<br />classHayes : public Modem { ... };<br />classCurrier : public Modem { ... };<br />classErnie : public Modem { ... };<br />void LogOn(Modem& m, string& pno,<br /> string& user, string& pw)<br />{<br /> m.dial(pno);<br /> // you got the idea.<br />}<br />Um módulo deve ser aberto a extensões mas fechado a modificações<br />(2)Violação do OCP(Uma solução)<br />Tipos diferentes de modems:Uma solução<br />
  21. 21. template<typenameModemType><br />void LogOn(ModemType& m, string& pno,<br /> string& user, string& pw)<br />{<br /> m.dial(pno);<br /> // you got the idea.<br />}<br />Um módulo deve ser aberto a extensões mas fechado a modificações<br />(2)Violação do OCP (Outra solução)<br />Tipos diferentes de modems:Outra solução<br />Fugindo da Lei de Murphy:<br />“Se alguma coisa tem chance de dar errado, então vai dar errado.”<br />Se você não precisar modificar código que já está funcionando, não existirá chance de quebrá-lo.<br />
  22. 22. Princípios de Projeto Orientado a Objetos:O Princípio da Substituição de Liskov (LSP)<br />Subclasses devem ser substitutas<br />de suas classes base<br />class Base { ... };<br />classDerived : public Base { ... };<br />voidUser(Base &b);<br />intmain() {<br />Derived d;<br />User(d);<br /> ...<br />}<br />Barbara Liskov, Data abstractionandhierarchy, 1987<br />
  23. 23. Subclasses devem ser substitutas de suas classes base<br />Violação do LSP<br />O Dilema do Círculo e da Elipse.<br />Na Geometria, o círculo é uma elipse de focos coincidentes.<br />Minor<br />Axis<br />Major<br />Axis<br />Focus A<br />Focus B<br />
  24. 24. O Dilema do Círculo e da Elipse.<br />Subclasses devem ser substitutas de suas classes base<br />Violação do LSP<br />classEllipse {<br />public:<br /> virtual doublecircumference();<br /> virtual doublearea();<br /> virtual PointgetFocusA();<br /> virtual PointgetFocusB();<br /> virtual doublegetMajorAxis();<br /> virtual doublegetMinorAxis();<br /> virtual voidsetFoci(constPoint &a, constPoint &b);<br /> virtual voidsetMajorAxis(double);<br />private:<br />PointitsFocusA;<br />PointitsFocusB;<br />doubleitsMajorAxis;<br />};<br />classCircle : publicEllipse {<br />public:<br /> virtual voidsetFoci(constPoint &a, constPoint &b);<br /> virtual voidsetMajorAxis(double);<br />};<br />Elipse<br />Circulo<br />
  25. 25. Subclasses devem ser substitutas de suas classes base<br />Violação do LSP<br />O Dilema do Círculo e da Elipse.<br />voidCircle::SetFoci(const Point& a, const Point& b)<br />{<br /> itsFocusA = a;<br /> itsFocusB = a;<br />}<br />void test_EllipseObject(Ellipse& e)<br />{<br /> Point a(-1,0);<br /> Point b(1,0);<br /> e.SetFoci(a,b);<br /> e.SetMajorAxis(3);<br /> assert(e.GetFocusA() == a);<br /> assert(e.GetFocusB() == b); // wtf!<br /> assert(e.GetMajorAxis() == 3);<br />}<br />
  26. 26. Subclasses devem ser substitutas de suas classes base<br />Violação do LSP<br />O Dilema do Círculo e da Elipse.<br />Repercussão da violação! (remendos...)<br />void test_EllipseObject(Ellipse& e)<br />{<br />if (typeid(e) == typeid(Ellipse)) {<br /> Point a(-1,0);<br /> Point b(1,0);<br /> e.SetFoci(a,b);<br /> e.SetMajorAxis(3);<br /> assert(e.GetFocusA() == a);<br /> assert(e.GetFocusB() == b);<br /> assert(e.GetMajorAxis() == 3);<br /> }<br />}<br />Aí voltamos na violação<br />do Aberto-Fechado!!!<br />
  27. 27. O Dilema do Círculo e da Elipse.<br />Como faz?<br />Subclasses devem ser substitutas de suas classes base<br />Violação do LSP<br />Elipse<br />Circulo<br />Elipse<br />Circulo<br />
  28. 28. O Princípio da Inversão de Dependências (DIP)<br />Dependa de Abstrações.<br />Não dependa de Concreções.<br />
  29. 29. Dependa de Abstrações.Não dependa de Concreções.<br />Violação do DIP<br />Main<br />Função 1<br />Função 2<br />Função 3<br />Função 4<br />Função<br />S.O. 1<br />Função<br />S.O. 2<br />Função<br />S.O. 3<br />Função<br />S.O. 4<br />A estrutura acima é particularmente fraca (castelo de cartas):<br />Os módulos de mais alto nível tratam as políticas de mais alto nível da aplicação.<br />Por que então o módulos de mais alto nível dependem diretamente dos módulos de implementação?<br />
  30. 30. Dependa de Abstrações.Não dependa de Concreções.<br />Violação do DIP(Uma solução)<br />Interface de alto nível<br />Interface abstrata 1<br />Interface abstrata 2<br />Interface abstrata 3<br />Implementação detalhada 1<br />Implementação detalhada 2<br />Implementação detalhada 3<br />Numa boa estrutura orientada a objetos, a maioria das implementações dependem de abstrações.<br />Assim os módulos que contêm implementações específicas não são mais dependentes entre si, e sim dependentes de abstrações (derivam de interfaces).<br />Por isso diz-se que a dependência desses tipos foi invertida.<br />
  31. 31. O Princípio da Segregação de Interfaces (ISP)<br />Múltiplas interfaces específicas para cada uso são melhores que uma única interface de uso geral.<br />
  32. 32. Múltiplas interfaces específicas para cada uso são melhores que uma única interface de uso geral.<br />Violação do ISP<br />Cliente A<br />Serviço<br />«Métodos do Cliente A»<br />+ ...<br />«Métodos do Cliente B»<br />+ ...<br />«Métodos do Cliente C»<br />+ ...<br />Cliente B<br />Cliente C<br />Note que sempre que uma alteração for feita nos métodos que o Cliente A usa, o Cliente B e o Cliente C podem ser afetados.<br />E mais, pode ser necessário recompilar e mesmo reescrever partes do código para tudo continuar funcionando.<br />
  33. 33. Múltiplas interfaces específicas para cada uso são melhores que uma única interface de uso geral.<br />Violação do ISP(Uma solução)<br />Cliente A<br />interface A<br />Serviço<br />«Métodos do Cliente A»<br />+ ...<br />«Métodos do Cliente B»<br />+ ...<br />«Métodos do Cliente C»<br />+ ...<br />Cliente B<br />interface B<br />Cliente C<br />interface C<br />Neste caso, se a interface A for alterada, o Cliente B e o Cliente C não serão afetados.<br />No VXCOM, isso significa que somente o módulo que implementa o Serviço e o que implementa o Cliente A precisarão ser recompilados!<br />

×