You are on page 1of 22

Programao Modular

Parte VII Princpios de Projeto OO

Prof. Mrcio Barros


marcio.barros@uniriotec.br

Caractersticas de um Projeto Ruim


Rigidez: o projeto tende a ser difcil de alterar. Uma alterao em um mdulo provoca uma cascata de alteraes em mdulos dependentes. Fragilidade: a tendncia de um sistema falhar em diversos lugares quando uma simples alterao realizada. Imobilidade: um projeto imvel quando contm partes que poderiam ser teis em outros sistemas, mas o esforo e o risco de separar estas partes do sistema original muito grande. Complexidade Exagerada: um projeto que contm elementos que no so teis no presente momento. Geralmente, esta complexidade decorrente de antecipao exagerada de mudanas. Viscosidade no software: dificuldade de se fazer a coisa certa quando uma mudana no sistema desejada. Em geral, mais fcil fazer um ajuste de projeto do que a alterao correta. Viscosidade no ambiente: a usabilidade das ferramentas de construo (depurador, compilador, ...) se deteriora ao longo do tempo Repetio Desnecessria: cdigo repetido em decorrncia de copy-andpaste. O cdigo deveria ser isolado e representado em apenas um lugar. Opacidade: tendncia de um mdulo permanecer difcil de entender.

Como estas caractersticas surgem?


Sistemas de software so entrpicos!
Se nenhum esforo adicional for realizado, o projeto de um sistema tende ao caos ao longo do tempo Em geral, quando a primeira verso de um sistema criada, os projetistas conseguem manter um projeto consistente A medida que novos requisitos so adicionados para atender aos clientes, o projeto comea a degradar O re-projeto do sistema uma atividade complexa, pois novos requisitos continuam surgindo a medida que o novo projeto ocorre (shooting at a moving target)

Motivos da Entropia
Qual a substncia mais voltil do mundo ?
Os novos requisitos no so compatveis com o projeto inicial do sistema O projeto deve ser ajustado rapidamente para atender aos requisitos na janela de tempo disponvel Novas dependncias entre mdulos surgem em funo das alteraes rpidas (afetando a estrutura do sistema) Degradando a estrutura, o sistema pode se tornar mais frgil, difcil de alterar e manter

Degradao do Projeto
A busca do clice sagrado
Fomos acostumados a acreditar que somente os maus projetos degradam ao longo do tempo Isto gera uma tendncia de se investir muito tempo nos estgios iniciais de desenvolvimento, buscando o melhor projeto possvel O problema que os melhores projetos tambm possuem entropia, degradando ao longo do tempo

Degradao do Projeto
Possveis solues
Avaliao contnua da estrutura do sistema e re-projeto das partes mais afetadas ao longo do processo Investir continuamente no sentido de manter o projeto simples e flexvel (reduo de dependncias) No deixar o re-projeto para ser feito todo de uma nica vez, pois o sistema continuar a se mover O re-trabalho inevitvel em grande parte dos sistemas, exceto em domnios muito conhecidos. Aceite o re-trabalho! Princpios de projeto ajudam a manter o projeto de um sistema consistente.

Projeto Lgico
Entre o modelo de anlise ...
Classes Atributos Mtodos Cardinalidades Dependncias

Entre a Anlise e o Cdigo ...


O projeto adapta o modelo de anlise, permitindo sua codificao Para cada classe, devemos incluir:
Mtodos para conectar e desconectar os objetos associados Um mtodo para criao de um objeto da classe (construtor) Mtodos para alterar e consultar os atributos visveis externamente Nem todos os atributos precisam ser visveis. Atributos internos no tero mtodos que permitam o acesso do exterior da classe. A alterao de algum atributo pode provocar alguma reao da parte da classe, que pode ser implementada como parte do mtodo de alterao do atributo.

Atributos, Mtodos e Associaes


Para cada atributo devemos definir
Seu tipo (de acordo com a linguagem de programao) e valor inicial Suas regras de negcio, ou seja, as restries a que o atributo est sujeito dentro da aplicao No pode ser vazio Deve estar no intervalo entre os valores x e y Deve conter um dos valores de um atributo de outro objeto

Para cada mtodo devemos definir


Seus parmetros, incluindo seus tipos, e seu tipo de retorno

Para cada associao, devemos investigar


Navegabilidade Cardinalidades Dependncias

Exemplo: Clculo de Taxa de Retorno


