princípios de desenho

33
À Equipe ComunIP PRINCÍPIOS DE DESENHO

Upload: g-b-versiani

Post on 22-Jan-2018

2.764 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Princípios De Desenho

À Equipe ComunIP

PRINCÍPIOS DE DESENHO

Page 2: Princípios De Desenho

O que tem de errado no software?

O desenho de muitos softwares começa com uma imagem vívida na cabeça de seus criadores.

No início tudo é muito limpo, elegante e convincente.

A beleza da simplicidade faz os designers e os desenvolvedores desejarem vê-lo funcionando.

Tudo vai bem até a primeira release.

Mas então alguma coisa acontece...

Page 3: Princípios De Desenho

O que tem de errado no software?

O software começa a apodrecer.

No começo nem parece tão ruim.

Uma verruga feia aqui, uma gambiarra ali, mas tudo ainda parece bonito.

A podridão continua e o software vira uma massa grudenta.

Pequenas alterações ficam tão difíceis de fazer que os desenvolvedores começam a clamar por um redesenho.

Page 4: Princípios De Desenho

O que tem de errado no software?

Tais redesenhos raramente são bem sucedidos.

Apesar dos designers iniciarem com boas intenções, eles percebem que estão tentando atirar num alvo em movimento.

O sistema antigo continua a evoluir e mudar, e o novo desenho precisa ser mantido.

As verrugas e ulcerações acumulam no novo desenho mesmo antes de sua primeira release.

Num belo dia, o novo desenho precisa de um redesenho...

Page 5: Princípios De Desenho

Sintomas de Desenhos Podres:Rigidez

Rigidez é a tendência do software ser difícil de ser estendido.

Uma mudança causa um efeito cascata de alterações em módulos dependentes.

Quando isso acontece, os gerentes passam a ter medo de permitir correções de BUGs não-críticos.

Essa relutância vem do fato de que eles não sabem, com nenhuma confiança, quando a correção estará pronta.

Page 6: Princípios De Desenho

Sintomas de Desenhos Podres:Fragilidade

Fragilidade é a tendência do software quebrar em muitos lugares sempre que alguma coisa é alterada.

Aquilo que quebra geralmente não tem nenhuma relação com a área que foi alterada.

Quando isso acontece , os gerentes começam a ter maus presságios.

A cada vez que uma alteração é autorizada, o medo de que alguma coisa quebre aparece.

Page 7: Princípios De Desenho

Sintomas de Desenhos Podres:Imobilidade

Imobilidade é inabilidade de reusar software de outros projetos ou de partes do mesmo projeto.

Freqüentemente um desenvolvedor descobre que ele precisa de um módulo que se parece com um já desenvolvido.

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.

E o código é reescrito no lugar de reusado.

Page 8: Princípios De Desenho

Sintomas de Desenhos Podres:Viscosidade

Viscosidade vem em duas formas: no desenho, e no ambiente.

No desenho, torna-se evidente quando preservar o desenho se torna mais difícil que fazer um puxadinho (hack).

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.

Page 9: Princípios De Desenho

Causas do Apodrecimento:Alterações nos Requisitos

Os requisitos mudam de maneira que o desenho inicial não antecipa.

Essas alterações precisam eventualmente ser feitas rapidamente, e por quem não conhece bem a filosofia do desenho inicial.

Porém, não podemos atacar as mudanças nos requisitos.

As pessoas mudam, o mercado muda, e o software, bem,muda ou morre!

Page 10: Princípios De Desenho

Causas do Apodrecimento:Ingerência de Dependências

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.

O gerenciamento de dependências consiste-se na criação de firewalls: além deles, as dependências não passam.

O desenho orientado a objetos está repleto de técnicas para construir tais firewalls, e gerenciar dependências dos módulos.

Page 11: Princípios De Desenho

Alta Coesão e Baixo Acoplamento

Coesão é uma medida de quão intra-relacionadas e focadas as várias responsabilidades de um módulo são.

Acoplamento é o grau de dependência de cada módulo do programa com outros módulos.

Quanto mais o módulo é coeso, menos acoplado ele é.

Page 12: Princípios De Desenho

Alta Coesão e Baixo Acoplamento:Isso funciona?

Baixo acoplamento: Os módulos não precisam

conhecer muito sobre os outros módulos para interagir

Baixo acoplamento permite que futuras modificações sejam mais simples.

Alta Coesão: O conteúdo de cada módulo

é fortemente inter-relacionado.

Alta coesão faz com que um módulo seja mais fácil de entender.

Page 13: Princípios De Desenho

Acoplamento e Complexidade:Potencial de Catástrofe