Um investidor realiza investimentos em algumas companhias atravs de um nico banco. A situao da carteira de investimentos fornecida pelo banco atravs de um extrato emitido em uma determinada data e enviado para o investidor. O extrato apresenta, para cada investimento, o nome da companhia, o volume financeiro atualmente investido, a lista de aquisies e de retiradas de capital. Cada aquisio indica a data de compra e o valor comprado. Cada retirada indica o valor financeiro retirado e a data do saque. O investidor necessita calcular a taxa de retorno do seu investimento. O clculo deve ser realizado para uma companhia ou para todos os investimentos, segundo a seguinte frmula:

Taxa =

ValorCorrente + Re tiradas

Aquisies

Um Modelo de Anlise ...

Extrato Extrato DataEmissao DataEmissao

CalculaTaxaRetorno CalculaTaxaRetorno CalculaTaxaCompanhia CalculaTaxaCompanhia Aquisies Aquisies Data Data Valor Valor

Companhia Companhia Nome Nome

Investimento Investimento ValorCorrente ValorCorrente

*
CalculaRetiradas CalculaRetiradas CalculaAquisies CalculaAquisies

Retiradas Retiradas Data Data Valor Valor

Um Primeiro Modelo de Projeto ...

Extrato Extrato DataEmissao DataEmissao

CalculaTaxaRetorno CalculaTaxaRetorno CalculaTaxaCompanhia CalculaTaxaCompanhia Aquisies Aquisies Data Data Valor Valor

Companhia Companhia Nome Nome

Investimento Investimento ValorCorrente ValorCorrente

*
CalculaRetiradas CalculaRetiradas CalculaAquisies CalculaAquisies

Retiradas Retiradas Data Data Valor Valor

Cardinalidades
Consideremos uma associao ou agregao entre duas classes, digamos C1 e C2. Esta associao possui uma cardinalidade em cada classe. Cardinalidade 0:1
A classe possui um atributo indicando o objeto que est na outra ponta da associao Se este atributo contiver o valor nulo, no existe nenhum objeto na outra ponta (cardinalidade 0) Se seu valor for diferente de nulo, ele dever ser a referncia do objeto na outra ponto (cardinalidade 1)

Cardinalidade 1:1
Mesma soluo, sem permitir que o atributo receba valor nulo

Cardinalidades
Cardinalidade 0:N
A classe deve conter uma lista que indique os elementos da outra ponta da associao Se a lista estiver vazia, no teremos nenhum objetos na outra ponta (cardinalidade 0) Caso contrrio, a lista contm as diversas referncias dos outros objetos (cardinalidade N)

Cardinalidade 1:N
Mesma soluo, sem permitir que a lista fique vazia

Cardinalidades
Em suma, no projeto no existe diferena significativa entre as cardinalidades 0:1/1:1 ou entre 0:N/1:N Duas solues para cardinalidade 1:N ou 0:N
Classe herda de uma classe que represente uma lista Classe contm uma classe que represente uma lista

Lista Lista

C1 C1

C1 C1

C2 C2

Lista Lista

C2 C2

Cardinalidades no Exemplo
Extrato Extrato DataEmissao DataEmissao Vector Vector

CalculaTaxaRetorno CalculaTaxaRetorno CalculaTaxaCompanhia CalculaTaxaCompanhia Vector Vector Aquisies Aquisies Data Data Valor Valor

Companhia Companhia Nome Nome

Investimento Investimento ValorCorrente ValorCorrente

*
CalculaRetiradas CalculaRetiradas CalculaAquisies CalculaAquisies Vector Vector Retiradas Retiradas Data Data Valor Valor

Aplicao de Herana
Extrato Extrato DataEmissao DataEmissao Vector Vector ListaMovimentos ListaMovimentos

CalculaTaxaRetorno CalculaTaxaRetorno CalculaTaxaCompanhia CalculaTaxaCompanhia

2
CalculaSomatrio CalculaSomatrio

Companhia Companhia Nome Nome

Investimento Investimento ValorCorrente ValorCorrente

*
CalculaRetiradas CalculaRetiradas CalculaAquisies CalculaAquisies

Movimento Movimento Data Data Valor Valor

Aquisies Aquisies

Retiradas Retiradas

Dependncias
Uma relao de dependncia ocorre quando uma classe utiliza os servios de outra classe sem estar associada a ela
Suponha que um mtodo de uma classe C1 receba como parmetro um objeto da classe C2 A classe C1 pode utilizar os mtodos do objeto da classe C2 atravs do parmetro. Dizemos que a classe C1 depende da classe C2, pois, caso C2 no existisse, o mtodo de C1 citado acima tambm no poderia existir Observe que isto no significa que a classe C1 est associada classe C2. O parmetro transitrio, enquanto uma associao pode existir ao longo de toda a vida da classe.

Dependncias
Problemas com dependncias
A existncia de uma dependncia entre duas classes ou pacotes implica em uma amarrao entre estes elementos Uma alterao ou um erro em uma classe poder afetar as classes que so dependentes desta Uma classe no pode ser reutilizada sem que as classes de quem ela depende tambm sejam reutilizadas

Dependncias devem ser minimizadas


Quanto menor o nmero de dependncias ... Mais fcil ser corrigir problemas no sistema Mais fcil ser evoluir o sistema (novos requisitos) Mais reutilizveis sero os componentes do sistema

Dependncias Circulares
Dependncia bidirecional
Ocorrem quando uma classe A depende de uma classe B, que por sua vez depende da classe A A dependncia circular faz com que as duas classes sejam tratadas como uma nica classe Algumas linguagens apresentam dificuldades no tratamento de dependncias circulares Algumas vezes, a dependncia circular pode ser resolvida dividindo-se uma das classes em classes menores

Interfaces

Interface de Entrada

Regras de Negcio

Regras de Negcio

Interface de Sada

Princpios de Projeto OO

Responsabilidade nica Princpio Open-Closed Substituio de Liskov Inverso de dependncias Lei de Demeter

Princpio da Responsabilidade nica


Uma classe deve possuir uma nica razo para sofrer uma alterao
Baseado no princpio da coeso funcional, uma classe deve ter uma nica responsabilidade Se uma classe possuir mais de uma responsabilidade, deve-se considerar sua decomposio em duas ou mais Cada responsabilidade um eixo de mudana e as fontes de mudana devem ser isoladas Podem existir razes para no separar as responsabilidades, como semntica, dependncia do sistema operacional ou de hardware

Adaptado de: Structured Analisys and System Specification, Tom de Marco, Yourdon Press, 1979

Princpio da Responsabilidade nica


Considere um sistema que trate retngulos
Problema: a aplicao de clculo geomtrico, que pode no precisar utilizar grficos, obrigada a utilizar o mdulo GUI em decorrncia do mtodo desenha() (maior consumo de memria e tempo de compilao)

Retngulo Retngulo Aplicao de Clculo Aplicao de Clculo Geomtrico Geomtrico desenha () desenha () calculaArea () calculaArea () Aplicao Grfica Aplicao Grfica

GUI GUI

Princpio da Responsabilidade nica


Considere um sistema que trate retngulos (soluo)
Decomposio da funcionalidade do retngulo

Aplicao de Clculo Aplicao de Clculo Geomtrico Geomtrico

RetnguloGeomtrico RetnguloGeomtrico calculaArea () calculaArea ()

Retngulo Retngulo Aplicao Grfica Aplicao Grfica desenha () desenha ()

GUI GUI

Princpio Open-Closed
Classes devem ser abertas para extenso, mas fechadas para modificao
Quando uma mudana em um sistema resulta em uma cascata de mudanas em mdulos dependentes, o projeto do sistema possui caractersticas indesejveis A cascata de alteraes torna o sistema mais frgil e torna suas partes mais difceis de reutilizar O princpio open-closed sugere que se construam classes que nunca mudem. Quando os requisitos mudarem, o comportamento das classes deve ser estendido pela adio de cdigo novo, no pela alterao do cdigo existente.

Object Oriented Software Construction, Bertrand Meyer, Prentice Hall, 1988

Princpio Open-Closed
Projeto baseado em interfaces
Uma forma de atingir o princpio open-closed consiste em basear as dependncias entre classes em interfaces As interfaces definem o protocolo de mtodos necessrios para a comunicao entre as classes Diferentes classes que atendam a interface podem ser utilizadas, cada qual com comportamento distinto