Simples ComplexoB

aixo

Alt

o

Aco

pla

me

ntoComplexidade

- Charles Perrow, Normal Accidents

Page 14: Princípios De Desenho

Princípios de Projeto Orientado a Objetos:O Princípio Aberto-Fechado (OCP)

Um módulo deve ser aberto a extensões mas fechado a modificações

Bertrand Meyer, Object Oriented Software Construction, 1988

Page 15: Princípios De Desenho

class Shape {

public:

enum ShapeType {circle, square};

virtual ShapeType getType()=0;

};

class Circle : public Shape {

public:

virtual ShapeType getType() { return circle; }

virtual double radius();

Point getCenter();

};

class Square : public Shape {

public:

virtual ShapeType getType() { return square; }

virtual double getSide();

Point getTopLeft();

};

Um módulo deve ser

aberto a extensões

mas fechado a

modificações

(1) Violação do OCP

C++

Formas geométricas

Page 16: Princípios De Desenho

void DrawSquare(Square *square);

void DrawCircle(Circle *circle);

void DrawShapes(Shape *shapes[], int n)

{

for (int i = 0; i < n; i++) {

switch (shape[i]->getType()) {

case Shape::square:

DrawSquare((Square*) shape[i]);

break;

case Shape::circle:

DrawCircle((Circle*) shape[i]);

break;

}

}

}

Um módulo deve ser

aberto a extensões

mas fechado a

modificações

(1)Violação do OCP

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.

C++

Page 17: Princípios De Desenho

class Shape {

public:

virtual void draw() const = 0;

};

class Circle : public Shape {

public:

virtual void draw() const;

};

class Square : public Shape {

public:

virtual void draw() const;

};

void DrawShapes(Shape *shapes[], int n)

{

for (int i = 0; i < n; i++)

shape[i]->draw();

}

Um módulo deve ser

aberto a extensões

mas fechado a

modificações

(1)Violação do OCP(Uma Solução)

C++

Formas geométricas: uma solução

Page 18: Princípios De Desenho

struct Modem {

enum Type {hayes, courrier, ernie) type;

};

struct Hayes {

Modem::Type type;

// Hayes related stuff

};

struct Courrier {

Modem::Type type;

// Courrier related stuff

};

struct Ernie {

Modem::Type type;

// Ernie related stuff

};

Um módulo deve ser

aberto a extensões

mas fechado a

modificações

(2)Violação do OCP

Tipos diferentes de modems: otimizações locais

Page 19: Princípios De Desenho

void LogOn(Modem& m, string& pno,

string& user, string& pw)

{

if (m.type == Modem::ernie)

DialErnie((Ernie&) m, pno)

else

DialHayes((Hayes&) m, pno); // local optimization

// ...you get the idea

}

Um módulo deve ser

aberto a extensões

mas fechado a

modificações

(2)Violação do OCP

A função LogOn viola o princípio Aberto-Fechado porque ela necessitará ser alterada sempre que um novo tipo de Modemfor 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 Hayesinadvertidamente para um novo tipo de Modem que não suporta tais comandos!

Page 20: Princípios De Desenho

class Modem {

public:

virtual void dial() = 0;

};

class Hayes : public Modem { ... };

class Currier : public Modem { ... };

class Ernie : public Modem { ... };

void LogOn(Modem& m, string& pno,

string& user, string& pw)

{

m.dial(pno);

// you got the idea.

}

Um módulo deve ser

aberto a extensões

mas fechado a

modificações

(2)Violação do OCP(Uma solução)

Tipos diferentes de modems:Uma solução

Page 21: Princípios De Desenho

template<typename ModemType>

void LogOn(ModemType& m, string& pno,

string& user, string& pw)

{

m.dial(pno);

// you got the idea.

}

Um módulo deve ser

aberto a extensões

mas fechado a

modificações

(2)Violação do OCP (Outra solução)

Fugindo da Lei de Murphy:

“Se alguma coisa tem chance de dar errado, então vai dar errado.”

Se você não precisar modificar código que já está funcionando, não existirá chance de quebrá-lo.

Tipos diferentes de modems:Outra solução

Page 22: Princípios De Desenho

Princípios de Projeto Orientado a Objetos:O Princípio da Substituição de Liskov (LSP)

Subclasses devem ser substitutasde suas classes base

Barbara Liskov, Data abstraction and hierarchy, 1987

class Base { ... };

class Derived : public Base { ... };

void User(Base &b);

int main() {

Derived d;

User(d);

...

}

Page 23: Princípios De Desenho

Subclasses devem ser

substitutas de suas

classes base