Princpio Open-Closed
Considere um sistema que desenha figuras geomtricas mantidas em uma lista. Temos funes para desenhar as figuras de acordo com seu tipo.
enum ShapeType {circle, square}; struct Shape { ShapeType itsType; }; struct Circle { ShapeType itsType; double itsRadius; Point itsCenter; }; struct Square { ShapeType itsType; double itsSide; Point itsTopLeft; }; typedef struct Shape *ShapePointer; // Funes Externas void DrawSquare(struct Square*) void DrawCircle(struct Circle*); void DrawAllShapes(ShapePointer list[], int n) { int i; for (i=0; i<n; i++) { struct Shape* s = list[i]; switch (s->itsType) { case square: DrawSquare((struct Square*)s); break; case circle: DrawCircle((struct Circle*)s); break; } } }

Princpio Open-Closed
O projeto anterior no atende ao princpio
Uma nova figura geomtrica no pode ser introduzida sem alteraes no mtodo de desenho Funes para outros requisitos, como redimensionar a figura e indicar sua cor, tambm precisariam do switch

Princpio Open-Closed
class Shape { public: virtual void Draw() = 0; }; class Square : public Shape { public: virtual void Draw (); }; class Circle : public Shape { public: virtual void Draw (); }; void DrawAllShapes (Shape *list[], int n) { int i; for (i = 0; i < n; i++) list[i] -> Draw(); } Classe abstrata (interface)

Implementao do quadrado (subclasse da classe Shape extenso)

Implementao do crculo (subclasse da classe Shape extenso)

Mtodo de desenho independente da figura em particular que ser traada

Princpio Open-Closed
Um projeto no pode ser totalmente fechado
O projeto anterior permite que uma nova figura seja includa, mas e se um novo requisito exigisse uma ordem especfica de desenho das figuras? Como o projeto no pode ser totalmente fechado, este deve ser planejado para fechar os requisitos mais importantes Experincia e conhecimento do domnio da aplicao auxiliam o projetista na identificao destes fechamentos

Princpio Open-Closed
Heursticas
Observe os elementos envolvidos no sistema (figuras) Observe as operaes sobre estes elementos (desenho, ...) Observe hierarquias e organizaes naturais Observe as regies de extenso do projeto Estimule mudanas o mais cedo possvel (prottipos)

Planeje fazer duas vezes ...


Sempre que possvel, construa uma verso mais simples para testar o projeto e a idia antes de construir o sistema propriamente dito

Princpio da Substituio de Liskov


Mtodos que utilizem referncias para superclasses devem ser capazes de utilizar subclasses sem qualquer conhecimento especfico
Em geral, os mtodos que apresentam problemas utilizam typecasting para referenciar subclasses especficas Muitas vezes, o typecasting tambm utilizado para identificar o tipo de subclasse

Barbara Liskov, Data Abstraction and Hierarchies, SIGPLAN Notices, 23/5 (May/1988)

Princpio da Substituio de Liskov


Exemplo: lista de figuras
Caso especial de insero de quadrados Tratamento diferenciado dos outros tipos de figuras

class Shape { public: virtual void Draw() = 0; }; class Square : public Shape { public: virtual void Draw (); }; class Circle : public Shape { public: virtual void Draw (); };

class ShapeList { public: void addShape (Shape *ashape); void removeShape (int index); ... }; void ShapeList::addShape (Shape *ashape); { if (ashape instanceof Square) { // Faa algo especfico para quadrados } // Insero convencional ... }

Princpio da Substituio de Liskov


Exemplo: lista de figuras
Verificao subliminar de tipo das subclasses

class Shape { public: virtual int getType () = 0; virtual void Draw() = 0; }; class Square : public Shape { public: virtual int getType () { return 1 }; virtual void Draw (); }; class Circle : public Shape { public: virtual int getType () { return 2 }; virtual void Draw (); };

class ShapeList { public: void addShape (Shape *ashape); void removeShape (int index); ... }; void ShapeList::addShape (Shape *ashape); { if (ashape -> getType () == 1) { // Faa algo especfico para quadrados } // Insero convencional ... }

Princpio da Substituio de Liskov


Problemas mais sutis relacionados com a substituio de Liskov

Um quadrado um retngulo ?
class Rectangle { private: int height, width; public: void setHeight (int aheight); void setWidth (int awidth); int setHeight (); int setWidth (); virtual void Draw (); }; class Square : public Rectangle { public: virtual void Draw (); };

Em termos de dados, um quadrado pode ser representado como um retngulo, mas o comportamento das duas classes distinto ! Por exemplo, chamar o mtodo herdado setHeight() para o quadrado pode transform-lo em algo que no mais quadrado. Isto pode provocar problemas com testes e assertivas. Revise heranas com esta caracterstica (distines comportamentais).

Princpio da Inverso de Dependncias


Considere o trecho de cdigo abaixo ...
O que h de errado com ele? Quais so as suas limitaes?

class CartaBaralho { public boolean Compara (CartaBaralho c) { ... } }; class Ordenador { public void ordena (CartaBaralho[] cartas) { // Implementao do algoritmo de quicksort ! } };

Princpio da Inverso de Dependncias


O principal problema com o cdigo que o algoritmo de ordenao parece estar limitado a cartas de baralho
Entretanto, o algoritmo de ordenao ser exatamente o mesmo se ordenarmos qualquer outro elemento O algoritmo depende apenas da existncia de uma funo de comparao entre elementos do mesmo tipo Entretanto, se usarmos a estratgia do cdigo anterior, teremos diversas repeties do algoritmo ao longo do projeto

Assim ...
Um bom projeto deveria evitar estas repeties definindo um conceito genrico, que seria especializado pelos objetos de interesse do sistema e utilizado pelo algoritmo Este o princpio da inverso de dependncias !!!

Princpio da Inverso de Dependncias


Princpio da inverso de dependncia
Originalmente, o algoritmo dependia do elemento que est sendo ordenado Com as alteraes realizadas, o algoritmo e o elemento ordenado dependem de um mesmo conceito central

Carta de Baralho Carta de Baralho Compara () Compara ()

Carta de Baralho Carta de Baralho Compara () Compara ()

Ordenvel Ordenvel Compara () Compara ()

Algoritmo de Algoritmo de Ordenao Ordenao

Algoritmo de Algoritmo de Ordenao Ordenao

Princpio da Inverso de Dependncias


Vantagem da inverso de dependncias: reutilizao
O algoritmo de ordenao pode ser utilizado (sem qualquer alterao) com qualquer outro elemento que implemente a interface genrica

Carta de Baralho Carta de Baralho Compara () Compara ()

Arquivo Arquivo Compara () Compara ()

Cliente Cliente Compara () Compara ()

Ordenvel Ordenvel Compara () Compara ()

Algoritmo de Algoritmo de Ordenao Ordenao

Lei de Demeter
A probabilidade de uma classe parar de funcionar depende do nmero de classes com quem ela se comunica
A idia geral que se uma classe depende de diversas outras classes, ela poder parar de funcionar quando qualquer destas classes apresentar um problema Esta uma das razes pela qual devemos reduzir o nmero de classes de quem uma classe depende

A Lei de Demeter determina que um objeto somente deveria chamar mtodos ...
Da prpria classe De objetos armazenados diretamente em seus atributos De objetos recebidos como parmetros De objetos criados dentro de seus mtodos

Lei de Demeter
Finalidade
A Lei de Demeter evita longas cadeias de chamadas entre diferentes classes para cumprir uma nica funo O cumprimento da funo deveria se desencadear pela interao entre os objetos destas classes O preo da aplicao da Lei de Demeter o crescimento no nmero de mtodos oferecidos por cada classe Sua principal vantagem est na reduo da complexidade da realizao de um conjunto de tarefas

Em suma ...
Fale apenas com seus vizinhos

Lei de Demeter
Cdigo que no segue a Lei de Demeter ...
public void ligaTelevisao (Televisao televisao) { televisao.painel.Botoes.botao_principal.liga (); } Televisao Televisao

Painel Painel

Botoes Botoes

Botao_Principal Botao_Principal Liga () Liga ()

Lei de Demeter
Cdigo que segue a Lei de Demeter ...
... televisao.Liga (); ... public Televisao::Liga () { painel.Liga (); } public Painel::Liga () { botoes.botao_principal.liga (); } public Botoes::Liga () { botao_principal.liga (); } public Botao::Liga () { ... // Cdigo que liga o boto } Televisao Televisao Liga () Liga ()

Painel Painel Liga () Liga ()

Botoes Botoes Liga () Liga ()

Botao_Principal Botao_Principal Liga () Liga ()

Lei de Demeter
Sem a Lei de Demeter Televisao Televisao get () get () get () liga () Painel Painel Botoes Botoes Botao_Principal Botao_Principal

Seguindo a Lei de Demeter Televisao Televisao liga () liga () liga () Painel Painel Botoes Botoes Botao_Principal Botao_Principal