Violação do LSP

O Dilema do Círculo e da Elipse.

Na Geometria, o círculo é uma elipse de focos

coincidentes.

Focus A Focus B

MajorAxis

MinorAxis

Page 24: Princípios De Desenho

O Dilema do Círculo e da Elipse.

Subclasses devem ser

substitutas de suas

classes base

Violação do LSP

class Ellipse {

public:

virtual double circumference();

virtual double area();

virtual Point getFocusA();

virtual Point getFocusB();

virtual double getMajorAxis();

virtual double getMinorAxis();

virtual void setFoci(const Point &a, const Point &b);

virtual void setMajorAxis(double);

private:

Point itsFocusA;

Point itsFocusB;

double itsMajorAxis;

};

class Circle : public Ellipse {

public:

virtual void setFoci(const Point &a, const Point &b);

virtual void setMajorAxis(double);

};Circulo

Elipse

Page 25: Princípios De Desenho

Subclasses devem ser

substitutas de suas

classes base

Violação do LSP

O Dilema do Círculo e da Elipse.

void Circle::SetFoci(const Point& a, const Point& b)

{

itsFocusA = a;

itsFocusB = a;

}

void test_EllipseObject(Ellipse& e)

{

Point a(-1,0);

Point b(1,0);

e.SetFoci(a,b);

e.SetMajorAxis(3);

assert(e.GetFocusA() == a);

assert(e.GetFocusB() == b); // wtf!

assert(e.GetMajorAxis() == 3);

}

Page 26: Princípios De Desenho

Subclasses devem ser

substitutas de suas

classes base

Violação do LSP

O Dilema do Círculo e da Elipse.

Repercussão da violação! (remendos...)

void test_EllipseObject(Ellipse& e)

{

if (typeid(e) == typeid(Ellipse)) {

Point a(-1,0);

Point b(1,0);

e.SetFoci(a,b);

e.SetMajorAxis(3);

assert(e.GetFocusA() == a);

assert(e.GetFocusB() == b);

assert(e.GetMajorAxis() == 3);

}

}

Aí voltamos na violaçãodo Aberto-Fechado!!!

Page 27: Princípios De Desenho

O Dilema do Círculo e da Elipse.

Como faz?

Subclasses devem ser

substitutas de suas

classes base

Violação do LSP

Circulo

Elipse

CirculoElipse

Page 28: Princípios De Desenho

O Princípio da Inversão de Dependências (DIP)

Dependa de Abstrações.Não dependa de Concreções.

Page 29: Princípios De Desenho

Dependa de

Abstrações.

Não dependa de

Concreções.

Violação do DIP

Função 1

Main

Função 2 Função 3 Função 4

FunçãoS.O. 1

FunçãoS.O. 2

FunçãoS.O. 3

FunçãoS.O. 4

A estrutura acima é particularmente fraca (castelo de cartas):

Os módulos de mais alto nível tratam as políticas de mais alto nível da aplicação.

Por que então o módulos de mais alto nível dependem diretamente dos módulos de implementação?

Page 30: Princípios De Desenho

Dependa de

Abstrações.

Não dependa de

Concreções.

Violação do DIP(Uma solução)

Interface abstrata 1

Interface de alto nível

Interface abstrata 2

Interface abstrata 3

Implementação detalhada 1

Implementação detalhada 2

Implementação detalhada 3

Numa boa estrutura orientada a objetos, a maioria das implementações dependem de abstrações.

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).

Por isso diz-se que a dependência desses tipos foi invertida.

Page 31: Princípios De Desenho

O Princípio da Segregação de Interfaces (ISP)

Múltiplas interfaces específicas para cada uso são melhores que uma única

interface de uso geral.

Page 32: Princípios De Desenho

Múltiplas interfaces

específicas para cada

uso são melhores que

uma única interface

de uso geral.

Violação do ISP

Cliente B

Serviço

Cliente A

Cliente C

«Métodos do Cliente A»+ ...«Métodos do Cliente B»+ ...«Métodos do Cliente C»+ ...

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.

E mais, pode ser necessário recompilar e mesmo reescrever partes do código para tudo continuar funcionando.

Page 33: Princípios De Desenho

Múltiplas interfaces

específicas para cada

uso são melhores que

uma única interface

de uso geral.

Violação do ISP(Uma solução)

Cliente B

Serviço

Cliente A

Cliente C

«Métodos do Cliente A»+ ...«Métodos do Cliente B»+ ...«Métodos do Cliente C»+ ...

interface B

interface A

interface C

Neste caso, se a interface A for alterada, o Cliente B e o Cliente C não serão afetados.

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!