FUNDAMENTOS DO
DESENHO ORIENTADO A
OBJETO COM UML
Meilir Page-Jones
Tradução:
Celso Roberto Paschoa
Revisão Técnica:
José Davi Furlan
Consultor em UML e autor da Makron Books
São Paulo • Rio de Janeiro • Ribeirão Preto • Belém • Belo Horizonte • Brasília • Campo Grande
• Cuiabá • Curitiba • Florianópolis • Fortaleza • Goiânia • Manaus • Natal • Porto Alegre •
Recife • Salvador
Brasil • Argentina • Colômbia • Costa Rica • Chile • Espanha • Guatemala • México • Peru
• Porto Rico • Venezuela
Do original: Fundamentals of Object-Oriented Design in UML
Copyright © 2000 by Meilir Page-Jones. Original em inglês publicado pelo
acordo com a editora Addison Wesley Longman, Uma companhia da Person
Education.
Copyright © 2001 MAKRON Books Ltda.
Gerente de Produção
Silas Camargo
Editora Assistente
Eugênia Pessotti
Produtora Editorial
Salete Del Guerra
ISBN: 1243-9
FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
AGRADECIMENTOS
A
gradecimentos
V
VI FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
1. N.T.: Silepse — construção de linguagem em que a concordância das palavras se faz pelo sen-
tido e não segundo as regras de sintaxe.
2. N.T.: Lítotes — construção de linguagem em que a frase afirmativa é feita pela negação do
contrário.
AGRADECIMENTOS VII
NOTA DO AUTOR:
Nenhum metodologista foi prejudicado ao se fazer este livro.
DEDICATÓRIA
A minha família
SUMÁRIO
FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
S umário
Apresentação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XIX
Parte I — Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1. Afinal de Contas, o Que Significa Ser Orientado a Objeto? . . . . . . . 1
1.1 Encapsulamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2 Ocultação de Informações e Implementações . . . . . . . . . . . . . . . . . . . . . . 13
1.3 Retenção de Estado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.4 Identidade de Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.5 Mensagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.5.1 Estruturas de mensagens . . . . . . . . . . . . . . . . . . . . 20
1.5.2 Argumentos de mensagens . . . . . . . . . . . . . . . . . . . 21
1.5.3 Os papéis dos objetos em mensagens . . . . . . . . . . . . . . 23
1.5.4 Tipos de mensagem . . . . . . . . . . . . . . . . . . . . . . . 25
1.6 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.7 Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.8. Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.9 Generalização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.10 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
1.11 Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.12 Respostas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2. Breve História da Orientação a Objeto . . . . . . . . . . . . . . . . . . 58
2.1 De Onde Surgiu a Orientação a Objeto? . . . . . . . . . . . . . . . . . . . . . . . . . . 58
2.1.1 Larry Constantine . . . . . . . . . . . . . . . . . . . . . . . . 59
2.1.2 O.-J. Dahl e K. Nygaard . . . . . . . . . . . . . . . . . . . . 59
2.1.3 Alan Kay, Adele Goldberg e outros . . . . . . . . . . . . . . . 59
2.1.4 Edsger Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.1.5 Barbara Liskov . . . . . . . . . . . . . . . . . . . . . . . . . . 60
IX
X FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
PREFÁCIO
FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
XV
XVI FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Aqui você encontrará tudo aquilo de que se precisa para começar a domi-
nar os fundamentos do desenho orientado a objeto. Não só as técnicas básicas
de desenho e construção com objetos estão explicadas com uma clareza excep-
cional, como também estão ilustradas com um enorme número de exemplos, e
elaboradas com discussões sobre os prós e contras de se dispor de bons siste-
mas orientados a objeto. O resto é com você!
Larry Constantine
Rowley, Massachusetts Co-autor do livro Software for Use:
A Practical Guide to the Models and
Methods of Usage-Centered Design
(Reading, Mass.: Addison-Wesley, 1999)
Página em branco
“Vocês dizem que querem algum tipo de evolução.
Bem, vocês sabem, eu estou fazendo o que posso.”
Charles Darwin, A Origem das Espécies
A lgumas pessoas que leram este livro, quando ele ainda era apenas um
rascunho, levantaram algumas questões que talvez sejam interessantes
também para você. Permita-me analisar algumas delas.
XIX
XX FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Por que este livro não é dedicado ao desenho de janelas, ícones e menus?
Existem duas razões. A primeira é que não acredito que a orientação a objeto
seja útil somente para o desenho de interfaces gráficas do usuário. A segunda
é que existem muitos livros no mercado dedicados unicamente ao tópico do de-
senho orientado a objeto de janelas. Eu queria que esta obra abordasse tópicos
não muito bem cobertos por outros livros orientados a objeto. Entretanto, no
Capítulo 7, forneço um pouco de notação para o desenho de navegação em ja-
nelas.
Você disse um monte de coisas a respeito do que este livro não trata.
Afinal, do que ele trata?
Ele trata das idéias fundamentais, da notação, da terminologia, dos critérios
e dos princípios do desenho de software orientado a objeto. O software orien-
tado a objeto é um software constituído de objetos e das classes para as quais
eles pertencem. Um objeto é uma construção de software na qual operações
(que são semelhantes a funções ou procedimentos) são organizadas em torno
de um conjunto de variáveis (que funcionam como dados). Uma classe imple-
menta um tipo, o qual define o grupo de objetos pertencentes à essa classe.
As frases despretensiosas anteriores retêm algumas implicações sur-
preendentes para os desenhistas e programadores de software, implicações es-
sas que surgem dos conceitos do desenho de herança, polimorfismo e desenho
de segunda ordem. Porém, visto que você fez uma pergunta específica, deixe-
me oferecer a você uma resposta também específica.
A Parte I do livro (Capítulos 1 e 2) fornece uma introdução à orientação
a objeto. O Capítulo 1 resume os conceitos-chave e desmistifica o “polimorfis-
mo”, a “generalidade” e todos os demais jargões da OO. O Capítulo 2 posiciona
a orientação a objeto na estrutura dos primeiros desenvolvimentos ligados a
software. Se você já for familiarizado com a orientação a objeto (talvez por ter
programado em uma linguagem orientada a objeto), então poderá pular a Par-
te I ou apenas passar os olhos rapidamente por ela.
A Parte II (Capítulos 3 ao 7) aborda a Linguagem de Modelagem Unifi-
cada (UML), que de facto tornou-se a notação padrão para retratar o desenho
orientado a objeto. Passando pela mesma, a Parte II também ilustra muitas
das estruturas que você encontrou nos sistemas orientados a objeto. O Capí-
tulo 3 introduz a UML para retratar classes, juntamente com seus atributos
e operações. O Capítulo 4 trata da UML nos casos de associações, objetos agre-
gados e compostos, assim como hierarquias de subclasses e superclasses. O
Capítulo 5 exibe a UML para mensagens (seqüenciais e assíncronas), enquan-
to o Capítulo 6 refere-se à UML nos casos de diagramas de estado. O Capítulo
7 revisa a UML para a arquitetura de sistemas e para as janelas que formam
uma interface humana.
A Parte III (Capítulos 8 a 14) aborda os princípios do desenho orientado
a objeto com alguma profundidade. O Capítulo 8 fixa a cena com as notações
vitais de congeneridade e encapsulamento de nível-2. O Capítulo 9 explora os
vários domínios a partir dos quais “as classes surgem” e descreve diferentes
graus de coesão de classe. Os Capítulos 10 e 11 são os pilares centrais da Par-
te III, aplicando os conceitos de espaço-estado e comportamento para avaliar
XXII FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Meilir Page-Jones
Bellevue, Washington
meilir@waysys.com
Página em branco
“Todas as pessoas têm uma gratidão habitual,
e certo fanatismo, em relação a alguns
objetos que por um longo tempo,
continuaram a satisfazê-las.”
P arte I — Introdução
Parte I — Introdução
2 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Encapsulamento
Ocultação de informações e implementações
Retenção de estado
Identidade de objeto
Mensagens
Classes
Herança
Polimorfismo
Generalização
1 Se você estiver curioso a respeito de como a terminologia orientada a objeto passa de uma
linguagem de programação a outra consulte o Apêndice C deste livro.
3
4 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
MEMORANDO
2 Onde cito código ou pseudocódigo no corpo do texto, utilizo uma fonte similar a esta para
realçá-lo.
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 5
(display) antes que nos deixem aplicá-lo no hardware deles. Eles marcaram a
data para o “demo” de nosso software: na próxima segunda-feira.
Na Versão 1 do software, o hominóide teria simplesmente de navegar em
um caminho linear com curvas, como o mostrado abaixo. Você poderia imagi-
ná-lo como alguns blocos quadrados, dispostos para gerar uma trajetória com
largura igual a um bloco, indo desde um quadrado INÍCIO (I) até um quadra-
do FINAL (F). Cada curva ao longo da trajetória seria em ângulo reto, confor-
me mostrado na Figura 1.1.
Um simples avanço feito pelo hominóide faz com que o mesmo percorra
exatamente um quadrado adiante (no sentido de seu nariz). É importante que
o hominóide passe por todos os quadrados no caminho, desde o quadrado INÍ-
CIO até o FINAL. Chega até a ser mais importante que o hominóide não bata
em qualquer parede, porque então pareceríamos idiotas, e eles não permiti-
riam que instalássemos o software no real hardware do hominóide.
Felizmente, já contamos com duas classes escritas que se encontram ar-
quivadas em nossa biblioteca. Essas classes são a Grade e a própria Hominóide.
Portanto, tudo o que vocês precisam fazer até segunda-feira é escrever o códi-
go orientado a objeto que se utiliza das operações dessas classes.
Se vocês tiverem quaisquer perguntas, podem me contatar no meu chalé
habitual no Julius Marx Country Club. Tenham um excelente fim de semana!
P.S.: Anexei breves especificações — das quais dispomos na biblioteca
para as duas classes (Hominóide e Grade).
6 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Hominóide
Novo: Hominóide
// cria e retorna uma nova instância de Hominóide
virarÀEsquerda
// vira o hominóide no sentido anti-horário em 90o
virarÀDireita
// vira o hominóide no sentido horário em 90o
avançar (semPularQuadrados: NúmeroInteiro, out avançarOK: Booleano)
// move o hominóide ao longo de vários quadrados
// no sentido em que ele está apontando e retorna
// se bem-sucedido
posição: Quadrado
// retorna o atual quadrado em que se encontra o
// hominóide
apontandoParaParede: Booleano
// retorna se o hominóide estiver para bater em uma
// parede do display da grade
//mostra o hominóide como um ícone na tela
Grade
Novo: Grade
// cria e retorna uma nova instância de Grade com
// um padrão ao acaso
iniciar: Quadrado
// retorna o quadrado que é o início pensado para
// o caminho pela grade
finalizar: Quadrado
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 7
Chave
Exemplo Significado
avançar As palavras iniciadas com letra
minúscula denotam objetos,
operações de instância e atributos
de instância.
Hominóide As palavras iniciadas com letra
maiúscula denotam classes,
operações de classe e atributos de
classe.
inserirHominóide
(hom: Hominóide, iniciarQuadrado: Quadrado,
out inserirOK: Booleano)
Denota uma operação que toma um
objeto da classe Hominóide e um
objeto da classe Quadrado, e retorna
com um objeto da classe Booleano
(sendo que o out separa os
argumentos de entrada de dados dos
argumentos de saída de dados)
:= Operador de atribuição
var inserirOK Denota uma variável de
programação inserirOK
8 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
iniciarQuadrado := grade.iniciar;
grade.inserirHominóide (hom1, iniciarQuadrado, out inserirOK);
if not inserirOK
then aborte tudo !;
endif;
grade.exibir;
hom1.exibir;
if hom1.apontandoParaParede
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 9
then hom1.virarÀEsquerda;
if hom1.apontandoParaParede
then hom1.virarÀDireita; hom1.virarÀDireita;
endif;
endif;
endrepeat
// o hominóide está no final — sucesso !
1.1 Encapsulamento
Como exemplo, veja a Figura 1.2, que mostra uma sub-rotina para uma
aplicação de empréstimo.
to, que necessite das informações retidas por um atributo, somente pode aces-
sar essas informações se recorrer a uma das operações do objeto.
Uma vez que apenas as operações do objeto podem ler e atualizar os seus
atributos, essas operações formam um anel protetor em volta do núcleo cen-
tral das variáveis implementadas no interior do objeto. Por exemplo, a opera-
ção posição (a propósito, provavelmente implementada como uma função)
ajusta os objetos situados do lado externo do hominóide com a posição do ho-
minóide (presumivelmente na forma de um par de coordenadas x, y). Nós não
podemos acessar diretamente qualquer variável implementada no interior do
objeto (tal como Pos em x e Pos em y) para obter essas informações de maneira
direta.
Uma estrutura de objeto, portanto, se parece com uma cidade européia
medieval, que tradicionalmente era cercada por uma muralha de proteção.
Portões bem-definidos e protegidos ao redor dessa muralha regulavam o in-
gresso e o egresso na cidade. Na Figura 1.4, mostro uma cidade murada cujos
portões, foram denominados segundo os nomes de operação do hominóide.
Figura 1.4 Cidade murada com portões denominados por operações de objeto.
cebeu à 1 hora da madrugada. Contanto que o objeto exporte para nós, seus
clientes, sua posição de uma forma aceitável, não nos importa como ele se lem-
bra de sua posição.
Assim, o sentido do Hominóide é um exemplo tanto da ocultação de infor-
mações como da ocultação de implementações. Por exemplo, não sabemos se a
informação sobre o sentido é mantida dentro do objeto como um ângulo numé-
rico (com valores de 0 a 359 graus), como caracteres simples (com valores de
N, L, S e O), ou como percentualSentido, que expressa o sentido para o qual o
hominóide está voltado como uma porcentagem de um círculo pleno, de 0 a
99,999.
Em um redesenho futuro, talvez decidiríamos por revelar a informação
sobre o sentido e, assim, proporcionaríamos uma operação para exportar o
atributo do sentido para outros objetos. Mas, mesmo assim, reteríamos a ocul-
tação da implementação porque ainda não necessitaríamos saber se a imple-
mentação dentro do objeto era a mesma que a da informação pública.
Por exemplo, podemos decidir que o objeto deverá manter o sentido inter-
namente na forma de caractere e — após convertê-lo — efetuar sua exporta-
ção publicamente na forma angular. Em outras palavras, a operação que
proporciona o valor desse atributo poderia convertê-lo desde uma representa-
ção interna idiossincrática até um ângulo numérico que a maioria das pessoas
gostaria de ver como o atributo sentido.
A ocultação de informações e implementações é uma técnica poderosa
para lidar com a complexidade existente em software. Isso significa que um
objeto se parece com uma caixa preta para um observador externo. Em outras
palavras, o observador externo tem pleno conhecimento do que o objeto pode
fazer, mas não tem conhecimento de como ele pode fazer isso, ou de como ele
é construído internamente. Eu mostrarei isso esquematicamente na Figura 1.5.
1. Ela localiza decisões de desenho. Decisões privadas de desenho (as que fi-
cam dentro de um objeto) têm pouco ou nenhum impacto sobre o resto do
sistema. Portanto, essas decisões locais podem ser feitas e mudadas com
um mínimo impacto sobre o sistema como um todo. Isso limita o efeito da
“pequena ondulação de mudança”.
2. Ela desacopla o conteúdo de informações da sua forma de representação.
Dessa forma, nenhum usuário de informações, que se encontra externo a
um objeto, pode tornar-se vinculado a qualquer formato particular de in-
formações internas. Isso evita que usuários externos de um objeto (por
exemplo, outros programadores) possam intervir no interior do mesmo.
Isso também evita que programadores velhacos introduzam conexões ins-
táveis em um objeto as quais dependam de artimanhas e acidentes de for-
mato. (Eu sei que você não faria uma coisa dessas, mas talvez você já
tenha encontrado por acaso os libertinos de software, aos quais me refiro.)
6 Se você estiver familiarizado com desenho estruturado, poderá reconhecer esse conceito como
memória de estado, conforme exemplificado por um agrupamento de informações. Veja Page-
Jone, 1998.
16 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
cuto isso posteriormente no Capítulo 10.) Por exemplo, o hominóide retém in-
definidamente o conhecimento de qual é o quadrado no qual ele se encontra e
o sentido para o qual está apontando. Todavia, vimos, nas seções 1.1. e 1.2,
que a forma como o objeto escolhe para reter esse conhecimento corresponde
a uma tarefa interna, exclusiva dele.
O encapsulamento orientado a objeto, a ocultação de informações e imple-
mentações, assim como a retenção de estado, estão no centro da orientação a
objeto. Mas essas não são idéias novas. Professores acadêmicos de informática,
extremamente dedicados e trabalhadores, curvados durante incontáveis anos
em torno de quadros-negros pelo mundo inteiro, estudaram essas idéias sob o
termo abstract data-type — ADT (tipo de dado abstrato)7. Entretanto, a orien-
tação a objeto vai muito além do ADT, como as próximas seis propriedades da
orientação a objeto (nos itens 1.4 a 1.9) revelarão.
O lado direito dessa linha cria um novo objeto (da classe Hominóide), o
qual mostro na Figura 1.6. Observe o identificador do objeto, que para o objeto
da figura é o número 602237. O identificador é uma identidade anexada a um
objeto quando este é criado.
1. O mesmo identificador permanece com o objeto por toda sua vida, inde-
pendentemente do que possa acontecer ao objeto durante esse período.
2. Dois objetos nunca podem ter o mesmo identificador. Sempre que o siste-
ma cria um novo objeto, com o passar do tempo o sistema designa um
identificador, diferente de todos os outros identificadores, — do passado,
do presente e do futuro9. Portanto, você sempre poderá distinguir dois ob-
jetos, mesmo se forem idênticos quanto à estrutura e informações que
eles retenham. Eles terão identificadores diferentes.
O lado esquerdo da linha do código é a notação var hom1: Hominóide. Essa
é uma notação normal de programa que fornece um nome expressivo de pro-
gramação (neste caso, hom1) para, digamos, uma palavra de memória que
pode reter um valor. Aqui, o termo Hominóide é o nome da classe do hom1, um
tópico que discutirei na seção 1.6.
Como você já deve ter adivinhado, a atribuição (:=) (que você poderá ler
como “agora aponta para” ou “agora refere-se a”), faz com que a variável hom1 re-
tenha o identificador do objeto criado no lado direito da assertiva de atribuição10.
Ninguém (programador, usuário ou qualquer outra pessoa) jamais verá
realmente o identificador do novo objeto (602237), a menos que eles se enraí-
zem pela memória com um “depurador”. Em vez disso, o programador acessa-
rá o objeto por meio da variável hom1, que foi nomeada pelo programador. Em
outras palavras, hom1 aponta para o objeto cujo identificador é 602237, con-
forme mostrado na Figura 1.7.
Essa linha cria outro objeto (também da classe Hominóide) com um iden-
tificador de, digamos, 142857, e então armazena esse identificador na variável
hom2. (Veja a Figura 1.8.)
hom2 := hom1
11 Um coletor de lixo nesse contexto é um serviço do ambiente operacional, não aquele grande
e barulhento caminhão malcheiroso que passa roncando em sua rua sem saída todas as ma-
nhãs de sexta-feira. A coleta de lixo automatizada é bem-implementada em Java e Eiffel, mas
não — neste contexto — em meios que utilizam a linguagem C++.
20 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
A idéia de dar a cada objeto sua própria identidade por meio de um iden-
tificador parece inócua ao extremo. Entretanto, surpreendentemente, essa
simples idéia provoca uma profunda mudança na forma como desenhamos e
construímos software orientado a objeto. E essa mudança é tratada na seção
seguinte. Continue sintonizado!
1.5 Mensagens
Um objeto solicita a outro que execute uma atividade via uma mensagem.
Muitas mensagens também transmitem algumas informações de um objeto
para outro. A maioria dos experts incluíram mensagens em suas listas de pro-
priedades vitais relativas à orientação a objeto.
12 O objeto obj1 e o objeto obj2 podem ser o mesmo objeto. Por conseguinte, como discuto no
Capítulo 5, um objeto pode enviar uma mensagem a si próprio.
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 21
13 Outros termos utilizados para remetente e destinatário são, respectivamente, cliente e servi-
dor (ou serviço).
14 O código no exemplo anterior deste capítulo é um código dentro de um objeto remetente que
não especifiquei.
22 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
15 O mesmo argumento pode aparecer em ambas as listas ou somente uma vez, pré-fixado por
inout. Entretanto, esse caso é raro em orientação a objeto pura. (Veja o exercício 3, no final
deste capítulo.)
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 23
Por que esses números estranhos? Pelo fato de que 123432 poderia ser o iden-
tificador do objeto (da classe NúmeroInteiro), o qual normalmente lembraríamos
como o número inteiro 2, e 664730 poderia ser o identificador do objeto (da classe
Booleano), que normalmente lembraríamos como o valor lógico verdadeiro.16
Incidentemente, se você preferir uma notação mais formal para a mensa-
gem anterior, a Fig.1.10b apresenta-a em uma notação de UML, que aborda
em profundidade nos Capítulos 3 a 7.
16 Eu não estou sugerindo que o seu ambiente orientado a objeto utilizaria esses números exa-
tos. Eu os utilizo apenas a título de ilustração.
24 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
17 Veja o exercício 4, no final deste capítulo para mais detalhes sobre a distinção entre objetos
e valores de dados.
26 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
1.6 Classes
Recorde que no software do hominóide criamos um objeto (para representar
um hominóide) ao executar Hominóide.Novo. Hominóide, um exemplo de uma
classe, serviu como modelo a partir do qual criamos objetos hominóides (tal
como aquele com o identificador 602237). Sempre que executarmos a assertiva
Hominóide.Novo, geramos um objeto que é estruturalmente idêntico a qual-
quer outro objeto criado por essa assertiva. Por “estruturalmente idêntico”,
quero dizer que cada objeto hominóide apresenta as mesmas operações e va-
riáveis que os outros — especificamente, as operações e variáveis que o progra-
mador codificou quando ele escreveu a classe Hominóide18. Veja a Figura 1.13.
18 A propósito, durante todo este livro, pretendo que um ele indefinido signifique ele ou ela. Em
outras palavras, como eles dizem na burocracia do British Civil Service (Serviço Civil Britâ-
nico), deve ser entendido no seguinte documento que ele engloba ela.
28 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Existem duas diferenças entre objetos da mesma classe. Cada objeto tem
um identificador diferente e, a determinada hora, cada objeto provavelmente
terá um diferente estado (o que significa “valores” diferentes armazenados em
suas variáveis).
Em primeiro lugar, você deve estar confuso sobre a distinção entre uma
classe e um objeto. O modo mais simples de definir dessa distinção é lembrar
que:
20 A analogia padrão da escola “Os Objetos São Apetitosos” é a de que uma classe é como um
cortador de biscoitos e os objetos são como biscoitos. Presumivelmente, o coletor de lixo é mui-
to mais parecido com um “monstro de biscoitos”.
21 Um método é a implementação de uma operação. Em termos de programação, você pode pen-
sar em um método como o código de um corpo de um procedimento (ou de uma função). De
forma similar, uma variável é a implementação de um atributo, e um identificador é a imple-
mentação de um identificador de objeto.
30 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
gerar objetos que operam muito eficientemente, enquanto outra classe para o
mesmo ADT poderia produzir objetos que absorvem pouca memória.
Eu escrevo muito mais sobre abstract data-types, classes e as diferenças
entre eles na Parte III do livro. Até então, entretanto, tratarei classe e ADT
como sinônimos. Com isso em mente, vamos nos mover para o importante con-
ceito de herança.
1.7 Herança
O que você faria se tivesse criado uma classe C e então, mais tarde, descobris-
se uma classe D, praticamente idêntica à C, salvo por alguns atributos ou ope-
rações extras? Uma solução seria simplesmente duplicar todos os atributos e
operações de C, e colocá-los em D. Mas isso não só representaria um trabalho
extra para você, como a duplicação também transformaria a manutenção em
um grande aborrecimento. Uma solução melhor seria conseguir que a classe
D, de alguma forma, tivesse de “solicitar para utilizar as operações” da classe
C. Essa solução é chamada de herança.
que. A herança não ajuda em nada neste caso, uma vez que Planador
é a única classe que tem soltarCaboDeReboque nela definida, e Plana-
dor é uma subclasse de Aeronave. Como a herança não funciona nes-
sa direção, o sistema parará com um erro de run-time. Isso parece
lógico, uma vez que o objeto ae poderia indicar um grande avião a
jato, para o qual a operação soltarCabodeReboque não teria qualquer
sentido.
Na seção 1.6, vimos a distinção entre classe e objeto. Agora, vemos que
há também uma sutil distinção entre objeto e instância. Embora até o momen-
to tenhamos utilizado objeto e instância praticamente como sinônimos um do
outro, notamos que a herança, em certo sentido, permite que um único objeto
seja simultaneamente uma instância de mais de uma classe.
Isso corresponde muito bem ao mundo real. Se você possuir um planador,
você possui exatamente um objeto com uma identificação (determinador) em
sua cauda. Ainda que este planador seja (obviamente!) um exemplo de plana-
dor e, ao mesmo tempo, um exemplo de aeronave. Por conseguinte, conceitual-
mente, o objeto que representa o que você possui é uma instância de Planador
e uma instância de Aeronave.
De fato, esse exemplo comprova um teste revelador para a utilização efi-
caz da herança: ele é chamado do teste do é. Se você puder dizer: “uma classe
D é uma classe C”, então D, quase certamente, deve ser uma subclasse de C.
Assim, já que podemos dizer “um planador é uma aeronave”, a classe Planador
deverá ser uma subclasse de Aeronave22.
Vamos explorar esse tópico mais um pouco analisando os “bastidores” da
herança. O objeto referido como pl será representado em run-time por um
amálgama de duas partes. Uma parte será constituída pelas operações e pelos
atributos de instância definidos por Planador; a outra será constituída pelas
operações e pelos atributos de instância definidos por Aeronave; conforme mos-
trado na Fig. 1.18.23
Na maioria das linguagens, a subclasse sucessora herda tudo o que a su-
perclasse tem a oferecer. A subclasse não chega a apanhar e escolher o que
ela herda. Entretanto, existem algumas artimanhas com as quais a subclasse
pode suprimir (ou seja, neutralizar) certas operações herdadas; conforme dis-
cuto na seção 1.8.
22 Eu vou explorar com mais detalhes o é como um tópico e os usos corretos da herança nos Ca-
pítulos 10, 11 e 12.
23 Na realidade, uma ferramenta denominada achatador de classe (class-flattener) — construída
em ambientes tais como Eiffel — fornece exatamente essa visão.
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 37
1.8. Polimorfismo
A palavra polimorfismo origina-se de duas palavras gregas que significam,
respectivamente, muitas e forma. Algo que é polimórfico, portanto, apresenta
a propriedade de assumir muitas formas, como no episódio do Red Dwarf (de-
nominado muito apropriadamente Polymorph [Polimorfo]), no qual a tripula-
ção de uma nave espacial era continuamente atacada por um ser
extra-terrestre que conseguia rapidamente mudar de uma forma corporal
para outra.
Os livros didáticos contêm duas definições de polimorfismo, nenhuma de-
las tão dramática como o Red Dwarf. Na coluna de definições a seguir, eu as
marquei como (A) e (B). Ambas as definições são válidas e ambas as proprie-
24 Voltarei à questão de herança múltipla diversas vezes no livro, especialmente nos Capítulos
11, 12 e 13. É provável que você queira consultar Meyer, 1992 para uma discussão da herança
múltipla e sua prima menos útil, a herança repetida (na qual uma classe herda característi-
cas da mesma superclasse mais do que uma vez).
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 39
Suponha que tenhamos uma classe Polígono, que represente o tipo de for-
mato em duas dimensões que a Figura 1.20 ilustra:
Formato2-D.calcularÁrea;
Você talvez esteja pensando em como é estranho que um objeto não possa
saber a exata classe do objeto destinatário para o qual ele está enviando uma
mensagem. Entretanto, essa situação é bastante comum. Por exemplo, na li-
nha final de código a seguir, em compile-time, não podemos dizer para qual
classe de objeto p apontará em run-time. O verdadeiro objeto a ser apontado,
será determinado pela escolha do usuário na última hora (testado pela afir-
mação if).
var p: Polígono;
var t: Triângulo := Triângulo.Novo;
var h: Hexágono := Hexágono.Novo;
...
if usuário diz OK
then p:= t
else p:=h
endif;
...
p.calcularÁrea; // aqui p pode referir-se a um objeto Triângulo ou a
um objeto Hexágono
...
1.9 Generalização
Tudo é muito fácil de ser compreendido — até que você tenha de inserir
outro número inteiro na árvore (digamos 5, conforme mostrado na Figura
1.23). Em conseqüência, a árvore pode perder o balanceamento e pode ser pre-
ciso executar um árduo rearranjo em sua estrutura até que ela recupere o ba-
lanço.
Após muitas horas de planejamento no computador e de depuração (de-
bugging) on-line, a maioria de nós colocou nossos algoritmos para funcionar.
Com sorrisos de auto-satisfação, apresentamos nossos programas, saímos em
férias, e fizemos repetidos esforços noturnos para esquecer tudo sobre árvores
binárias balanceadas e classificadas de números inteiros.
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 45
ficará que, pelo menos, uma das classes usadas dentro de ÁrvoreBalanceada
não necessitaria ser atribuída até o run-time26. Esta presumivelmente é a
classe dos itens a serem armazenados nos “nós”27 do objeto da particular ár-
vore balanceada gerada por nós.
Portanto, poderia escrever a classe ÁrvoreBalanceada como segue:
...
var custoÁrvore: ÁrvoreBalanceada := ÁrvoreBalanceada.Nova
<Cliente>;
var produçãoÁrvore: ÁrvoreBalanceada := ÁrvoreBalanceada.Nova
<Produto>;
...
classe ÁrvoreBalanceadaCliente;
...
var atualNó: Cliente:= Cliente.Novo;
26 Uma classe parametrizada é conhecida em C++ como uma classe modelo (template class).
27 N.T.: Nó significa “parte central”.
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 47
...
atualNó.imprimir;
...
classe ÁrvoreBalanceadaProduto;
...
var atualNó: Produto:= Produto.Novo;
...
atualNó.imprimir;
...
classe ÁrvoreBalanceada;
...
var atualNó: Objeto := Objeto.Novo;
...
atualNó.imprimir;
...
1.10 Resumo
Uma vez que o termo orientação a objeto se ressente a priori da falta de um
significado em nosso idioma, tem havido muito pouco consenso histórico sobre
o conjunto de propriedades que a definam. Eu considero as seguintes proprie-
dades como centrais à orientação a objeto: encapsulamento, ocultação de in-
formações e implementações, retenção de estado, identidade de objeto,
mensagens, classes, herança, polimorfismo e generalização.
O encapsulamento orientado a objeto produz uma estrutura de software
(“objeto”) que compreende um anel de operações protetoras em torno de atri-
butos que representam o estado do objeto. (Em termos de implementação, os
métodos das operações manipulam variáveis que mantêm o estado do objeto.)
Esse encapsulamento assegura que qualquer mudança (ou acesso) a informa-
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 49
1.11 Exercícios
1. (a) Reescreva o algoritmo do problema de navegação do hominóide para
torná-lo mais robusto.
(b) Você consegue verificar algum problema na operação inserirHominóide
(hom: Hominóide, posição: Quadrado, out inserirOK: Booleano), a qual está
definida em Grade?
2. Um objeto conhece o seu próprio identificador? Se a resposta for afirma-
tiva, como um objeto se refere a seu próprio identificador?
3. Por qual motivo seria raro para o mesmo nome de argumento aparecer
tanto como um argumento de entrada de dados como um argumento de
saída de dados na assinatura de uma mensagem? (Suponha que o argu-
mento refira-se a — isto é, detenha o identificador de — um objeto.)
4. Na seção 1.5.3, eu disse que “Na orientação a objeto pura, não há neces-
sidade de dados”. Em outras palavras, tudo é um objeto — um encapsu-
lamento de operações em torno de variáveis, que, em seus próprios
termos, se referem a objetos (por meio das variáveis que os implemen-
tam.) Porém, certamente, deverá haver dados “no fundo disso tudo” ou
então não estaríamos descendo em espiral em uma ladeira sucessiva in-
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 51
finita. Assim, será que realmente todas as coisas podem ser consideradas
um objeto? E o que você me diz dos números inteiros e reais, dos quais
existem milhões de instâncias? Como elas foram criadas?
5. Uma operação de instância pode referir-se a uma variável de classe. En-
tretanto, em um ambiente verdadeiramente orientado a objeto, uma ope-
ração de classe não pode, diretamente, referir-se a uma variável de
instância no interior de um objeto. Por que não?
6. Quando executamos a operação Planador.Novo na seção 1.7, quantos ob-
jetos criamos?
7. Como um programa orientado a objeto é iniciado?
8. O que acontece a todos os seus objetos quando você desliga o computador?
9. O que acontece a todas as suas classes quando você desliga o computador?
10. Você consegue pensar em um único meio de envolver mecanismos robus-
tos de encapsulamento com orientação a objeto em uma linguagem como
a C++?
11. Peter Wegner, em um trabalho de enorme expressão, categorizou os am-
bientes como estruturados em objeto, baseados em objeto, baseados em
classe ou orientados a objeto. A primeira categoria tinha somente encap-
sulamento e retenção de estado; à segunda ele acrescentou a identidade
de objeto; a terceira, acrescentou o conceito da classe; e à última acres-
centou herança e as outras propriedades presentes neste capítulo28. De-
cida qual desses quatro termos é o mais apropriado para a linguagem que
você está utilizando no momento.
12. Eu mencionei neste capítulo que a linguagem Java apóia a herança sim-
ples, mas não a herança múltipla. Isso porque uma classe estende, no má-
ximo, para outra classe. Todavia, uma classe potencialmente implementa
muitas interfaces. Assim, a minha afirmação estava correta? Se você es-
tiver familiarizado com essa linguagem, comente sobre a distinção que
existe em Java entre extensões e implementos sob a forma de mecanismos
de herança.
13. Reescreva o pseudocódigo do hominóide da página 8 na linguagem de pro-
gramação orientada a objeto de sua escolha.
14. Considere uma versão de software comprada por você (ou sua compa-
nhia), que o vendedor alega ser “orientada a objeto”. Que características
do software o vendedor identificou como sendo “orientadas a objeto”?
1.12 Respostas
1. (a) Duas sugestões: suponha que o quadrado INÍCIO esteja completamen-
te cercado por paredes e suponha que alguém esqueceu de marcar o qua-
drado FINAL na grade. Modifique o algoritmo para enfrentar com
sucesso essas duas situações e quaisquer outras anormalidades que você
tenha considerado, tal como o fato de que não há verificação de segurança
para saber se avançarOK é, de fato, correta.
(b) o problema é este: o que a operação inserirHominóide do objeto Grade
faz com a informação posição: Quadrado (que é a posição de início do ob-
jeto Hominóide)? Ela deve usar essa informação para dizer ao objeto Ho-
minóide qual é sua posição inicial, mas Hominóide não tem uma operação
fixarPosição nele definida! Por causa disso, em vez de se ter inserirHomi-
nóide definida em Grade), deveríamos ter uma operação inserirNaGrade
(grade: Grade, posição: Quadrado, out inserirOK: Booleano) definida em Ho-
minóide. Incidentalmente, também precisamos de uma operação éUmaPo-
siçãoDeParede: Booleano definida em Grade. (Crédito extra: Por quê?)
2. Sim. Um objeto tem uma variável (na realidade, uma constante) — que
você não precisa declarar — que retém o seu próprio identificador. A va-
riável é chamada pela palavra-chave auto, este, este ou Atual (em, respec-
tivamente, Smalltalk, C++, Java ou Eiffel).
3. Isso implica que o destinatário da mensagem tenha alterado o identifica-
dor em um dos argumentos, o que representa uma má prática de dese-
nho. O remetente de uma mensagem deve ter o direito de supor que suas
variáveis detenham os mesmos identificadores, depois de ele ter enviado
suas mensagens, como anteriormente. Poucas linguagens especificamen-
te proíbem esse tipo de prática; os manuais delas contêm afirmativas tais
como “Argumentos são referências de objetos que são passadas pelo valor
e que não podem ser mudadas por código na operação-alvo”.
4. Em uma linguagem orientada a objeto resoluta, tal como Smalltalk, todas
as coisas são um objeto; de fato, a linguagem Smalltalk decididamente
adotou a posição de que “não existem dados”. Por exemplo, em Smalltalk,
a seguinte operação de soma
x<-5+7
CAP. 1 AFINAL DE CONTAS, O QUE SIGNIFICA SER ORIENTADO A OBJETO? 53
29 Como seria a vida sem exceções? Em Smalltalk, por exemplo, uma string é um objeto literal
que pode mudar seu valor. Em Java, Date (Data) é uma classe comum, à qual new é aplicado.
54 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
a diferença entre classes, que são formas permanentes, e objetos, que são
unidades mercuriais sempre sofrendo alterações em run-time.
10. Em algumas linguagens, um projetista indescritivelmente maldoso pode
criar certos meios para que pessoas estranhas (outsiders) “pulem sobre os
muros de um objeto” e misturem suas variáveis de forma direta. Uma
possibilidade se dá por meio do mecanismo amigo (friend) da linguagem
C++. Semelhantemente à maioria de outros pecados cometidos por proje-
tistas, este, em particular, é perpetrado em nome da grande entidade de-
nominada eficiência.
11. Agora que você escolheu um termo para sua linguagem em curso, consi-
dere quais das propriedades orientadas a objeto dela (se houver) você con-
sidera a mais valiosa. Se sua linguagem não for plenamente orientada a
objeto, quais das propriedades por mim relacionadas neste capítulo você
mais gostaria de ter presente em sua linguagem? Por quê?
12. A linguagem Java de fato sustenta unicamente a herança simples no sen-
tido da “habilidade em herdar”, que é a forma como descrevo herança nes-
te capítulo. Vamos dizer que uma classe S tenha descrita nela a seguinte
assertiva:
estende C implementa I1, I2
Isso significa que S tem acesso a todas operações de C (métodos de Java).
Em outras palavras, por meio da construção estende, S não só herda a in-
terface de C como também a sua habilidade — o código que faz com que
sua interface trabalhe. Entretanto, S herda responsabilidade, e não habi-
lidade, por meio da construção implementa. Neste exemplo, o desenhis-
ta/programador de S deverá proporcionar métodos de Java operantes
para todas as operações definidas nas interfaces I1 e I2.
13. A seguir é o que você poderia trazer à baila para a reescrita do hominóide
em Java. Eu assumo que o algoritmo está contido na operação navegar,
que será uma operação da classe Grade. (Dessa maneira, qualquer refe-
rência ao objeto grade em si será feita por meio da notação this.) Eu tam-
bém dou como certo que o objeto grade é propriamente inicializado (por
exemplo, que quaisquer hominóides previamente inseridos dentro dele te-
nham sido removidos).
56 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
inicialContagemDeViradas = 1;
while (inicialContagemDeViradas <= 4 && hom.apontandoParaParede)
{hom.virarÀEsquerda( ); inicialContagemDeViradas ++;}
//endwhile
hom.avançar (umQuadrado);
hom.exibir;
}//endwhile
//o hominóide está no final — sucesso!
return true;
}//end navegar
B reve história da orientação
a objeto
2.Breve História da Orientação a Objeto
BREVE HISTÓRIA DA ORIENTAÇÃO A OBJETO
FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
58
CAP. 2 BREVE HISTÓRIA DA ORIENTAÇÃO A OBJETO 59
nológica) estão algumas das pessoas que, suponho, fizeram significativas con-
tribuições, teóricas e práticas, para o “dilúvio” da orientação a objeto.
depois, quando elas passaram por correções e tornaram-se um pouco mais for-
talecidas, foi concedido a elas o direito de mudarem seus nomes sob a tutela
do Programa de Proteção Federal (Federal Protection Program). Hoje, as pri-
mitivas ferramentas CASE são conhecidas como ferramentas de modelagem
automatizadas. A modelagem de ferramentas ajuda-nos no caso das discipli-
nas ligadas à análise de requisitos e ao desenho e construção de software. Elas
também fazem com que o desenvolvimento e a manutenção do software se tor-
nem mais gerenciáveis.
Durante toda a história do software, as pessoas tentaram atingir a reu-
tilização. Infelizmente, a maioria das unidades procedimentais de código não
são suficientemente independentes para serem reutilizadas independente-
mente. Mas, agora, com a orientação a objeto, temos outra chance de “ganhar
o paraíso” da reutilização.
Entretanto, a orientação a objeto não garante milagres. Se as classes de
objeto não forem desenhadas cuidadosamente, por meio de diretrizes iguais às
que exponho no decorrer deste livro, a orientação a objeto será incapaz de pro-
porcionar um software confiável e reutilizável.
Caso essa triste situação não seja superada, tudo o que obteremos para
reutilizar serão as histórias relatando o infortúnio de gerentes que fracassa-
ram ao tentarem se valer dos benefícios da orientação a objeto.
Como mencionei anteriormente, a história da orientação a objeto corre
paralela à história corrente predominante do software. Entretanto, com a
orientação a objeto, a progressão da implementação para a abstração ocorreu
em um ritmo extraordinário. A programação orientada a objeto primeiramen-
te atingiu a popularidade nos anos 80. Essa mesma década presenciou a in-
trodução tanto do desenho orientado a objeto como da análise orientada a
objeto. Os sistemas de gerenciamento de bancos de dados orientados a objeto
(ODBMS) e as ferramentas de modelagem orientadas a objeto começaram a
“engatinhar” por volta de 1990.
A rápida chegada desses campos orientados a objeto levou a uma estra-
nha amnésia. Algumas das pessoas que passaram pela ontogênese orientada
a objeto repentinamente se esqueceram de sua filogênese predominante. O
slogan delas tornou-se: “Tudo o que conhecíamos antes de 1990 não tem qual-
quer valor!” Esses indivíduos eram os revolucionários orientados a objeto im-
petuosos, intocáveis e reacionários. Eles denunciaram o então corrente
“sistema (establishment) de software” como os “tigres de papel” burgueses e
imperialistas de uma dinastia COBOL retrógrada, os quais mereciam nada
mais do que uma passagem de ida a Ekaterinburg.
64 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
4. No Capítulo 15, exploro uma construção de software que é análoga à placa de circuito impres-
so eletrônica. Essa construção é na verdade chamada de componente.
CAP. 2 BREVE HISTÓRIA DA ORIENTAÇÃO A OBJETO 65
Embora essa imagem seja sedutora, não podemos esquecer que a escolha
de circuitos de valor a serem encapsulados em chips depende da habilidade do
engenheiro em identificar abstrações proveitosas. As pessoas correriam apres-
sadamente para comprar ICs com utilização em amplificadores operacionais,
amplificadores de áudio, contadores de tempo, line-drivers e assim por diante.
Porém, ninguém nem mesmo chegaria a dar um passo para comprar um IC
de ultra-grande-escala, que tivesse utilização em transistores, indutores e re-
sistores randômicos. Antes de o primeiro IC útil ser construído, os engenheiros
levaram décadas para descobrir os modelos mais proveitosos, que foram emer-
gindo em sistemas eletrônicos, uns após os outros.
Em software, por analogia, devemos nos certificar de que as classes que
desenvolvemos são baseadas em abstrações fortes, sadias e de fácil manejo.
Classes como Cliente, e a adorável, antiga e felpuda, Pilha, muito provavelmen-
te receberão uma ovação em pé; classes tais como Egabragrettu são muito mais
passíveis de serem jogadas em depósitos de lixo nos arredores da cidade.
O segundo ponto de Skolnik é sobre técnicas. Uma vez que os ICs que não
pudessem ser combinados praticamente seriam inúteis, é auspicioso saber que
os engenheiros eletrônicos têm à disposição deles placas de circuito impresso
(PCBs), que retém os ICs em conjunto.
De maneira similar, no desenvolvimento de software orientado a objeto,
nós também devemos nos encarregar do nível “macro” do projeto. Esse nível
lida com os meios pelos quais as classes (e os objetos gerados pelas classes em
run-time) estão interligados. Claramente, haverá uma forte correlação entre o
que desenhamos em nível de intraclasse e o que desenhamos neste nível mais
alto, de interclasse. Isso é esperado, é certo, uma vez que o layout de uma pla-
ca de circuito impresso depende, até certo ponto, do projeto dos ICs que ficam
nela posicionados5.
Poderão existir bons e ruins desenhos orientados a objeto, tanto no nível
de intraclasse como no nível de interclasse. Assim, bons sistemas orientados
a objeto, similarmente a bons sistemas eletrônicos, dependem não somente
das abstrações de alta qualidade como também das técnicas de alta qualidade
destinadas à construção com essas abstrações. As Partes II e III deste livro
tratarão dessas questões. Mas, primeiro, devemos responder a uma pergunta
básica: Para que serve a orientação a objeto?
5. Se você estudou desenho estruturado padrão (veja Page-Jones, 1988, por exemplo), recordará
um princípio similar: o acoplamento entre um conjunto de módulos depende grandemente da
coesão de cada módulo no conjunto.
66 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Reutilização
A orientação a objeto acentua a reutilização porque ela promove um novo uso
do código em nível de classe, em vez de em nível de sub-rotina individual. Ao
desenvolver e cultivar uma biblioteca de classes para suas aplicações em seu
local de trabalho, você está, na prática, criando uma linguagem nova e de mui-
to alto nível feita sob medida especificamente para atender suas necessidades.
Parece, empiricamente, que a classe de objetos é um organismo suficien-
temente sofisticado capaz de migrar de aplicação à aplicação pela sua compa-
nhia, como uma unidade de software independente. Pelo menos, isso é
verdadeiro se a classe carrega consigo o arcabouço de classes subsidiárias ne-
cessário para a realização do trabalho dela. (Exploro esse tópico no decorrer
das seções 9.1 e 9.2.)
Confiabilidade
Códigos confiáveis trabalham repetida e consistentemente. Seu código atingi-
rá essas qualidades somente se você puder de alguma forma verificar a exati-
dão do mesmo. O código orientado a objeto empresta-se a si mesmo para
verificação por meio do uso de certas asserções denominadas de invariantes de
classe. Uma invariante de classe é uma condição que todo objeto de uma dada
classe deve satisfazer. (Por exemplo, uma invariante da classe Pessoas pode
ser dataDeNascimento<= dataDeHoje.)
CAP. 2 BREVE HISTÓRIA DA ORIENTAÇÃO A OBJETO 69
Robustez
A robustez em software é a habilidade de eles se recuperarem facilmente sem-
pre que ocorrer alguma falha. As falhas típicas são violações na asserção, vio-
lações na memória, erros em dispositivos externos e extravasamentos
aritméticos. O software torna-se robusto quando é capaz de capturar uma fa-
lha inesperada (geralmente chamada de exceção [exception]) e é capaz de exe-
cutar uma rotina (geralmente chamada de manipulador de exceção [exception
handler], ou cláusula de resgate [rescue clause]) para recuperar-se de uma fa-
lha.
Muitas linguagens e ambientes modernos orientados a objeto suportam a
detecção e o manuseio de exceções e, dessa forma, encorajam o desenvolvimen-
to de software robusto. Uma excelente maneira de atingir robustez para o có-
digo orientado a objeto é combinar a idéia de asserções e invariantes com a de
manipuladores de exceção. Em alguns meios orientados a objeto, você conse-
gue monitorar invariantes de classe e outras asserções em run-time, e também
ter o software habilmente recuperado se (Deus me livre!) uma asserção for vio-
lada.
A alternativa para o manuseio desses manipuladores é não termos esse
recurso: nunca detecte exceções e simplesmente deixe que o software pare
quando ocorrer uma exceção. Certamente, isso não é robustez!
Extensibilidade
Uma fácil extensibilidade de software depende tecnicamente do que é chama-
do de “homomorfismo entre o domínio da especificação e o domínio da imple-
mentação”. Ai! Em palavras menos formais, isso significa que você deverá
fazer com que o formato da solução se encaixe no formato do problema. Ao fa-
zer isso, é assegurado que uma pequena mudança de usuário não irá se tornar
7. A razão essencial é que o conceito de correção não é algo absoluto que possa ser determinado
para todos os observadores, mas sim algo relativo ao quadro de referência de um observador
em particular. Em outras palavras, a correção é fundamentalmente subjetiva.
70 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Distributividade
Em 1989, um órgão conhecido como Grupo de Gerenciamento Dirigido a Ob-
jeto (Object Management Group — OMG) empreendeu uma tarefa digna de
Hércules: reunir uma grande quantidade dos principais vendedores de hard-
ware e software para que chegassem a um acordo sobre normas de interope-
rabilidade destinadas à orientação a objeto. Eu fiquei maravilhado por eles
terem se incumbido de um empreendimento desse porte — e fiquei assombra-
do quando obtiveram êxito!
O fruto mais visível desse empreendimento foi a criação da Common Ob-
ject Request Broker Architecture (CORBA), uma arquitetura para software
concordante e padronizada, direcionada a dar suporte a sistemas orientados a
objeto distribuídos por plataformas múltiplas.9 O fato em si é impressionante.
Entretanto, a CORBA também permite que objetos “falem uns com os outros”,
não só por meio de máquinas similares, mas também por meio de máquinas
de diferentes modelos, executando sistemas operacionais diferentes e conecta-
dos por uma variedade de redes distintas.
Sob a tutela da CORBA, os objetos por si próprios podem até mesmo ser
criados a partir de classes escritas em diversas linguagens, e compiladas em
diferentes compiladores. E, acima de tudo, uma implementação de CORBA in-
clui vários serviços padrão (tais como: replicação [replication], representação
[ p roxy], manipulação de relacionamentos [relationship-handling] e mediação
de transações (transaction-mediating), que automatizam as partes enfadonhas
8. Entretanto, esse é um exemplo fácil. Como veremos nos Capítulos 11, 12 e 13, a maioria dos
exemplos na vida real não é tão clara e fácil.
9. Para mais detalhes sobre a CORBA, veja Mowbray e Zahavi, 1995 e Orfali e outros, 1996.
CAP. 2 BREVE HISTÓRIA DA ORIENTAÇÃO A OBJETO 71
de códigos que, de outra forma, você teria de realizar manualmente para con-
figurar seu sistema distribuído.
Em outras palavras, com a arquitetura CORBA a natureza heterogênea
e distribuída das plataformas torna-se transparente a você, ao desenhista e ao
programador da aplicação. Você pode escrever mensagens da mesma forma
que no modo habitual do processador comum, e deixar que os serviços CORBA
lidem com muitos dos detalhes “sujos” da infra-estrutura.
Armazenabilidade
Esta seção não estaria completa sem uma menção dos sistemas de gerencia-
mento de bancos de dados orientados a objeto. Um ODBMS é útil se você es-
tiver construindo qualquer aplicação orientada a objeto, e é especialmente
proveitoso se a sua aplicação manipular sons ou gráficos, nenhum dos quais
é facilmente retido em uma forma tabular relacional padrão.
Um ODBMS manterá objetos de classes arbitrárias (não somente classes
tais como String, Números Reais, Números Inteiros e Data, mas também Cliente,
Aeronave, MapaDaCidade, Videoclipe e assim por diante), e proporcionará o en-
capsulamento orientado a objeto, a herança, o polimorfismo e outras caracte-
rísticas importantes orientadas a objeto. A maioria dos ODBMS vem com uma
linguagem de perguntas (tal como a linguagem de perguntas a objeto, OQL)
que substitui o SQL dos DBMSs relacionais.
Assim, embora não seja muito verdadeiro que a orientação a objeto “per
si” torna os softwares mais utilizáveis, a realidade é que uma boa interface
gráfica do usuário torna o software mais utilizável e que a orientação a objeto
pode ser a melhor abordagem para a construção de bibliotecas de software que
suportem GUIs.
2.5 Resumo
Parte do encanto da orientação a objeto é a analogia entre os componentes de
software orientados a objeto e os circuitos eletrônicos integrados. Finalmente,
em software, temos a oportunidade de construir sistemas de modo similar ao
utilizado pelos engenheiros eletrônicos modernos: somos capazes de conectar
componentes pré-fabricados implementadores de abstrações poderosas. Mas,
para nos beneficiarmos disso, primeiro devemos identificar abstrações bem
fundamentadas de software e dispor de meios para interligá-las construtiva-
mente.
CAP. 2 BREVE HISTÓRIA DA ORIENTAÇÃO A OBJETO 75
2.6 Exercícios
1. A maioria das analogias se quebra em algum lugar. Ao ler os capítulos
deste livro, considere as imperfeições da analogia entre uma classe de ob-
jetos software e um circuito eletrônico integrado.
2. Tendo em mente o que você conhece sobre orientação a objeto, em que
classe você se colocaria: reacionário, evolucionista, revolucionário? Justi-
fique sua posição comparando desenvolvimentos em orientação a objeto
com os presentes na corrente predominante de desenvolvimento de soft-
ware.
3. Em sua opinião, é necessário que um local de trabalho escolha uma lin-
guagem (ou ambiente operacional) que tenha todas as propriedades
orientadas a objeto que descrevi no Capítulo 1? Em outras palavras, você
concorda com alguns revolucionários orientados a objeto que tendem a
zombar de locais de trabalho que optaram por algo que não seja a orien-
tação a objeto em sua forma mais plena?
4. Considere a “lista de celebridades” da orientação a objeto constante na se-
ção 2.1. Quem, na sua opinião, eu deixei de fora? Que contribuição essa
“celebridade” trouxe fizeram para o tema da orientação a objeto?
2.7 Respostas
1. Uma deficiência na analogia origina-se da forma como os ICs são interli-
gados por fios na maioria das linguagens orientadas a objeto atuais. Os
ICs eletrônicos permanecem simetricamente anônimos, uns em relação
aos outros. A “fiação” vai de um “plugue” de IC até um outro “plugue” de
IC e, em tempo algum, um IC “reconhece” os outros ICs com os quais ele
está conectado; ele “reconhece” apenas os seus próprios pinos, não os pi-
nos de outros ICs.
Isso não ocorre com classes e objetos. Classes são conectadas a outras
classes pelo nome explícito. Por exemplo, se a ClasseA é herdeira da Clas-
seB , então a ClasseA conterá uma linha de código tal como herda da
ClasseB. Um objeto envia uma mensagem nomeando um operador de ou-
CAP. 2 BREVE HISTÓRIA DA ORIENTAÇÃO A OBJETO 77
79
Eu não pretendo que este livro seja um manual de referência para cada
símbolo e nuança da UML.5 Portanto, uma vez que aproximadamente 40% da
UML provavelmente expressará cerca de 98% de seus resultados (delivera-
bles) de desenho, concentrei-me na porção da UML que rende o maior número
de desenhos por símbolo. Entretanto, certamente apresento uma quantia su-
ficiente de UML para que você entenda os diagramas na Parte III do livro.
Ao selecionar a porção da UML para apresentar neste livro, omiti os dia-
gramas de caso de uso e atividade. Os diagramas de caso de uso são extrema-
mente valiosos durante a análise de requisitos e são importantes o bastante
para garantir um livro inteiro sobre eles.6 Todavia, não são centrais aos prin-
cípios do desenho orientado a objeto que determinei neste livro. Quanto aos
diagramas de atividade, considero-os excessivamente poluídos graficamente
para exprimirem as idéias de desenho que desejo aqui comunicar.
Ocasionalmente, assinalo uma variação menos importante na UML, que
talvez você prefira quando estiver desenhando uma particular construção de
desenho em um quadro branco. O estilo de notação que você escolherá vai de-
pender de seu gosto e de sua técnica de desenho. Entretanto, já que aposto
que você utilizará uma ferramenta de modelagem automatizada para criar a
maioria de seus resultados formais, você provavelmente terá de trabalhar den-
tro do dialeto notacional dessa ferramenta a fim de obter em papel seus dia-
gramas de desenho. Incidentalmente, do que vi até agora, as ferramentas de
modelagem em UML extraviam-se bem menos do reto e estreito em suas ico-
nografias, em relação ao que as ferramentas para outras notações fazem; por-
tanto, na prática, você não ficará assoberbado com confusas mutações de
notação.
3.1 A Classe
O símbolo de uma classe, no lado esquerdo da Figura 3.1, é central a qualquer
aplicação de UML. O mais alto dos três compartimentos do símbolo mostra o
nome da classe; ele inicia, convencionalmente, com uma letra maiúscula. Este,
em particular, foi imaginativamente denominado de AlgumaClasse. Dentro
desse compartimento, você também pode acrescentar alguma informação adi-
cional sobre a classe, conforme discutido na seção 3.7. A convenção para as fer-
ramentas de modelagem é representar o nome da classe em fonte do tipo
negrito, mas eu nunca consegui escrever em negrito sobre um quadro branco.
87
88 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
1. A principal diferença é que um objeto — uma instância de uma classe — não tem quaisquer
atributos ou operações de classe. Entretanto, as classes típicas têm muito poucos atributos e
operações de classe. A maioria dos atributos de uma classe típica são atributos de instância
(ou seja, atributos de objetos da classe), e a maioria das operações são operações de instância
(ou seja, operações de objetos da classe).
Cap. 3 EXPRESSÃO BÁSICA PARA CLASSES, ATRIBUTOS E OPERAÇÕES 89
Cliente ou AsaEsquerda). Se você não tiver um bom nome para objeto — o que
é bem possível em seu primeiro trabalho de desenho — pode deixá-lo como
anônimo. Nesse caso, uma notação satisfatória seria: NomeDaClasse.
3.2 Atributos
Aqui temos uma rápida revisão dos atributos antes de explorarmos suas re-
presentações na UML. (Para fins de clareza, limitarei minha discussão a atri-
butos de instância publicamente visíveis.)
• Por outro lado, alguns atributos são do tipo apenas para leitura (read-
only), isto é, acessíveis apenas desde o exterior do objeto.2 Em geral
— mas não necessariamente — esses atributos são derivados de ou-
tros; por exemplo, capacidade de um objeto Cubóide ou idade de uma
Pessoa (derivada de dataDeNascimento).
Note que, muito embora capacidade constitua um atributo apenas para lei-
tura, ela não é uma constante; ela pode ser alterada realizando-se certas opera-
ções no objeto; por exemplo, por meio de escala (que amplia ou reduz um cubóide).
Agora vamos ver como a UML representa atributos. A classe Pessoa na
Figura 3.3 tem três atributos listados no seu compartimento médio: nome, da-
taDeNascimento e idade. Cada nome de atributo é seguido por sua respectiva
classe (ou tipo de dado), após um sinal de dois pontos (:). Eu precedo os atri-
butos apenas para leitura com um sinal de corte (slash) inclinado para frente
(/); por exemplo, / idade.3 A classe Cubóide na Figura 3.3 tem quatro atributos:
comprimento, largura, altura e capacidade, o último dos quais não é diretamen-
te determinável (em outras palavras, é apenas para leitura).
3.3 Operações
Nesta seção, analisamos as operações e exploramos suas representações na
UML ao mesmo tempo. Conforme mostrado na Figura 3.4, as operações apa-
recem no compartimento mais baixo com suas assinaturas formais completas.
Cada assinatura formal compreende o nome da operação, juntamente com a
lista dos argumentos de dados de entrada e saída formais da operação4
6. Você deve recordar que, no Capítulo 1, afirmou-se que uma operação é sobreposta se ela for
definida por mais de uma vez na mesma classe.
Cap. 3 EXPRESSÃO BÁSICA PARA CLASSES, ATRIBUTOS E OPERAÇÕES 95
7. Para uma discussão divertida de quão exatamente diversa a definição de “protegida” pode ser,
veja Fowler e Scott, 1997, p. 100-101.
Cap. 3 EXPRESSÃO BÁSICA PARA CLASSES, ATRIBUTOS E OPERAÇÕES 97
Em face disso, uma operação abstrata, e a classe na qual ela está defini-
da, podem parecer inúteis. Assim, por que as temos em uma linguagem orien-
tada a objeto?
A resposta tem relação com a herança. Uma operação abstrata em uma
classe C serve como um modelo, indicando para o leitor que classes abaixo de
C, na hierarquia de herança, proporcionarão uma implementação concreta da
operação (também conhecida como implementação não abstrata ou efetiva).
Em última análise, cada uma das subclasses mais inferiores irão provavel-
mente aplicar seu próprio método para implementar a operação, provavelmen-
te otimizada para atender as necessidades específicas dessa subclasse.
Por exemplo, utilizando a classe Polígono do Capítulo 1, poderíamos
transformar obterÁrea em uma operação abstrata de Polígono, que, em decor-
rência, se tornaria uma classe abstrata. Essa transformação teria duas impli-
cações:
A Figura 3.11 mostra Polígono como exemplo de uma classe abstrata, com
o nome da classe abstrata em itálico, e a propriedade {abstrata} entre chaves
sob o nome. Algumas ferramentas de modelagem abandonam o nome da pro-
priedade e deixam a fonte em itálico realizar todo o trabalho. Isso é suficiente
na grande maioria dos casos, mas acredito que o itálico é um tanto duro para
ser tratado em um quadro branco. A operação obter abstrata, obterÁrea, tam-
8. No Exercício 4 do Capítulo 1, vimos que a geração a partir das classes literais (tal como
NúmerosInteiros.Novo) é ilegal na maioria das linguagens. Quanto a esse caso, a classe
abstrata é similar à classe de literais. Porém, em outras questões, ela possui um conceito
distinto. Uma classe abstrata não possui objetos, mas uma classe literal pode ter muitos
objetos predefinidos.
Cap. 3 EXPRESSÃO BÁSICA PARA CLASSES, ATRIBUTOS E OPERAÇÕES 99
bém aparece em itálico, com a propriedade {abstrata} entre chaves após seu
nome.
3.8 O Utilitário
Utilitário (ou pacote de utilitário) é um grupo de procedimentos e funções en-
capsulados em um única unidade com um conjunto de dados privados. Ele di-
fere da classe pelo fato de que os objetos individuais nunca são gerados a
partir dele; utilitário se assemelha muito mais a um grupo de funções e pro-
cedimentos convencionais (como uma biblioteca dinamicamente concatenada)
ou um pacote de Ada-839.
Utilitário é como uma classe destituída de objetos: suas operações são, na
realidade, operações de classe. Alternativamente, a utilidade poderia ser con-
siderada uma classe com um único objeto predefinido.
A Figura 3.12 mostra a notação da UML para um utilitário. O nome da
classe é precedido pelo estereótipo «utilitário», encerrado no que meus amigos
da editora disseram-me eram guillemets. Repare que nenhum dos nomes de
operação na Figura 3.12 está sublinhado. Muito embora as operações de uma
utilidade sejam, na realidade, operações de classe, por convenção a UML não
sublinha seus nomes.
9. Eu não utilizo o termo pacote aqui, porque a UML utiliza pacote para um conceito diferente
(mas relacionado). Em Ada, você pode criar novos pacotes a partir de um pacote genérico, uti-
lizando-se da palavra-chave novo. Entretanto, isso não é completamente análogo à geração
de objetos. A habitual implementação do COBOL para um utilitário é um subprograma com
múltiplos e discretos pontos de entrada. Um sistema desenhado sobre utilitários, em vez de
classes, é geralmente denominado baseado em objeto (object-based). Em desenho estruturado,
o termo que designa um utilitário é agrupamento de informações (information cluster) ou mó-
dulo baseado em informações (information-strenght module).
100 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
A Figura 3.13 mostra a classe Conjunto, uma típica classe container, que
toma uma classe formalmente denominada T como argumento. Quando uma
classe real, assim como Carro, é fornecida a T (ou, expondo mais formalmente,
“vinculada a T”), cada um dos objetos daquela classe representará um conjunto
de carros. O nome da classe parametrizada, vinculada, é então dado como Con-
junto <Carro>, o qual é mostrado no lado esquerdo da Figura 3.14.
3.10 Resumo
O símbolo de classe é o fundamento da UML. Ele é tipicamente mostrado com
três compartimentos; do topo à base, eles contêm o nome de classe e algumas
informações suplementares, — respectivamente, atributos e operações. Os ob-
jetos aparecem em uma forma similar às classes, exceto pelo fato de que o
nome de objeto, no compartimento superior, é sublinhado e normalmente ex-
presso como obj: Classe.
O atributo representa uma informação sobre um objeto e aparece no com-
partimento médio do símbolo de classe: cada atributo é listado pelo nome, se-
guido pela sua classe (ou tipo de dado [data-type]). Por implicação, quando um
atributo é listado, o compartimento de operação contém uma operação obter
(get) e uma operação especificar (set). Se a operação especificar não for neces-
sária, ou não for permitida — por exemplo, se o valor do atributo é derivado
de um algoritmo —, precedo o nome do atributo com um sinal de corte (slash)
inclinado para a direita (/).
No terceiro compartimento do símbolo de classe, cada operação é listada
com sua assinatura formal plena (seu nome, seguido pelos argumentos for-
mais de entrada e/ou saída de dados que uma mensagem deve fornecer para
invocar a operação). Convencionalmente, as operações obter e especificar são
omitidas por brevidade, a menos que um desenhista necessite enfatizar uma
operação — tal como uma operação obter requerendo um argumento de entrada.
Muitas operações executam atividades mais complexas do que simples-
mente especificar ou obter valores para atributos — atividades essas que pos-
sivelmente exigem comunicação com outros objetos ou dispositivos externos. A
implementação dessas operações recebe o nome de método de operação.
Os nomes dos atributos e das operações públicas, protegidas e privadas
são prefixados, respectivamente, por um sinal de adição (+), um símbolo de
número (#) e um sinal de subtração (−). Entretanto, o sinal de adição é nor-
malmente omitido porque a visibilidade é pública por padrão (default). Aos
atributos e operações de classe que não forem gerados para objetos indivi-
duais, são dados nomes sublinhados. Uma operação é sobreposta se ela apa-
rece diversas vezes em um diagrama de classe, cada hora com uma assinatura
diferente (com um número diferente de argumentos ou com diferentes classes
de argumentos).
O nome de uma classe abstrata aparece em itálico, e é seguido pela pro-
priedade {abstrata}. O nome de uma operação abstrata também aparece em
itálico e tem a propriedade {abstrata}. O nome de uma classe de utilitário é
precedida pelo estereótipo «utilitário». Embora as operações de um utilitário
Cap. 3 EXPRESSÃO BÁSICA PARA CLASSES, ATRIBUTOS E OPERAÇÕES 103
sejam, na realidade, operações de classe, por convenção os seus nomes não são
sublinhados.
Uma classe parametrizada (ou classe genérica, ou padrão) aparece com
uma caixa pontilhada em cima do canto direito superior. A caixa pontilhada
contém a lista de nomes dos argumentos formais, cada um dos quais repre-
senta uma classe específica a ser fornecida (ou “vinculada”) à classe parame-
trizada. Uma classe vinculada, que corresponde a uma classe parametrizada
já vinculada com classes específicas, é denominada de acordo com a seguinte
sintaxe:
3.11 Exercícios
1. Resuma as diferenciações entre classes (tal como abstrata versus concre-
ta), que abordamos neste capítulo e no Capítulo 1.
2. Resuma as características diferenciadoras de operações (tal como instân-
cia versus classe), que abordamos neste capítulo e no Capítulo 1.
3. Resuma os símbolos da UML utilizados para prefixar o nome de uma ca-
racterística (atributo ou operação). Suponha, para este exercício, que os atri-
butos e operações de classe tenham um prefixo com o sinal de cifrão ($).
4. A classe Pessoa tem uma operação altura que, — muito embora seja ape-
nas uma operação obter para seu atributo altura, — tinha de ser exibida
na forma de uma operação explícita porque ela necessitava de um argu-
mento de entrada. (Veja a Figura 3.6.) Por que não “parametrizar” o atri-
buto altura, como altura (data: Data): Comprimento, e dessa forma remover
a necessidade de mostrar suas operações obter e especificar explicitamente?
5. Pesquise a notação da UML para expressar uma classe cujos objetos são
imutáveis (conceito que abordei nos exercícios do Capítulo 1).
104 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
6. Na página 98, dei o nome Frota para uma classe parametrizada vinculada
Conjunto<Carro>. Esse é o melhor nome para essa classe? (Dica: especule
sobre algumas aplicações que utilizariam Conjunto<Carro>, juntamente
com os atuais e futuros campos de aplicabilidade de operações.)
7. Pelo que você viu até o momento, como você caracterizaria a diferença en-
tre uma propriedade e um estereótipo da UML?
8. Mostre, em notação da UML, uma classe de sua preferência (tal como a
classe Hominóide do Capítulo 1). Inclua os atributos e as operações.
3.12 Respostas
1 Aqui constam algumas características diferenciadoras de classes que con-
sideramos neste capítulo e no Capítulo 1. Em cada par, refiro-me primei-
ramente à classe “comum”, o tipo de classe que será encontrado com mais
freqüência:
(i) Não utilitário versus utilitário. A primeira faz surgir objetos distin-
tos. A última não (podendo ser considerada como o único objeto de
sua “classe”).
(ii) Não literal versus literal. A primeira suporta geração de objetos em
run-time. A última tem todos os seus objetos “gerados” implicitamen-
te pelo compilador, e os “identificadores” de objetos são seus valores
literais.
(iii) Mutável versus imutável. Os objetos gerados a partir da primeira po-
dem mudar de estado após a geração (conseqüentemente, essa classe
deveria ser, na realidade, chamada de classe com objetos mutáveis).
A última tem objetos cujos estados não mudam após a geração. A
maioria das classes imutáveis são classes literais, e a grande parte
das classes literais são classes imutáveis.
(iv) Concreta versus abstrata. (Não diferida versus diferida.) A primeira
é uma classe sem operações abstratas. Nenhum objeto pode ser ge-
rado a partir do último tipo de classe, normalmente porque ele tem
uma ou mais operações abstratas. (Entretanto, podem ser gerados
objetos desde uma subclasse concreta de uma classe abstrata.)
(v) Não parametrizada versus parametrizada. (Não genérica versus ge-
nérica; não padrão versus padrão.) A primeira é uma classe simples
que não necessita de quaisquer nomes vinculados à mesma. A últi-
ma é uma classe que vincula nomes de classes como argumentos
quando da geração de objetos.
2. Aqui constam algumas características diferenciadoras de operações que
consideramos neste capítulo e no Capítulo 1:
Cap. 3 EXPRESSÃO BÁSICA PARA CLASSES, ATRIBUTOS E OPERAÇÕES 105
108
Cap. 4 DIAGRAMAS DE CLASSE 109
2. A UML se refere a esse diagrama semelhante a uma árvore denominando esse estilo de alvo
compartilhado.
110 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
fato de que assumo que a fonte propulsora de um veículo deve ser ou externa
ou interna, não havendo qualquer outra opção.
8. Alguns modeladores hábeis e experientes têm um ponto de vista diferente, e modelam asso-
ciações utilizando somente nomes de papel e sem um nome de associação. Infelizmente, em
mãos menos hábeis, essa prática muitas vezes conduz a associações vagas ou ambíguas.
Cap. 4 DIAGRAMAS DE CLASSE 121
9. Lembre-se de que, formalmente falando, a associação tem sido uma classe o tempo todo (cujas
instâncias são vínculos). Assim, como muitas em minha própria carreira, essa promoção é
apenas simbólica.
122 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
1. Você pode apostar que a multiplicidade em todas essas três classes será
0..*. (Do contrário, a associação ternária conseqüentemente poderia ser
geralmente resolvida em duas ou três associações binárias.)
2. Muito provavelmente você necessitará promover a associação para uma
classe exclusiva dela — aqui, é CatálogoParaCompraDeItens —, devido ao
fato de que a associação terá, no mínimo, um atributo ou uma operação.
(Do contrário, a associação ternária conseqüentemente poderia ser resol-
vida de novo em duas ou três associações binárias.)
Associações ternárias proveitosas são muito mais raras de se encontrar
do que as associações binárias. Associações quaternárias (ou de quatro vias)
são ainda mais raras. Se alguma aparecer em seu próximo projeto, você pode-
rá mostrá-la exatamente igual a uma associação ternária (mas com quatro li-
nhas sólidas tocando o diamante).
10. Eu retorno às opções de implementação no final da seção 4.3.1, e discuto-as mais adiante no
exercício 4 do Capítulo 14.
124 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
dono: Pessoa;
11. Você pode encontrar um resumo esclarecedor das várias construções todo/parte em Martin e
Odell, 1995. Eu também menciono algumas delas nos exercícios no final deste capítulo.
Cap. 4 DIAGRAMAS DE CLASSE 125
4.3.1 Composição
Composição é uma estrutura comum em sistemas de software, orientados ou
não a objeto, devido ao fato de que muitos objetos compostos aparecem no
dia-a-dia. Por exemplo, um cachorro é uma composição contendo uma cabeça,
um corpo, um rabo e quatro patas (uma em cada extremo). Outras composi-
ções são mais conceituais ou específicas de software: por exemplo, uma men-
sagem de e-mail é uma composição contendo um cabeçalho e alguns
parágrafos de texto. Por sua vez, o cabeçalho é uma composição do nome do
remetente, do nome do destinatário, do título da mensagem e de algum outro
e-stuff (item eletrônico).
Agora, antes de prosseguirmos, vamos ver um pouco de terminologia.
Essa associação todo/parte é chamada de composição; o “todo” é denominado
[objeto] composto; a “parte” é denominada [objeto] componente.12
As três características mais importantes da composição são os que se-
guem:
1. O objeto composto não existe sem os seus componentes. Por exemplo, re-
mova as cerdas, o cabo e a pecinha de borracha de uma escova de dentes
e você não mais terá uma escova de dentes. Na verdade, a simples remo-
ção das cerdas de uma escova de dentes faz com que ela dificilmente seja
qualificada como tal. Dessa forma, o tempo de vida de um objeto composto
não pode ultrapassar o tempo de vida de seus componentes.
2. A qualquer hora, cada dado objeto componente pode ser parte de somente
um objeto composto.
3. A composição é tipicamente heterômera (cujas partes não são semelhan-
tes). Os componentes provavelmente são de tipos misturados: algumas ro-
das, alguns eixos, alguns pedaços de madeira... e aí está a sua carroça.
12. Aqui, utilizo o termo componente em seu tradicional sentido de “peça”; eu não atribuo ao mes-
mo o significado de “construção de software pré-fabricado”, que é o significado do componente
do Capítulo 15.
126 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
13. Diferentemente dos diagramas de herança de classe que vimos na seção 4.1, os diagramas de
composição normalmente mostram hierarquias no estilo de “alvo separado” ou de “não asse-
melhado à árvore”.
14. As caixinhas cinza, tais como aquelas em torno de asaEsquerda e asaDireita na Figura 4.16,
são apenas para esclarecer a parte gráfica e não apresentam qualquer significado semântico.
Cap. 4 DIAGRAMAS DE CLASSE 127
Uma vez que a composição é uma forma de associação, você pode imple-
mentar navegabilidade da mesma maneira que discutimos na seção 4.2.4. Por
exemplo, a classe Planador poderá ter as seguintes variáveis declaradas dentro
dela:
fuselagem: Fuselagem;
cauda: Cauda;
asaEsquerda: Asa;
asaDireita: Asa;
Quando um objeto — vamos indicá-lo como planador1 — da classe Plana-
dor, é gerado (e inicializado), a variável cauda aponta para um objeto repre-
sentativo da cauda do planador1. De maneira similar, as variáveis fuselagem,
asaEsquerda e asaDireita detêm os identificadores dos outros componentes de
um objeto Planador.
Essa implementação suporta navegabilidade de um objeto composto a
seus objetos componentes. Para mostrar isso na Figura 4.16, você poderá
acrescentar setas de navegabilidade de Planador a, respectivamente, Fusela-
gem, Cauda, Asa e Asa. Você também poderá deixar que os objetos componen-
tes detenham o identificador do objeto composto a fim de implementar a
navegabilidade de um objeto componente ao objeto composto. Sua escolha do
desenho de navegabilidade dependerá dos seguintes fatores:
achar o peso de uma cadeira, você poderia enviar uma mensagem a cada com-
ponente da cadeira, pedindo a eles o peso de cada componente.
Nos casos em que precisar de propagações de mensagens desse tipo, você
geralmente suporta tal situação desenhando a navegabilidade em suas asso-
ciações de composição. No último exemplo, você se certificaria de que, para
qualquer objeto Cadeira, você poderia rapidamente obter o identificador do ob-
jeto AssentoDaCadeira, do objeto EspaldarDaCadeira e de cada um dos objetos Pé-
DaCadeira que a compõem. No Capítulo 5 (no diagrama de interação entre
objetos), mostro a notação da UML para mensagens aos componentes de um
objeto composto.
4.3.2 Agregação
Semelhante à composição, a agregação é uma construção familiar por meio da
qual os sistemas de software representam estruturas da vida real. Por exem-
plo, uma cidade é um agregado de casas, uma floresta é um agregado de ár-
vores, e um rebanho é um agregado de ovelhas.15 Em outras palavras, a
agregação é uma associação grupo/membro.
Novamente, vamos ter um pouco de terminologia. A associação é chama-
da agregação; o “todo” é denominado [objeto] agregado; a parte é denominada
[objeto] constituinte.16 As três características mais importantes da agregação
são as seguintes:
1. O objeto agregado pode potencialmente existir sem os seus objetos cons-
tituintes. Por exemplo, mesmo que demita todos os funcionários de um
departamento, você ainda tem um departamento. Entretanto, essa pro-
priedade nem sempre é útil. Eu consigo imaginar uma cidade destruída
e vazia, mas que tal um rebanho vazio? Indagando aos mestres do Zen,
eu gostaria de saber: vocês conseguem vigiar um rebanho sem ovelhas?17
2. A qualquer hora, cada objeto pode ser um constituinte com mais de um
agregado. Novamente, um agregado do mundo real pode ou não aprovei-
tar-se dessa propriedade. Uma pessoa pode pertencer a mais do que um
15. De fato, o rebanho de ovelhas era o agregado original, conforme revelado pela etimologia da
palavra (grex significa “rebanho”).
16. Embora eu tenha a tendência de utilizar o termo simples membro, em vez do pomposo cons-
tituinte, o qual é o termo padrão da UML, evito utilizar membro aqui porque me coloca em
problemas nos locais de trabalho que utilizam C++, em que ele representa a abreviação da
função membro. Se você quiser ser realmente desafiador, tente agregando. Porém, se você qui-
ser uma vida simples, queira aderir a todo e parte tanto para a composição como para a agre-
gação.
17. Uhmmm! Talvez esse seja o motivo pelo qual os pastores levaram tanto tempo para inventar
o conceito de zero.
Cap. 4 DIAGRAMAS DE CLASSE 129
clube no qual freqüentemente ela faça suas refeições, mas pode uma ove-
lha pertencer a mais de um rebanho? Os mestres do Zen mencionados es-
tão novamente silenciosos sobre esse ponto discutível.
3. A agregação tende a ser homeômera (cujas partes são todas semelhan-
tes). Isso significa que os constituintes de um agregado típico pertencerão
à mesma classe. Por exemplo, os constituintes de um parágrafo são todos
sentenças, enquanto os constituintes de uma floresta são todos árvores.
Agora, vamos examinar a notação da UML para agregação. A Figura 4.17
mostra um relatório de gerência que é composto de parágrafos de texto (por
simplicidade). Pode-se fazer as seguintes observações sobre a Figura 4.17:
parteDoTexto:Conjunto<Parágrafo>;
uma ordem, mas uma ordem pode até nem conter linhas (talvez se estiver sen-
do construída em vários minutos ou horas). Observe que, em ambos os exem-
plos, estamos utilizando a multiplicidade para anular uma habilidade teórica
que o constituinte possui de pertencer simultaneamente a mais do que um
agregado.
4.4 Resumo
A UML representa a herança com uma seta de generalização que se estende
da subclasse derivada à classe da qual ela foi herdada (superclasse). Essa no-
tação trata tanto da herança simples quanto da herança múltipla (na qual
uma classe é derivada de diversas superclasses). Entretanto, você pode optar
por representar a herança simples como uma listagem endentada de classes.
Para grandes hierarquias, a representação textual pode ser mais concisa; no
entanto, será menos graficamente imediata que na UML.
A UML indica propriedades de particionamento de subclasses com um
termo extraído de cada um destes pares: de disjunção/sobreposição e comple-
to/incompleto. O critério para essa divisão pode ser indicado por um discrimi-
nador de restrições.
A UML modela uma associação binária com uma linha desenhada entre
as duas classes associadas. O nome da associação (usualmente um substanti-
vo) aparece sobre a linha. Cada multiplicidade da associação aparece na res-
pectiva extremidade da linha na forma mín..máx, em que mín e máx são
geralmente exibidos por números inteiros, zero ou um, ou por um asterisco
para muitas delas. O papel de cada classe na associação pode também apare-
cer na extremidade da linha de associação, se ela adicionar significado ao mo-
delo. Você, além disso, poderá caracterizar uma associação com os detalhes de
132 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
4.5 Exercícios
1. Os desenhistas da UML poderiam ter unificado os conceitos de divisão
em subclasses, de disjunção/sobreposição e de completo/incompleto, em
um conjunto único de possíveis combinações? No caso de resposta afirma-
tiva, como? (Dica: sob cada uma das quatro combinações entre divisões,
pense sobre o número de subgrupos aos quais o membro de um grupo po-
deria simultaneamente pertencer. As Figuras 4.6 e 4.7 podem lhe ajudar
a visualizar a resposta.)
2. Na Figura 4.10, o discriminador sob o qual dividi VeículoExternamentePro-
pulsionado foi tipoDeVeículo. Entretanto, o discriminador sob o qual dividi
VeículoInternamentePropulsionado foi meioDoVeículo. Essa falta de sime-
tria chega a incomodá-lo?
3. Na Figura 4.10, marquei o particionamento da subclasse VeículoInterna-
mentePropulsionado como de sobreposição. Que implicação isso tem para
o desenho e programação do discriminador meioDoVeículo? Que implica-
ção um particionamento incompleto pode apresentar para o(s) valor(es)
de um discriminador?
Cap. 4 DIAGRAMAS DE CLASSE 133
4.6 Respostas
1. Para postular um conjunto unificado de divisões em subclasses, considere
o seguinte: G como um grupo de coisas e c como uma coisa arbitrária. Su-
ponha que G seja dividido em subgrupos. Portanto:
Um particionamento de completo significa que c deve pertencer, no
mínimo, a um subgrupo de G.
Um particionamento de incompleto significa que c deve pertencer, no
mínimo, a zero subgrupos de G.
134 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
você utilizar composição para retratar atributos, seus diagramas não te-
rão muito valor duradouro, e seus modelos parecerão maquinados.
7. Primeiramente, devemos perguntar: o que significa excluir uma cadeira?
Mesmo se desmontar uma cadeira, você ainda poderá querer reutilizar (e
assim não “excluir”) as pernas, o assento ou o espaldar da mesma, para
utilizá-los em outra cadeira. Em geral, você pode ainda estar interessado
nos objetos componentes de um objeto excluído, talvez para compor algo
a mais a partir deles.
Eu encontrei um problema particularmente expressivo com a exclusão
em cascata quando estava trabalhando em uma aplicação que tinha uma
construção todo/parte muito similar a essa da cadeira. Na verdade, para
este exemplo, fingirei que realmente era uma cadeira. Em determinada
aplicação, uma cadeira foi “excluída”, juntamente com suas partes (a ca-
deira foi vendida). Em outra aplicação, entretanto, uma cadeira foi “ex-
cluída” — mas suas partes foram retidas (ela foi desmontada).
Portanto, se seguirmos o princípio de exclusão em cascata nesta apli-
cação, estaremos nas presas de um dilema: uma cadeira seria modelada
como um objeto agregado ou composto? (Como vimos na seção 4.3.1, seria
um objeto composto.)
Alguns autores de livros de UML afirmam que “Um objeto é um objeto
composto se, e somente se, quando ele for excluído, seus objetos compo-
nentes também forem excluídos. De outra forma, ele é um objeto agrega-
do”. Entretanto, eu não acho esse “princípio” útil — ou até mesmo válido!
8. Inicialmente, a associação parece mais uma composição, devido ao fato de
que cada fatia pertence, no máximo, a um pão de cada vez. Mas espere!
A associação se parece mais com uma agregação, pelo fato de que todas
as fatias são semelhantes e podem ser arbitrariamente removidas do pão.
Arghh! Após pensar um pouco (e depois de comer alguns sanduíches), de-
cidi chamá-la de composição. Minha razão: quando eu como o último pe-
daço, não tenho mais um pão. (A propósito, Jim Odell cunhou um termo
especial para esse fenômeno de pão fatiado: composição de porção-objeto).
9. A Figura 4.21 mostra uma possível estrutura agregada para um capítulo
de livro.
O diagrama de agregação de objetos mostra que um capítulo é um con-
junto ordenado de seções. As estruturas para um parágrafo e uma sentença
são similares à estrutura de um capítulo.
Cap. 4 DIAGRAMAS DE CLASSE 137
138
Cap. 5 DIAGRAMAS DE INTERAÇÃO ENTRE OBJETOS 139
1. Os objetos remetente e destinatário são em geral, mas não necessariamente, objetos diferen-
tes. Igualmente, quase sempre, eles pertencem a classes diferentes.
2. Como você pode recordar do Capítulo 4, um vínculo é uma instância de uma associação. Em
outras palavras, uma associação existe entre classes, enquanto um vínculo existe entre obje-
tos.
3. Um lembrete: os argumentos seguintes à palavra-chave out na lista de argumentos (se hou-
ver) são argumentos de saída (argumentos do destinatário de volta ao remetente). Os outros
são argumentos de entrada (argumentos do remetente ao destinatário).
Cap. 5 DIAGRAMAS DE INTERAÇÃO ENTRE OBJETOS 141
4. A maioria das pessoas suprime os nomes de classe ao lado dos argumentos de mensagens em
um diagrama de interação entre objetos. Em outras palavras, normalmente escrevo (ângulo-
DeAterrissagem, out ânguloDeAterrissagemOK), preferentemente a (ânguloDe Aterrissa-
gem: Ângulo, out ânguloDeAterrissagemOK: Booleano). Entretanto, para fins de clareza,
incluí nomes de classe nos exemplos da primeira parte deste capítulo.
5. Para distinguir entre a sintaxe de qualificação de nome da sintaxe de mensagem, prefiro uti-
lizar a convenção da linguagem C++ dos dois-pontos duplos (::), e portanto: umaAeronave:
Aeronave::aterrissar. Todavia, a UML prefere o ponto (.) e reserva os dois-pontos duplos para
os componentes de pacotes, que discutirei no Capítulo 7.
6. Você poderia argumentar que, em vez de estar em ContaBancária, transferirFundos, deveria
ser uma operação na classe utilizada na Figura 5.3, uma classe mais específica de aplicação,
assim como Transferência. No decorrer deste capítulo, introduzirei Transferência.
142 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
7. Algumas ferramentas utilizam números seqüenciais aninhados hierárquicos para essa finali-
dade. Veja Fowler e Scott, 1997, como exemplo.
8. A propósito, esse não é o único — nem mesmo o melhor — desenho orientado a objeto para
satisfazer as condições da aplicação. Para uma discussão de alternativas, veja o exercício 3
do Capítulo 14.
Cap. 5 DIAGRAMAS DE INTERAÇÃO ENTRE OBJETOS 143
A Figura 5.5 mostra outra mensagem entre o mesmo par de objetos. Esta,
na realidade, corresponde à mensagem anterior de contaDoCliente a cliente
para que se encontre a data e a hora da mais recente retirada de dinheiro do
cliente. O objeto contaDoCliente obtém essa informação invocando a operação
obter, últimaHoraDeRetirada: DataHora.
razão disso, conseqüentemente, que nome deveria ser dado à classe do objeto
destinatário?
A resposta é esta: Faça da classe do objeto destinatário a classe mais bai-
xa na hierarquia de herança, ou seja, uma superclasse de todas as classes às
quais o objeto destinatário pudesse porventura pertencer. Por exemplo, se a
mensagem é ícone.escala e o objeto destinatário (referenciado por ícone) possi-
velmente da classe Triângulo, Retângulo ou Hexágono, em decorrência o nome
da classe no objeto destinatário seria Polígono (assumindo que Polígono seja a
superclasse direta daquelas três classes).
Alguns locais de trabalho dão ênfase ao polimorfismo de um objeto desti-
natário por meio de parênteses, como na Figura 5.6. Quando a determinação
da exata classe do objeto destinatário puder ser feita com toda a certeza na
época do desenho (o equivalente à ligação estática [static binding]), estes lo-
cais de trabalho exibiriam o nome da classe do objeto destinatário sem parên-
teses. Nos casos em que a classe exata é determinada somente em run-time
(ligação dinâmica [dynamic binding]), eles mostrarão o nome da classe do ob-
jeto destinatário dentro de parênteses.
9. Contudo, quando escrevi uma mensagem iterada como essa em um quadro branco, não me
incomodei com a duplicação do símbolo. Sua ferramenta pode ou não insistir em mostrá-la.
146 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
lo, Desenho e assim por diante), cada um deles pode executar a mensagem a
sua própria maneira especial. Graças ao milagre do polimorfismo, o objeto re-
metente continua, de forma ditosa, inconsciente da sua complicação.
10. auto não é um termo oficial da UML; é um termo do Smalltalk. Você talvez prefira o termo
de C++ e Java, this, ou o da linguagem Eiffel, Atual.
11. Entretanto, em um chamado mecanismo de recado (callback), o destinatário realmente preci-
sa do identificador do remetente. Eu abordo esse ponto na seção 5.3.2.
Cap. 5 DIAGRAMAS DE INTERAÇÃO ENTRE OBJETOS 147
12. Conforme você pode ver da Figura 5.9, é aceitável repetir símbolos de objetos em um diagra-
ma de colaboração. Essa repetição pode ajudar se as linhas começarem a se cruzar ou a fazer
curvas de maneira muito fechada (como na Figura 5.10). No restante deste capítulo, a esse
respeito, eu não mais incluirei nomes de classe ao lado de argumentos em mensagens. A maio-
ria das ferramentas também suprimem nomes de classe em argumentos reais, mostrando-os
somente em argumentos formais.
148 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
15. Algumas ferramentas mostram a geração de objetos com a seta de mensagem tocando a pró-
pria caixa de objeto.
150 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
O objeto Transferência precisa saber quem são os titulares das contas por duas
razões: para verificar que as duas contas têm o mesmo titular e para se intei-
rar da reputação do titular (com graduações como boa, má, desprezível, esgo-
tada ou indigna).
A próxima mensagem, retirarFundos (quantia, out retiradaOK), vai para
contaOrigem: ContaBancária, que envia a mensagem titularDaConta.saldoMíni-
moPermitido para certificar-se de que a retirada não reduziria o saldo da conta
a uma quantia abaixo do saldo mínimo que o cliente precisa manter. Conforme
determinado pelo pseudocódigo na caixa de descrição lateral, o argumento de
saída de retirarFundos (quantia, out retiradaOK) será colocado como true se tudo
estiver bem.
Finalmente, se tudo realmente estiver bem, a mensagem depositarFundos
(quantia, out depósitoOK) incrementará o dinheiro mantido em contaDestino, e
a mensagem registrar informará à transferirAçãoX que é inteiramente satisfa-
tório registrar irrevogavelmente todo o conjunto de atualizações. Se algo não
sair a contento, a mensagem desfazerOperações informa à transferirAçãoX que
ela deverá descartar quaisquer alterações ocorridas desde iniciar. (A propósito,
não tenho certeza de como depósitoOK poderia um dia tornar-se falso — talvez
somente quando a quantia for negativa.)
A maior parte do diagrama de seqüência é compreensível intuitivamente,
mas ele realmente apresenta algumas limitações. No diagrama de seqüência
da Figura 5.11, não fica claro que as mensagens desfazerOperações e registrar
sejam mutuamente exclusivas; uma é enviada, mas não ambas.
Dessa forma, para onde as nuanças do algoritmo irão? Formalmente, elas
fazem parte, na condição de pseudocódigo, de um ou mais métodos. Informal-
mente, você pode anexar essas caixas de descrição aos diagramas de seqüência
como se fossem recados adesivos, e algumas ferramentas dispõem de uma fa-
cilidade para isso. Se você está usando um diagrama de seqüência para mo-
delar um caso de uso, você pode colocar a especificação textual do mesmo na
caixa à esquerda. (Veja Rosenberg e Scott, 1999, para maiores detalhes.)
Nesse exemplo de diagrama de seqüência, os nomes junto à parte supe-
rior do diagrama referem-se a objetos. Alternativamente, você pode utilizar
nomes de classes, como mencionei anteriormente, ou você pode colocar tarefas
ou processadores inteiros junto ao topo do diagrama para modelar o compor-
tamento da execução dos artefatos arquiteturais de um sistema.16
16. Neste livro, eu tendo a utilizar a palavra artefato para representar um fragmento de tecno-
logia (tal como uma tarefa de processador ou banco de dados), que é capaz de executar ins-
truções de software ou reter dados.
Cap. 5 DIAGRAMAS DE INTERAÇÃO ENTRE OBJETOS 151
17. Uma vez que esta seção trata da modelagem de concorrência e mensagens assíncronas na
UML, não me estenderei muito nos mecanismos específicos pelos quais a concorrência pode
ser implementada (tal como envios de mensagens não bloqueadas), mas mencionarei aqui al-
guns termos. Se o sistema como um todo apresenta concorrência, mas cada objeto destinatário
manipula somente uma mensagem de cada vez, há uma concorrência em nível de sistema
(system-level concurrency). Se um objeto destinatário pode manipular várias mensagens de
cada vez, há uma concorrência em nível de objeto (object-level concurrency). Se uma única ope-
ração pode manipular várias mensagens de cada vez, há uma concorrência em nível de ope-
ração (operation-level concurrency). (Uma operação poderia processar simultaneamente
diversas mensagens tendo diversos encadeamentos de execução por meio do código reentrante
ou, alternativamente, o sistema poderia simplesmente processar diversas cópias da operação.)
Um processador pode igualmente simular concorrência (denominada pseudoconcorrência) por
meio de, digamos, “repartimento de tempo” (time-slicing). Veja Booch, 1994, ou Atkinson,
1991, por exemplo, para descobrir mais sobre as várias formas de concorrência.
152 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Caso você tenha esquecido de seus dias na Legião Estrangeira, a Figura 5.14
expressa o mecanismo de recado de uma forma mais orientada a objeto. Veja
aqui a descrição detalhada:
19. Observe que, embora listenobj não saiba coisa alguma sobre os objetos signatários (à parte
de seus identificadores), ele deve acreditar que cada um deles tem a operação eventoDoTi-
po1Ocorrido, a qual é requerida para receber e entender a mensagem de recado (callback).
Mesmo assim, esses objetos signatários podem ser de muitas classes diferentes, e podem con-
ter definições e implementações da operação eventoDoTipo1Ocorrido, que são polimorfica-
mente diferentes para cada caso.
20. Eu mostro aqui apenas as mensagens mais importantes para o mecanismo de recado (call-
back), omitindo quaisquer mensagens de geração e inicialização porventura necessárias.
156 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
remota for atingida com êxito.) A seguir, o objeto sessãoDoUsuário pode acessar
a máquina remota, talvez obter autorização remota para banco de dados e as-
sim por diante.
Figura 5.17 Três filas paralelas, cada uma com sua própria prioridade.
portanto elas têm de se dirigir para uma fila de prioridade múltipla. A pro-
priedade {prioridade = 3}, ao lado da seta de mensagem, indica que a mensa-
gem na Figura 5.18 tem uma prioridade igual a 3.21
21. Embora neste caso eu mostre mensagens com prioridades, não discuti exatamente como a im-
plementação das prioridades de mensagens poderia trabalhar. A abordagem mais simples é
ter um objeto processando suas mensagens na ordem descendente de prioridade. Os objetos
remetentes também podem ter níveis de prioridade. Sendo assim, uma regra do tipo “Uma
mensagem não poderá ter uma prioridade maior que a do objeto remetente” fará com que par-
tes de um sistema obtenham uma prioridade de execução garantida. Por exemplo, se um rea-
tor nuclear está prestes a “fundir”, uma boa idéia pode ser assegurar que o software de
resfriamento do mesmo seja executado antes do software do relatório funcional mensal. Já
que em muitos ambientes as prioridades são definidas como propriedades de tarefas, uma
operação pode arrendar suas mensagens a uma de um grupo de tarefas similares, cada uma
delas operando com uma diferente prioridade. Mas essas simples abordagens não lidam com
problemas de prioridade particularmente desagradáveis, tais como fome e inversão de priori-
dades. Veja Sha e outros, 1990, Lampson e Redell, 1980, ou Grehan e outros, 1998, para de-
talhes adicionais.
22. Eu trato dos estados de objeto nos Capítulos 6 e 10.
160 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Para comparação com a Figura 5.20, mostro na Figura 5.21, por meio da
UML, uma mensagem interativa padrão (de não-transmissão), em que o obje-
to : MonitorDoPainelDeInstrumentos detém os identificadores de todos os ins-
trumentos relevantes (em uma Lista ou Conjunto denominado de instrumentos).
Desta vez, naturalmente, não há o estereótipo «broadcast» na mensagem.
5.4 Resumo
Este capítulo considerou os dois tipos de diagrama de interação entre objetos
da UML: o diagrama de colaboração e o diagrama de seqüência. Muito embora
ambos os tipos retratem a interação de mensagens entre objetos durante a
execução do sistema, cada tipo tem suas próprias características especiais.
O diagrama de colaboração é mais flexível no tocante à forma e revela se-
qüências temporais de mensagens só numericamente. Ele representa conjun-
tamente os relacionamentos estáticos de objetos e as mensagens dinâmicas
entre estes. O diagrama de seqüência mostra as seqüências temporais de men-
sagens graficamente, dessa forma útil para clarificar a seqüência de intera-
ções entre objetos se comunicando. Entretanto, o diagrama de seqüência não
representa os relacionamentos estáticos de objetos.
Os objetos são mostrados nos diagramas de colaboração como caixas, com
o nome de cada objeto expresso como nomeDoObjeto: NomeDaClasse, em que
o sublinhado enfatiza a natureza ilustrativa do símbolo. Se um objeto é o filho
único de sua classe, você pode deixá-lo sem nome, sob a forma : NomeDaClas-
162 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
5.5 Exercícios
1. Trace um diagrama de seqüência (ou colaboração) para uma operação de
sua escolha. (No meu caso, escolhi o algoritmo do Capítulo 1 para a na-
vegação do hominóide pela grade, que coloquei em uma operação de uma
nova classe, denominada Navegador.)
2. Para a classe que você escolheu no exercício 1, existe alguma concorrên-
cia possível em nível de objeto? Que operações poderiam executar simul-
taneamente no mesmo objeto? Existem quaisquer pares de operações que
você não desejaria executar simultaneamente no mesmo objeto?
3. Neste capítulo, sutilmente dei a entender que, quando um objeto reme-
tente detém os identificadores de seus objetos destinatários, ele remete
várias mensagens em série (interativamente). Alternativamente, quando
um remetente não detém os identificadores de seus objetos destinatários,
e emite uma mensagem de difusão, ele remete diversas mensagens em
paralelo. Será que essas duas implicações estão totalmente corretas?
5.6 Respostas
1. A Figura 5.22 mostra o diagrama de seqüência para a navegação do ho-
minóide pela grade até o quadrado final. (Estabeleço que esse é um dia-
grama de seqüência para a implementação do método de uma operação
definida em uma classe Navegador.) Exibo parte do algoritmo na caixa à
esquerda; na prática, provavelmente, você exibiria todo o algoritmo se
sua ferramenta suportasse tal capacidade.
23. Apenas como lembrete, o sinal de parênteses indica polimorfismo na UML. Por exemplo, (Ob-
jeto) significa a “classe Objeto ou uma subclasse de Objeto”.
164 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
6.Diagramas de Estado
165
166 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
4. Estilisticamente, a UML padrão prefere utilizar um símbolo de corte inclinado para a direita
(/), em vez de uma linha horizontal, para separar eventos de ações. Portanto, nesta seção, você
talvez queira substituir as palavras “antes do (/)” por “acima da linha” e “após o (/)” por “abai-
xo da linha”.
168 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
[self.statusDeManutenção = Funcionando]
5. Omiti as anotações nas transições apenas para fins de clareza neste exemplo.
Cap. 6 DIAGRAMAS DE ESTADO 171
do. O ponto mais interno significa que, não importando quando um objeto da
classe Máquina entra no estado Funcionando — isto é, sempre que o valor do
statusDeManutenção ficar igual a Funcionando —, o valor do statusDeOperação
deverá ser inicializado para estadoDeEspera.
A Figura 6.5 mostra parte do diagrama de estado para uma máquina de
lavar roupa. Bastante semelhante à máquina de fábrica da Figura 6.4, a má-
quina de lavar roupa completa o ciclo dela somente quando estiver funcionan-
do, o que no caso desta máquina de lavar implica que sua tampa esteja
fechada. Entretanto, há uma diferença crucial entre a máquina de lavar roupa
e a máquina de fábrica típica: se a tampa da lavadora abrir, e em seguida fe-
char, ela não deverá retornar a seu estado inicial — parada — mas sim ao seu
estado um pouco anterior à abertura da tampa. Em outras palavras, a lava-
dora deve ser capaz de restabelecer o subestado prévio de funcionabilidade
quando o estado funcionabilidade for reiniciado.
6. A fim de se concentrar em diagramas de estado, este capítulo não se estende mais sobre as
ações mencionadas nos diagramas.
Cap. 6 DIAGRAMAS DE ESTADO 175
Vimos exemplos dos dois primeiros dos elementos na Figura 6.2. Por
exemplo, a condição de guarda
[self.statusDeManutenção = Funcionando]
9. Eu me baseio no termo fluxo de dados contínuo, utilizado em Ward e Mellor, 198, para criar
o termo atributo continuamente variável. A idéia é que um atributo desse tipo — de qualquer
forma, a princípio — varia continuamente em vez de só variar depois de eventos discretos. A
pressão do ar em sua sala constitui um exemplo de um atributo continuamente variável.
180 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
10. Como veremos no Capítulo 10, qualquer assertiva desse tipo é na realidade equivalente à de-
finição de uma região de espaço-estado da classe.
Cap. 6 DIAGRAMAS DE ESTADO 181
≤tempMáx e ≥ tempMín // OK
<tempMín // frio demais
Note, da mesma forma, que a maioria das transições no diagrama são de-
sencadeadas por uma assertiva when (à parte da assertiva inicial). Isso é algo
próprio das transições entre estados caracterizadas por serem simplesmente
condições de um atributo. Se você achar essa abordagem de modelagem exces-
sivamente repleta de assertivas when, considere a alternativa constante no
exercício 6, deste capítulo.
O diagrama de estado na Figura 6.10 ilustra a convenção de Moore no
qual se colocam mensagens de saída em estados, em vez de em transições (a
qual corresponde à convenção de Mealy).11 Por exemplo, sempre que o estado
frioDemais for atingido, a mensagem aquecedor.ligar será enviada. A palavra-
chave entry no estado frioDemais indica que a mensagem é enviada quando da
entrada nesse estado. (Outras palavras-chave possíveis na UML são: exit,
para a ação realizada ao deixar-se um estado; e do, para a atividade executada
enquanto se está em um estado. Repare que entry e exit denotam ações atômi-
cas, enquanto do denota uma atividade em curso.)
6.6 Resumo
O diagrama de estado mostra os estados admissíveis que os objetos de uma
dada classe podem assumir e as transições permitidas entre pares de estados.
O diagrama é útil para a modelagem de classes cujos objetos têm um atributo
de estado com estas duas propriedades: o atributo pode assumir um pequeno
número de valores possíveis, e as transições permitidas entre esses valores
são restritas. Um atributo que exiba essas propriedades, com valores que re-
flitam os estados naturais de seu objeto proprietário, é chamado atributo de
estado.
Cada estado simples, não aninhado no diagrama de estado, corresponde
a um valor possível de um atributo de estado ou, às vezes, a uma faixa de va-
lores desse atributo. A transição inicial de um diagrama de estado, que conduz
um objeto para seu estado inicial, é indicada por um ponto preto na seta. A
transição final apresenta um “olho-de-boi” na cabeça da seta.
11. Observe que a Figura 6.10 utiliza tanto a convenção de Mealy como a de Moore no mesmo
diagrama de estado, o que é perfeitamente aceitável na UML.
182 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Cada transição entre um par de estados aparece como uma seta no dia-
grama de estado. O desencadeador de uma transição entre estados é um even-
to, que fica anotado acima de uma linha horizontal ou antes de um símbolo
de corte inclinado para a direita (/), em algumas ferramentas. A presença de
mais de um evento acima da linha indica que qualquer um dos eventos lista-
dos pode desencadear a transição. Uma condição de guarda na forma de uma
[expressãoBooleana] talvez apareça após um evento, o que significa que se a ex-
pressãoBooleana for true, a transição poderá se realizar assim que ocorrer o
evento desencadeador.
Existem três tipos de evento na UML: evento de chamada (call event),
evento de mudança (change event) e evento de tempo decorrido (elapsed-time
event). O evento de chamada ocorre mediante o recebimento de uma mensa-
gem de entrada a partir de outro objeto. (Uma variação do evento de chamada
é o evento de sinal [signal event], que ocorre mediante o recebimento de um
sinal explícito proveniente de outro objeto.) O evento de mudança, indicado
por uma cláusula when (expressãoBooleana), ocorre quando da transição de
uma expressãoBooleana de false para true. O evento de tempo decorrido, indi-
cado por uma cláusula after (períodoDeTempo), ocorre no término de um perío-
doDeTempo, o qual é normalmente medido a partir do tempo de entrada até
um dado estado.
Uma ação é normalmente definida como uma mensagem de entrada para
outro objeto ou para self. As ações que acompanham uma transição entre es-
tados (conhecidas como a convenção de Mealy) aparecem abaixo de uma linha
horizontal ou, em algumas ferramentas, depois de um símbolo de corte incli-
nado para a direita (/). A presença de mais de uma ação abaixo da linha indica
a ocorrência de todas as ações listadas.
As ações que se efetivam nos estados (conhecidas como a convenção de
Moore) são listadas dentro do próprio símbolo de estado, em seguida a uma
destas palavras-chave: entry (para ações realizadas quando da entrada no es-
tado), exit (para ações realizadas quando da saída do estado) e do (para ações
realizadas durante a residência no interior do estado).
Uma classe talvez tenha dois ou mais atributos de estado que podem ser
proveitosamente modelados com diagramas de estado. A utilização de diagra-
mas de estado dessa maneira geralmente leva a ter mais de um diagrama de
estado para a classe, ou ter diagramas de estado aninhados, no caso de os atri-
butos não serem totalmente independentes entre si. Um diagrama de estado
aninhado revela estados internos dentro de estados externos. Para evitar que
aconteça um amontoamento gráfico, a UML permite ao modelador exibir es-
tados aninhados como apêndices (stubs), os quais são expandidos em detalhe
184 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
6.7 Exercícios
1. Suponha que um desenhista tenha criado um diagrama de estado com 57
estados e 93 transições, muitos deles sob condições de guarda. A opinião
geral dos críticos é que o diagrama é irremediavelmente emaranhado e
Cap. 6 DIAGRAMAS DE ESTADO 185
6.8 Respostas
1. Certifique-se de que os seus diagramas de estado são abstrações de com-
portamento de classe. Um diagrama de estado não capta — e não deve
captar — todas as facetas e algoritmos possíveis da classe. Se você acha
que seu diagrama de estado está se tornando uma “miscelânea” de esta-
dos e condições, então muito provavelmente você gostaria de repensar
sua noção da classe — ou, no mínimo, posteriormente, abstrair seu dia-
grama. Você também tirará proveito se dividir o seu diagrama único e
emaranhado em vários diagramas de estado, cada um deles baseado em
um particular atributo de estado.
Se o seu modelo de estado de uma classe é terrivelmente complicado,
então você deveria considerar a decomposição desta em subclasses, com
cada subclasse assumindo o comportamento do estado mais importante
da classe original. Por exemplo, você poderia introduzir duas ou três sub-
classes de Máquina, tais como MáquinaFuncionando, MáquinaEsperandoPor-
Conserto e MáquinaEmConserto. (Essa idéia de formação de subclasses
baseadas em estados é muito fortemente enfatizada no trabalho de
Shlaer e Mellor, 1992.)
Cap. 6 DIAGRAMAS DE ESTADO 187
2. Não fique preocupado se a classe na qual você está trabalhando não re-
velar algo profundo por meio de um diagrama de estado. Muitas classes
não produzem diagramas de estado dignos de menção, porque seus atri-
butos podem assumir centenas de valores ou suas transições são irrestri-
tas. Capte o comportamento dessas classes por meio de invariantes de
classe e definições de operações e atributos de classe, e não se aborreça
com os diagramas de estado para essas classes. (Eu abordarei essas defi-
nições no Capítulo 10.)
3. Sim, se você suprimir o símbolo de estado inicial que leva à Funcionando,
o símbolo de estado inicial remanescente vai sugerir que um novo objeto
Máquina inicie no estado/subestado Funcionando/estadoDeEspera. Entre-
tanto, eu não gosto de fazer isso. Minha razão: o que aconteceria se mais
tarde tivéssemos um conjunto de subestados dentro de um outro estado,
com seu próprio símbolo de estado inicial? Dessa forma, nossa supressão
do ponto preto externo faria com que o diagrama se tornasse ambíguo.
4. Para as válvulas perpetuamente abertas, você presumivelmente precisa-
rá acrescentar um estado tal como encherCâmaraDeReação para lidar com
o primeiro problema. Se o seu local de trabalho utiliza when para signi-
ficar whenever (sempre), em conseqüência você não tem o segundo proble-
ma. Se when simplesmente significa when (quando) (como em, “no
momento exato em que false muda para true”), então você poderia intro-
duzir um estado transiente antes de reaçãoTerminada para verificar se
reação.temperaturaDeOperação <= temperaturaFinal.
5. Muito embora atributos continuamente variáveis se manifestem mais ob-
viamente em sistemas de controle de processos em tempo real, também
se pode encontrá-los em alguns sistemas comerciais. Da mesma forma
que o atributo Reação.temperaturaDeOperação da seção 6.4 teoricamente
provaria ser contínuo se pudéssemos medi-lo com freqüência e precisão
suficientes, o atributo Ações.preço poderia ser considerado continuamente
variável. Muito embora nós não possamos medi-lo tão freqüentemente
quanto Reação.temperaturaDeOperação, nossa aplicação poderia receber
uma torrente de preços de ações (semelhante às temperaturas de um rea-
tor), sem pistas sobre quaisquer “eventos contra a corrente” que, em ul-
tima análise, provocariam mudanças no preço.
Portanto, Ações.preço poderia ter um diagrama de estado muito simi-
lar àquele de Reação.temperaturaDeOperação, com os nomes de estado mo-
dificados para razoavelmenteCotadas, superCotadas e subCotadas, para
preservar a aplicação. Uma transição para superCotadas, por exemplo, po-
188 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
contaDeAções.venderPosição (self)
8. A Figura 6.12 mostra essa nova mudança a respeito de reservas. Para re-
fletir essa nova complexidade na diretriz da companhia, a condição de
guarda em cada uma das transições agora tornou-se mais complexa. Eu
tive alguns problemas ao tentar escrever as duas condições de guarda de
forma compreensível, portanto experimentei com as diversas formas des-
sas longas expressões booleanas. Tentei not self.éPopular, por exemplo,
mas descobri que self.éPopular = false é mais fácil de ser entendida. Você
talvez queira experimentar mais adiante para derivar melhores formas
para as expressões booleanas, talvez utilizando-se desta tabela lógica
como algo proveitoso; avaliando o and dos valores das linhas e colunas.
191
192 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
7.1.1 Pacotes
Uma vez que nem sempre dois locais de trabalhos diferentes tem a mesma no-
ção do que é um pacote, eu posso, normalmente, melhor descrevê-lo como um
agrupamento de elementos de software. Dentro de um sistema orientado a ob-
jeto, um pacote é geralmente uma coleção de classes. Ele poderá ser uma co-
leção de classes em uma biblioteca adquirida, uma coleção de classes para
uma particular aplicação ou uma coleção de classes que capture aspectos de
algo do mundo real (FinançasDeCliente, RemessasDeCliente e PerfilDeCliente;
cada uma delas se ocupando de um conjunto de características relativas a
cliente).
A UML ilustra um pacote como uma pasta estilizada, similar ao ícone de
pasta utilizado em muitas aplicações de desktop, conforme exemplificado na
Figura 7.1:
classe de uma classe Cama em qualquer outro pacote, tal como em Inventário-
DeHospital.
Uma seta pontilhada entre dois pacotes significa dependência. Neste
exemplo, a cabeça da seta nas duas extremidades do corpo da seta revela uma
dependência em ambos os sentidos. A seta apontada para a direita indica que
PacoteDePaciente depende de PacoteDeAlaHospitalar (talvez Paciente esteja se
referindo à ocupação de Cama), e a seta apontada para a esquerda indica que
PacoteDeAlaHospitalar depende de PacoteDePaciente (talvez Enfermeira conte-
nha alguma referência a Paciente).
O diagrama de pacote da UML, na Figura 7.2, ilustra como os pacotes po-
dem estar contidos dentro de pacotes, da mesma forma que as pastas podem
estar contidas no interior de pastas em aplicações relativas a gerenciamento
de arquivos.2
cada uma delas representada por um retângulo com pequenas barras abertas
em um lado.7
7. Estranhamente, as “pequenas barras abertas em um lado” não têm qualquer objetivo cone-
xional nos diagramas da UML (embora os “pequenos pirulitos abertos” certamente tenham,
como veremos). Meu colega Stan Kelly-Bootle ressalta que pequenas barras abertas sempre
terão uma finalidade conexional. Não obstante, quando desenho um componente de software
em um quadro branco, substituo as barras por pequenas linhas — ou nada coloco.
8. Desenvolvo mais essa definição no Capítulo 15.
198 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
que imprima por seus próprios termos”. A forma como ele imprime a si próprio
dependerá de o objeto selecionado tratar-se de uma planilha eletrônica, um
documento de texto ou um diagrama.
Pelo lado prático, o desenho orientado a objeto é uma abordagem muito
apropriada para construir a interface humana de um sistema. Você pode de-
senhar a interface do usuário exatamente da mesma forma que qualquer ou-
tra aplicação orientada a objeto. Por exemplo, quando uma janela da classe
JanelaAtualizável fecha, ela envia uma mensagem para uma janela SalvarIgno-
rarOuCancelar para prevenir o usuário que as alterações talvez sejam perdidas.
(A classe JanelaAtualizável pode derivar por herança da classe Janela.)
Os objetos da janela ainda podem trocar mensagens com objetos de negó-
cio. Como simples exemplo, uma janela da classe ObtençãoDeItemDeProduto
pode enviar uma mensagem para um objeto da classe ItemDeProduto a fim de
exibir a relação de VendaPorData de um particular item de produto que esteja
na janela.
Assim, tanto no sentido filosófico (ação-objeto) quanto no sentido prático
(desenho orientado a objeto), as interfaces gráficas do usuário ainda terão um
lugar proeminente na etapa orientada a objeto durante muito tempo no futuro.
7.3 Resumo
Este capítulo apresentou a notação de modelagem adicional para o desenvol-
vimento de sistemas, especialmente aqueles com uma arquitetura de sistema
sofisticada ou uma interface gráfica do usuário.
A UML oferece dois diagramas complementares para capturar os aspec-
tos relativos ao software e hardware da arquitetura de sistemas. O primeiro
é o diagrama de pacote, que representa agrupamentos exclusivamente de ele-
mentos de software. Esse diagrama é apropriado para modelar a estrutura de
alto nível do software a ser implementado.
O segundo diagrama é o diagrama de implantação, que retrata as unida-
des de tecnologia (especialmente hardware, tal como processadores e armaze-
namento de massa, juntamente com seus vínculos físicos de comunicação) nas
quais o sistema será implementado. O diagrama de implantação pode também
modelar como o software será distribuído pelas unidades de tecnologia esco-
lhidas, sobrepondo componentes de software e suas interconexões em um dia-
grama de implantação representativo de pura tecnologia física (tal como
processadores).
Um componente de software é um corpo de código em run-time com uma
interface bem-definida que provê serviços a outros componentes. Na prática,
um componente de software em um diagrama de implantação pode ser uma
Cap. 7 ARQUITETURA E DIAGRAMAS DE INTERFACE 207
7.4 Exercícios
1. O particionamento de software em unidades de tecnologia pode ocorrer
em diversos "níveis de granularidade". Por exemplo, certo subsistema in-
teiro poderia residir em um processador, enquanto outro subsistema fica-
ria em outro processador. Você consegue pensar em outros níveis de
particionamento, especialmente alguns que são melhores que o nível do
subsistema, que seriam possíveis com um sistema orientado a objeto?
(Dica: de que forma você poderia particionar objetos e as classes nas
quais eles pertencem?)
2. O fragmento de um diagrama de navegação de janelas na Figura 7.13
mostra três janelas a partir da interface de um sistema de informação --
cliente. Inicie com a janela SelecionarCliente e, julgando pelas rotas de na-
vegação e dos nomes das janelas e botões, adivinhe o que acontecerá em
cada janela mostrada. Para quais diagramas você se voltaria para confir-
mar ou refutar sua especulação?
208 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
7.5 Respostas
1. Uma possibilidade é que os objetos de determinada classe estariam dis-
tribuídos em processadores. Por exemplo, poderíamos ter outra em Paris,
outra em Tóquio, uma máquina no Rio de Janeiro e outra em Buenos Ai-
res. Os objetos da classe Cliente poderiam, então, ser distribuídos por es-
sas máquinas, pela razão óbvia de eficiência: Gostaríamos de, por
exemplo, acessar rapidamente nossos clientes japoneses na máquina de
Tóquio. O código para a classe Cliente presumivelmente seria completa-
mente replicado em todas essas quatro máquinas. O particionamento de
uma população de objetos como essa é denominado particionamento hori-
zontal.
Os objetos poderiam também ser totalmente replicados, talvez para
permitir que o escritório de Paris rapidamente tivesse acesso às informa-
ções sobre “les Hoosiers”. Entretanto, essa redundância complicaria a
manutenção de uma consistência mútua, por meio de cópias do mesmo
objeto.
Cap. 7 ARQUITETURA E DIAGRAMAS DE INTERFACE 209
211
213
214 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
1. O Módulo do Clube Mensal ofereceu uma ilustração disso. Após tornarem-se sócios do clube,
alguém poderia encomendar os mais recentes desenvolvimentos em módulos procedurais ex-
tremamente reutilizáveis. Infelizmente, o clube logo saiu dos negócios.
2. Um grupo horizontal de classes compreende classes do mesmo domínio que não necessaria-
mente interagem. (Os pacotes normalmente exibem essa estrutura.) Um grupo vertical de
classes interage para implementar uma atividade de negócio; elas normalmente são prove-
nientes de diversos domínios. (Os componentes normalmente exibem essa estrutura.) Para de-
talhes adicionais sobre quais classes pertencem a quais domínios, veja a seção 9.1.
216 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Tabela 8.1
Critérios de desenho estruturado (ou nível-1) que governam inter-
relacionamentos entre elementos em cada par de níveis de encapsulamento.
3. Veja, por exemplo, Page-Jones, 1988, e Yourdon e Constantine, 1979, para discussões deta-
lhadas sobre fan-out, coesão e acoplamento.
Cap. 8 ENCAPSULAMENTO E CONGENERIDADE 217
Tabela 8.2
Prolongamento da Tabela 8.1 para a inclusão de encapsulamento
de nível-2 (classes).
8.2 Congeneridade
Congeneridade (connascence), palavra derivada do latim, significa “terem nas-
cidos juntos”. Uma sugestão para esse significado poderia ser “destinos entre-
laçados na vida”. Dois elementos de software que são congêneres (ou que
nasceram ao mesmo tempo) são gerados desde alguma necessidade afim —
talvez durante a análise de requisitos, no desenho, ou na programação — e
compartilham um destino devido a pelo menos uma razão. A seguir tem-se a
definição de congeneridade da forma como ela é aplicada ao software:
2. que você pode postular alguma mudança, que pediria que tanto A
como B mudassem juntos para preservar a exatidão global.
int i: // linha A
e o elemento B a declaração:
i := 7; // linha B
X: JUMP Y+38
...
Y: CLEAR R1
... // 38 bytes de código ora existentes
CLEAR R2 // Esta é a instrução que “salta” de X para Z
...
Congeneridade de nome
Vimos isso no primeiro exemplo (linhas A e B) anterior, no qual duas variáveis
de programação precisam ter o mesmo nome a fim de referir-se à mesma coi-
sa. Outro exemplo: uma subclasse que utilize uma variável herdada de sua su-
perclasse deve obviamente utilizar o mesmo nome para a variável que o
utilizado pela superclasse. Se o nome for alterado na implementação da super-
Cap. 8 ENCAPSULAMENTO E CONGENERIDADE 221
classe, então ele deverá também ser trocado na subclasse se a exatidão tiver
de ser preservada.
Congeneridade de convenção
Digamos que a classe NúmeroDeConta tenha instâncias nas quais os números
de conta positivos, caso de 12345, pertençam a pessoas; os números de conta
negativos, como –23456, pertençam a empresas, e 00000 pertença a todos de-
partamentos internos. O código será salpicado com assertivas como:
if ordem.númeroDaConta > 0
then...
Congeneridade de algoritmo
A congeneridade de algoritmo é similar à congeneridade de convenção. Exem-
plo: um elemento de software insere símbolos em uma tabela de remodelagem
(hash-table). Outro elemento está à procura de símbolos na tabela. Evidente-
mente, para isso funcionar, ambos devem utilizar o mesmo algoritmo de re-
modelagem. Outro exemplo: os algoritmos de codificação e verificação para os
dígitos de cheques em um número de conta de um cliente.
Um exemplo bizarro de congeneridade de algoritmo que presenciei recen-
temente foi causado por um bug na implementação de um algoritmo de ope-
rações de uma classe. Supunha-se que esta operação fosse retornar uma série
de valores, classificados na ordem ascendente. Mas, devido ao bug, os dois úl-
timos valores da série estavam sempre trocados. Por causa da ausência de có-
digo-fonte, ninguém foi capaz de corrigir o bug.
Dessa forma, o “dilema” era o seguinte: qualquer classe que invocasse
essa operação tinha um código incorporado à mesma que recambiava nova-
mente os dois valores “atacados” da série. Isso proporcionou uma terrível con-
generidade de algoritmo, que afetou seriamente a todos quando o defeito
original foi finalmente corrigido. Conseqüentemente, todos os trechos de códi-
go (em mais de quarenta lugares) tiveram de ser encontrados e removidos.
Congeneridade de posição
A maioria dos códigos em uma unidade de procedimento tem congeneridade
de posição: cada duas linhas de código que devem ser executadas na correta
seqüência de execução, elas devem aparecer na exata seqüência léxica na lis-
tagem. Existem diversos tipos de congeneridade de posição, que incluem a se-
qüencial (“devem aparecer na ordem correta”) e adjacente (“devem aparecer
próximas uma da outra”). Outro exemplo de congeneridade de posição é a con-
generidade em uma mensagem entre os argumentos formais no remetente e
os argumentos reais no destinatário. Na maioria das linguagens, você deve es-
pecificar argumentos reais na mesma seqüência dos argumentos formais.
A congeneridade dinâmica é a congeneridade baseada no modelo de exe-
cução do código de execução — os objetos, preferentemente às classes, se as-
sim você desejar. Essa congeneridade, da mesma forma, tem diversas
variedades:
Congeneridade de execução
A congeneridade de execução é o equivalente dinâmico da congeneridade de
posição. Ela se apresenta em diversos tipos, incluindo a seqüencial (“deve ser
executada em uma dada ordem”) e adjacente (“deve ser executada sem qual-
Cap. 8 ENCAPSULAMENTO E CONGENERIDADE 223
Congeneridade temporal
A congeneridade temporal manifesta-se na maioria das vezes em sistemas de
tempo real. Por exemplo, uma instrução para desligar um aparelho de raio-X
deve ser executada em um período de n milissegundos com relação à instrução
para ligá-lo. Essa restrição temporal permanece verdadeira, não importando
quantas vezes o sistema operacional necessite reentrar a tarefa ControllerDe-
Raio-X com o intuito de levar a cabo a carga de funcionamento de suas outras
tarefas.
Congeneridade de valor
A congeneridade de valor normalmente envolve alguma restrição aritmética.
Por exemplo, durante a execução de um sistema, o indicadorInferior de um
buffer circular jamais poderá ser superior ao indicadorSuperior (segundo as re-
gras da aritmética de módulos). Igualmente, os quatro cantos de um retângulo
devem preservar certo relacionamento geométrico para seus valores: você não
consegue simplesmente mover um canto e reter um retângulo perfeito.
A congeneridade de valor é notória por ocorrer quando dois bancos de da-
dos mantêm a mesma informação redundantemente, muitas vezes em forma-
tos diferentes. Nessa situação, o software de procedimento tem de conservar
uma ponte de consistência entre os bancos de dados para assegurar que qual-
quer dado duplicado tenha valores idênticos em cada banco de dados. A ma-
nutenção desse software, o qual está provavelmente executando desastrados
traslados de formatos, pode ser embaraçosa.
Congeneridade de identidade
Um exemplo de congeneridade de identidade é proporcionado por uma restri-
ção típica em um sistema orientado a objeto: dois objetos, obj1 e obj2, cada um
dos quais com uma variável apontando para outro objeto, devem sempre apon-
tar para o mesmo objeto. Isto é, se obj1 aponta para obj3, então obj2 deve
apontar para obj3. (Por exemplo, se o informe de vendas refere-se à planilha
eletrônica de março, então o relatório de operações forçosamente deve referir-
se à mesma planilha eletrônica.) Nessa situação, os obj1 e obj2 têm congene-
ridade de identidade; ambos devem referir-se ao mesmo (ou melhor, ao
idêntico) objeto.
224 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
8.2.2 Contrageneridade
Até o momento, tacitamente equiparei a congeneridade com “igualdade” ou
“afinidade”. Por exemplo, duas linhas de código têm congeneridade de nome
quando a variável em cada uma delas deve portar o mesmo nome. Entretanto,
a congeneridade ainda existe em casos em que a diferença é importante.
Primeiro, darei um exemplo trivial. Digamos que temos duas declarações:
int i;
int j;
isso teria lhes informado que a herança por meio de uma subclasse deve-
ria apenas ficar restrita àquelas propriedades da superclasse já externa-
mente visíveis. (Outra forma de dizer isso seria: a noção do comporta-
mento abstrato herdado deveria ser divorciada da noção de herdar a im-
plementação interna desse comportamento.)8 Caso a biblioteca deles ti-
vesse sido desenhada de acordo com este princípio, suas vidas teriam sido
menos repletas de problemas, e talvez eles não tivessem chamado aquele
local de trabalho de “A Terra dos Acertos à Meia-Noite”.
3. Contando com acidentes de implementação
Em outro local de trabalho no passado, um programador chamado de “O
Astuto” (The Weasel) — não me pergunte o motivo! — tinha criado uma
classe Conjunto que fornecia o comportamento de um conjunto matemá-
tico. (Suas operações incluíam adicionar, remover, dimensionar e assim por
diante.) A classe aparentava estar bem desenhada e escrita e sempre tra-
balhava de forma satisfatória.
O Astuto utilizava essa classe Conjunto em diversos locais em suas
próprias aplicações. Por exemplo, utilizou a operação recuperar, que recu-
perava elementos do conjunto um por um em uma ordem randômica até
que todos os elementos fossem supridos. Todavia, O Astuto sabia que a
operação recuperar recuperava elementos exatamente na mesma ordem
em que eles tinham sido atrelados ao conjunto, embora isso não tivesse
sido documentado ou fosse esperado como uma propriedade inerente da
operação. Ele, por diversas vezes, se apossou desse fato acidental e não
documentado em sua aplicação. Ele tinha, dessa forma, criado uma con-
generidade de algoritmo por meio da fronteira de encapsulamento entre
a aplicação e as partes internas da operação recuperar.
Quando mais adiante Conjunto foi substituído por uma implementação
diferente — uma que aconteceu de não preservar a ordem da mesma for-
ma —, muitas das aplicações de O Astuto “explodiram”. Muitos dos usuá-
rios também “explodiram”, mas o Astuto não era encontrado em lugar
algum. Correram rumores de que ele tinha adotado outra identidade e fu-
gido clandestinamente para o Meio Oeste. Assim, vamos esperar que O
Dia do Astuto não se repita.
8.3 Resumo
O encapsulamento é um conceito venerável no mundo do software. A sub-ro-
tina, inventada nos anos 40, introduziu o encapsulamento de código nos mó-
dulos procedurais; isso corresponde ao encapsulamento de nível-1. Entretanto,
as estruturas orientadas a objeto são mais sofisticadas do que as estruturas
procedurais convencionais, como a sub-rotina. A orientação a objeto envolve
pelo menos o encapsulamento de nível-2. No encapsulamento de nível-2, as
operações (implementadas pelos métodos) são por si próprias encapsuladas,
juntamente com os atributos (implementados pelas variáveis), em classes.
9. Se dois objetos tinham de ser gerados conjuntamente, então diria que eles tinham congeneri-
dade de geração. Se dois objetos tinham de ter a mesma existência, eu diria que eles tinham
congeneridade de existência — ou, se quisesse impressionar as pessoas, congeneridade de du-
ração.
Cap. 8 ENCAPSULAMENTO E CONGENERIDADE 233
8.4 Exercícios
1. Um homem em um bar me disse, em uma ocasião, que toda idéia sobre
música moderna poderia ser encontrada em algum lugar nas obras de
Haydn. Poderia ser dito algo semelhante sobre o livro de Yourdon e Cons-
tantine com relação ao desenho estruturado (Yourdon e Constantine,
1979): suas páginas formam uma obra-prima de idéias de desenho muitas
vezes esquecidas, que são “redescobertas” de tempos em tempos. Verifi-
que esse livro para ver se Yourdon e Constantine têm algo a dizer sobre
congeneridade.
2. A utilização da declaração goto ficou famosa durante as últimas décadas
como uma das causas de termos software incompreensível. Do ponto de
vista da congeneridade, você consegue justificar a má reputação de goto?
3. Suponha que você tenha ficado cansado da orientação a objeto. Você de-
seja criar um novo paradigma relacionado a software que emprega níveis
de encapsulamento e tem várias formas de congeneridade. Explique (de
forma geral) como você estabeleceria os critérios e as diretrizes de dese-
nho para a congeneridade e o encapsulamento em seu paradigma.
4. Este capítulo discutiu a congeneridade principalmente em termos de có-
digo de programação. Há quaisquer outros exemplos de congeneridade
que emergem no contexto mais amplo do projeto global de desenvolvi-
mento de software?
5. Na seção 8.2.4, sugeri que a congeneridade implícita que cruza as fron-
teiras de encapsulamento normalmente provoca problemas particular-
mente a mantenedores de sistemas orientados a objeto. Você pode dar
exemplos dessa espécie de congeneridade e sugerir como torná-la mais
explícita, e, por conseguinte, mais fácil de ser rastreada?
6. É necessário que se faça pesquisa adicional sobre as formas de congene-
ridade aplicáveis a paradigmas modernos de desenho de software, espe-
cialmente à medida que o desenho orientado a objeto vai se tornando
cada vez mais popular. Realize uma experiência para trazer à tona os
graus de danos provocados pelas diferentes variedades de congeneri-
dade (por exemplo, congeneridade de nome e posição). O experimento po-
deria medir os efeitos da congeneridade (ambos dentro e através das
fronteiras de encapsulamento) em fatores dependentes pressupostos, tais
como tempo/custo da compreensão humana, tempo/custo de depuramento
e tempo/custo de modificações. Talvez você possa recrutar voluntários
Cap. 8 ENCAPSULAMENTO E CONGENERIDADE 235
para rever o código-fonte e medir seus tempos para detectar bugs delibe-
radamente semeados no mesmo.
8.5 Respostas
1. Sim, Yourdon e Constantine começam uma exploração do que é essencial-
mente congeneridade no Capítulo 3 do livro deles. Entretanto, após intro-
duzirem esse conceito (sob o termo vago estrutura) e abordá-lo
brevemente, o capítulo se desvia de certo ângulo do conceito geral. De-
pois, especialmente no Capítulo 6, o livro flerta novamente, e de forma
atormentadora, com esse tópico.
2. Há muito tempo sabemos que o uso indisciplinado da declaração goto
provoca a divergência das estruturas dinâmicas e estáticas do código. Em
termos de congeneridade, isso implica que a congeneridade de posição (na
listagem de código) forneça pouca pista para a congeneridade dinâmica
de execução (em run-time). Visto que os mantenedores fazem modifica-
ções no código estático, os gotos aumentam o risco de uma alteração es-
tática violar alguma congeneridade de execução. Além do mais, qualquer
goto induz uma congeneridade de nome extra entre o goto em si e a eti-
queta que é o alvo de goto, juntamente com a contrageneridade entre os
próprios nomes de etiquetas.
3. Nesse caso, veja uma estrutura possível sobre a qual se baseia uma defi-
nição de um futuro paradigma de software:
a. Defina o objetivo pretendido e o alcance de aplicabilidade de seu pa-
radigma.
b. Defina a estrutura de encapsulamento do paradigma. Estabeleça
quais são os componentes do paradigma e quais componentes estão
contidos dentro de quais outros componentes.
c. Em termos da estrutura de encapsulamento anterior, estabeleça as
regras padrão de visibilidade do paradigma. Isso prescreverá as cone-
xões permitidas entre componentes e especificará as “fronteiras de
privacidade” estabelecidas pela estrutura de encapsulamento.
d. Liste as possíveis formas de congeneridade inerentes no paradigma.
Haverá congeneridade explícita, a qual aparece no código-fonte, e con-
generidade implícita, que será mais difícil de ser percebida porque é
“invisível”. Como vimos, a congeneridade implícita torna-se particu-
larmente sutil quando ela transcende a estrutura de encapsulamento
oficial do paradigma.
236 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
1. Note que, nesse caso, refiro-me a negócio no sentido mais amplo possível. Incluo aí negócios
que envolvem eletrônica aplicada à aviação, instrumentação, controle de fornos microondas,
juntamente com os negócios mais tradicionais de atividades bancárias, seguros e atividades
pesqueiras.
237
238 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Uma parte da aplicabilidade das classes de negócio pode até mesmo ser
mais delimitada do que o bloco representativo de uma indústria: elas talvez
sejam proveitosas só para uma única corporação. De fato, visto que algumas
grandes corporações se envolvem em diversos segmentos de negócios, a utili-
zação de uma dada classe de negócio talvez nem encerre mais que uma divi-
são. Por exemplo, tenho conhecimento de uma empresa que está
desenvolvendo sete classes diferentes, todas nomeadas como OrdemDeCompra
— e essa empresa tem só nove divisões! Suspeito que eles poderiam efetuar
uma reengenharia na empresa para reduzir o número de classes OrdemDe-
Compra incompatíveis. Mas duvido que, algum dia, eles conseguissem reduzir
esse número para uma única classe, porque até mesmo duas divisões não
prosseguiriam fazendo compras exatamente nos mesmos moldes.
Novamente, vemos o modelo em que as classes nos domínios mais altos
são construídas, com base nas classes mais baixas. Por exemplo, Titularidade-
DeConta vai referenciar as classes Cliente e Conta, e, no mínimo, um atributo
de Conta pertencerá à classe Saldo.
das as classes de que necessita — mas também você gastará mil vezes mais
do que a biblioteca lhe custaria.
Você certamente terá de adicionar a sua biblioteca classes de base adqui-
ridas, com algumas classes de sua própria elaboração. Até aí tudo bem! Po-
rém, compre, tome emprestado e peça quaisquer classes de base de que você
precise antes de recorrer à opção última de construí-las.
ção do que sua biblioteca de classes de base em razão dos dois pontos
anteriores.
seu desenho fique tão bom, você não conseguirá muita reutilização. A princi-
pal origem para as classes nesse domínio são os eventos de negócio que você
descobre durante a análise.
Talvez “construção” seja um termo melhor que “classe” nos parágrafos
anteriores, visto que as classes reconhecedoras e gerenciadoras de eventos po-
dem ser implementadas da mesma forma que os procedimentos convencionais,
ou pacotes de utilidade de instância simples, em lugar de classes de raça de
puro sangue. Por exemplo, o objeto gerenciador de eventos AquecimentoDePa-
cienteHipotérmico poderia vir a ser um procedimento impar. Alternativamente,
ele poderia tornar-se a única operação de uma nova classe, ou mesmo poderia
tornar-se uma operação da classe Paciente já existente.
• C é herdeira de D.
• C tem um atributo da classe D.
• C tem uma operação com um argumento de entrada de dados da clas-
se D.
• C tem uma variável da classe.
• C tem um método que envia uma mensagem com um argumento re-
tornado da classe D.
• C tem um método contendo uma variável local da classe D.
• C supre D como um parâmetro de classe real em uma classe parame-
trizada.
• C tem uma classe amiga (friend) D (em C++).
4. Se você preferir uma definição até mesmo mais matemática: o conjunto classe-referência in-
direto de C é equivalente ao fechamento transitivo no conjunto classe-referência direto de C.
5. Alguns autores utilizam o termo acoplamento de classe total (total class coupling) para o ta-
manho de um conjunto de referência direto de uma classe.
Cap. 9 DOMÍNIOS, GRAU DE DEPENDÊNCIA E COESÃO 247
7. Na mitologia grega, Deméter era a deusa da colheita. Entretanto, essa Lei de Deméter tem
seu nome originário de um projeto orientado a objeto, denominado Deméter. Veja Lieberherr
e Holland, 1989, para detalhes adicionais.
250 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
definida na classe C em si. A Lei Fraca de Deméter define uma variável como
quer uma variável de C ou uma variável que C herda de suas superclasses.
A Lei de Deméter é eminentemente razoável, pois ela restringe referên-
cias arbitrárias a outras classes dentro de uma dada classe. Segundo a lei, o
código dentro de C irá referir-se apenas ao número mínimo viável de outras
classes. Prefiro a Lei Forte à Lei Fraca de Deméter pelo fato de que ela ainda
limita a congeneridade por meio das fronteiras de encapsulamento que execrei
no Capítulo 8 — neste caso, as fronteiras de classe das superclasses de C.
Como vimos na seção 8.2.4, a limitação de congeneridade por esse meio
simplifica a manutenção e a desdobralidade (evolvability) do sistema por duas
razões:
8. Coesão [de módulo] procedural, um termo do desenho estruturado, é uma medida de quão
bem as linhas de código constituem juntas dentro de um único módulo procedural, tal como
uma função ou um procedimento.
Cap. 9 DOMÍNIOS, GRAU DE DEPENDÊNCIA E COESÃO 251
fred.lançarPorcentagemDeComissão;
mary.lançarPorcentagemDeComissão;
252 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Uma classe com coesão de domínio misto contém um elemento que di-
retamente cria dependência da classe em relação a uma classe extrín-
seca de um domínio diferente.
9. No caso de sua trigonometria escolar estar um pouco enferrujada, eu deveria explicar que a
função arctang é a função inversa da tangente. Ela toma um número real r e lhe diz o ângulo
cuja tangente é r. O arctang de 1,0 é 45 graus, por exemplo. (arccos é a função inversa do
co-seno).
254 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
junto de clientes cujos saldos bancários fossem (até o último centavo) igual a
algum número real. A lista de possibilidades absurdas é interminável, confor-
me mostrado pela Figura 9.6.
Quando você desenhar uma classe de um dado domínio, você terá de in-
cluir classes dos domínios mais baixos em seu projeto — isso é inerente à reu-
tilização. Mas certifique-se de que você precisa realmente dessas classes por
causa das propriedades intrínsecas de sua classe. Por exemplo, seria inteira-
mente satisfatório para a classe Conta ter uma operação que retornasse um
objeto da classe Dinheiro ou mesmo um atributo da classe Data. Entretanto, eu
ficaria com muitas desconfianças se a classe retornasse um objeto da classe do
domínio de arquitetura, WireXferLink.
O domínio de arquitetura muitas vezes acaba misturado em uma classe
do domínio de negócio. Isso é errado — a menos que seu negócio esteja real-
mente edificando infra-estruturas arquiteturais. O antigo lema orientado a
objeto de “uma coisa deveria saber como fazer algo ela própria”, como em “um
documento deveria saber como imprimir ele próprio”, pode ser um pouco
atraente demais. Uma classe Documento, com conhecimento específico de uma
impressora, tem coesão de domínio misto.
Ao desenhar uma classe de um dado domínio, você deveria ser particu-
larmente cauteloso quanto à introdução de classes de domínio mais alto na
classe que você está desenhando. Esse é o motivo pelo qual anteriormente a
classe NúmeroReal era um problema. Número Real é uma classe de base, e não
deveria criar dependência com classes de domínio mais alto, ainda que, no de-
senho sem recursos visto por mim, o atributo arctang de NúmeroReal (da classe
Ângulo) tenha forçado NúmeroReal a lidar com Ângulo, uma classe de um grupo
mais alto de classes (a saber, do grupo semântico).
Cap. 9 DOMÍNIOS, GRAU DE DEPENDÊNCIA E COESÃO 255
Uma classe C com coesão de papel misto contém um elemento que di-
retamente cria dependência dessa classe com uma classe extrínseca
que reside no mesmo domínio de C.
E onde devemos parar com essa filosofia de desenho? Por que não incluir
esses atributos em Pessoa: númeroDeCarrosPossuídos, númeroDeBarcosPossuí-
dos, númeroDeGatosPossuídos, númeroDeSaposPossuídos...? Não apenas esses
atributos criariam dependência de forma severa em relação à classe Pessoa
com outras classes, como também cada um deles, ainda implicaria outro esta-
belecimento de operação para a atualização do número de coisas possuídas.10
A coesão de papel misto, conforme exemplificado por “pessoa possui ca-
chorro”, também é perfeitamente atraente porque:
Entretanto, por mais atrativos que sejam esses argumentos, você não de-
veria se distrair ao criar automaticamente uma classe Pessoa com coesão de
papel misto. Existem muitas outras opções de desenho que não comprometem
a coesão de Pessoa; eu exploro quatro dessas opções em detalhes no exercício
final do Capítulo 14.
Na qualidade de desenhista orientado a objeto, você deveria visar a cria-
ção de classes com coesão ideal, ou seja, classes destituídas de coesão de ins-
tância mista, de domínio misto ou de papel misto. As classes com coesão ideal
apresentam uma reutilização máxima, e isso é o objetivo principal de muitos
locais de trabalho voltados à orientação a objeto (particularmente para as
classes no domínio de negócio).
9.4 Resumo
Uma classe pode pertencer a um entre quatro domínios. Esses domínios são:
o domínio de base, que compreende classes de valor em todos os negócios e ar-
quiteturas; o domínio de arquitetura, que compreende classes de valor para
uma arquitetura de implementação; o domínio de negócio, que compreende
10. Sim, você poderia generalizar todas essas operações para númeroDeCoisasPossuídas usando
tipoDeCoisa passado como um argumento. Mas o problema básico de coesão permaneceria.
11. Acho essa visão antropomórfica da orientação a objeto capciosa e irrelevante. Eu a incluí por-
que muitas vezes ouvi sobre ela como um “princípio de desenho” orientado a objeto.
Cap. 9 DOMÍNIOS, GRAU DE DEPENDÊNCIA E COESÃO 257
9.5 Exercícios
1. Uma biblioteca de classes que você comprou de um distribuidor de classes
gerais provavelmente conterá somente classes de base. Por que você acha
que isso ocorre?
258 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
9.6 Respostas
1. Há diversas razões: em primeiro lugar, a maioria dos distribuidores visa
o mercado que tenha o máximo potencial comercial. Por definição, as clas-
Cap. 9 DOMÍNIOS, GRAU DE DEPENDÊNCIA E COESÃO 259
ses de base são proveitosas para o grupo mais amplo possível de indús-
trias e aplicações. Em segundo lugar, a maioria das classes de base de-
manda um conhecimento da informática em geral, mais que de qualquer
negócio em particular. Conseqüentemente, um distribuidor típico de clas-
ses pode reivindicar a si próprio (com credibilidade) um conhecimento das
classes de base tão bom quanto o de qualquer outro tipo de classe.
E, em terceiro lugar — e essa é a razão pela qual as bibliotecas da maio-
ria dos distribuidores não contêm classes de negócio —, essas classes são
difíceis de serem analisadas e freqüentemente incorporam informações e
planos de ação de negócios que as companhias consideram propriedade
delas. A maioria das companhias não se encontra atualmente na ativida-
de comercial de negociar suas especializações de negócios sob a forma de
classes de software para fins de reutilização.
Todavia, acredito que essa situação mudará e que, breve, veremos biblio-
tecas de classes do domínio de negócio específicas para indústria à venda
no mercado, inclusive bibliotecas de classes destinadas a atividades ban-
cárias, telecomunicações e serviços médicos. No momento, muitas das
forças-tarefa do OMG — Object-Management Group — estão se empe-
nhando em trabalhos voltados a componentes orientados a objeto especí-
ficos para indústria.
2. Não. Uma classe muito provavelmente não derivará uma quantidade sig-
nificativa de seu grau de dependência indireto de uma classe mais alta
na hierarquia de herança de classes por duas razões. Em primeiro lugar,
a herança é apenas um meio pelo qual uma classe se refere a outras clas-
ses; o grau de dependência de uma classe é devido também a muitos ou-
tros meios de referência. Em segundo lugar, as classes mais altas em
uma hierarquia de herança de classes tendem a ter um grau de depen-
dência que varia de baixo a médio, porque elas se referem a algumas ou-
tras classes. Por conseguinte, a contribuição de uma classe situada nas
partes altas da hierarquia para o grau de dependência de suas subclasses
provavelmente não será dramática.
Todavia, uma variação do grau de dependência indireto (recentemente,
estive em um local de trabalho que trabalhava sobre ela) exclui de um
conjunto classe-referência indireto de uma classe C quaisquer classes cu-
jas características não sejam realmente utilizadas pelos métodos de C.
Essa variedade de grau de dependência indireto pode render uma medida
mais acurada do grau de dependência de C, mas é também mais compli-
cada de se calcular do que a descrita por mim neste capítulo.
260 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
3. A classe Hominóide apresentava coesão de domínio misto, visto que ela ti-
nha uma operação exibir que mostrava um hominóide em uma tela. As-
sim, uma vez que Hominóide é presumivelmente misturado com
dispositivos e seus formatos, até mesmo podemos precisar de uma versão
diferente de Hominóide para cada uma das arquiteturas (impressora,
plotter, monitor) presentes no local de trabalho. É possível até mesmo que
nós — nem é bom pensar nisso! — solicitássemos uma versão diferente
para toda e qualquer resolução possível de monitor.
Se isso não o incomodar (provavelmente porque portabilidade e reutiliza-
ção não são problemas), então tudo bem! Mas, do contrário, você deverá
ter a classe Hominóide retornando uma referência para um objeto que de-
tenha o formato de exibição do hominóide (que seria da classe ModoBit ou
ModoLinha). Assim, você poderia enviar o objeto em uma mensagem para
um objeto de um dispositivo de saída, que, em seguida, o exibiria.
Entretanto, você talvez não goste dessa abordagem de desenho, porque
você pode considerar que a exportação da representação do padrão de bit
de um hominóide não devia ser parte da abstração de Hominóide. Se você
desejar capacitar uma operação exibir para acessar informação sobre a
aparência de um hominóide, mas, ao mesmo tempo, não quiser exportar
essa informação de Hominóide, você poderá criar outra classe, Hominói-
deExibível, herdeira de Hominóide, conforme mostrado na Figura 9.7. A
operação exibir, definida em HominóideExibível, operaria em variáveis de-
finidas no interior de Hominóide.
Tabela 9.1
As seis possíveis combinações de coesão de instância mista,
de domínio misto e de papel misto12.
Combinação IM DM PM
1 N N N
2 N N S
3 N S S
4 S N N
5 S N S
6 S S S
Embora essa frase soe um tanto pomposa, o que as palavras nela contidas
significam na realidade?
Com abstração, quero dizer que não necessariamente temos de considerar
toda propriedade possível das coisas do mundo real representada por objetos
de software. Por exemplo, embora possamos ter uma classe ClienteDeBancoPes-
263
264 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
Dessa forma, por que esse cavalo é diferente? A resposta é que, embora
ele compartilhe do mesmo comportamento que o cavalo tradicional, o espaço-
estado dele difere.
Do que já foi dito, vimos que duas classes podem diferir, seja no tocante
aos seus espaços-estados, seja nos seus comportamentos (ou em ambos). Ago-
ra, definirei espaço-estado de maneira mais exata. (Definirei comportamento
na seção 10.3.)
266 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
1. Eu disse “aproximadamente” porque a maioria dos atributos que podem ser derivados de ou-
tros não são normalmente considerados dimensões. Por exemplo, as dimensões de um Cubo
podem ser comprimento, largura e altura, mas não volume ou área.
Cap. 10 ESPAÇO-ESTADO E COMPORTAMENTO 267
sões são objetos da classe da dimensão. Por exemplo, a dimensão preço de Li-
nhaDeProduto seria marcada com objetos da classe Dinheiro, e a altura de um
paciente seria marcada em polegadas ou centímetros, objetos da classe Com-
primento.
2. Para ser coerente, eu deveria denominar essas dimensões de martelo (da classe Martelo) e
cabeça (da classe Cabeça). Discuto dimensões mais adiante, no exercício 1, no final deste ca-
pítulo.
268 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
classe. Em outras palavras, meu carro poderia ser um automóvel mas não um
veículo rodoviário — um absurdo.
Ironicamente — e isso surpreende muitas pessoas — o espaço-estado de
uma subclasse pode ter mais dimensões que o espaço-estado da superclasse.
Essa definição implica que nem todas as transições possíveis são válidas
para um objeto. Um objeto fica “pulando em volta” dentro do espaço-estado de
sua classe somente da maneira prescrita para ele pelo comportamento da clas-
se. Como vimos na seção 10.1, embora um cavalo possa ser encontrado em
qualquer uma das 64 casas do tabuleiro de xadrez, suas transições permitidas
são bastante limitadas (no máximo, oito transições desde a sua casa atual).
E o comportamento em subclasses? Os comportamentos de superclasse e
subclasse apresentam alguma relação parecida com o dos espaços-estados? Es-
Cap. 10 ESPAÇO-ESTADO E COMPORTAMENTO 271
Uma invariante de classe é uma condição que todo objeto dessa clas-
se deve sempre satisfazer (quando o objeto está em equilíbrio).
a + b ≥ c and b + c ≥ a and c + a ≥ b
Essa expressão afirma que, não importa qual objeto da classe Triângulo ti-
vermos, a soma dos comprimentos de dois quaisquer lados da figura deverá
ser maior ou igual que o comprimento de seu terceiro lado. (Não resta dúvida
de que podemos aparecer com diversas outras restrições similares em Triângu-
lo.)
A grade tridimensional da Figura 10.6 (com os eixos rotulados como a, b
e c) representa o espaço-estado de Triângulo. Porém, isso não é estritamente
verdadeiro; trata-se de um exagero. Apenas alguns dos pontos na grade — os
quais satisfazem a invariante anterior para o Triângulo — fazem parte do real
espaço-estado de Triângulo. Por exemplo, não poderíamos ter um triângulo com
lados de 1, 2 e 5.
4. Para manter esse exemplo simples, ignorarei a posição e a orientação dos triângulos e me con-
centrarei no tamanho deles. A invariante (com seu ≥ em lugar de >) permite degenerar triân-
gulos que são só linhas retas.
Cap. 10 ESPAÇO-ESTADO E COMPORTAMENTO 273
a = b or b = c or c = a
a + b ≥ c and b + c ≥ a and c + a ≥ b
not vazia
em que a palavra-chave old significa “qualquer que seja o valor que esta
tinha antes de a operação ser executada”.
Bertrand Meyer e outros descrevem as precondições e pós-condições de
uma operação como um contrato entre uma operação e um cliente que envia
uma mensagem para essa operação.7 A metáfora do contrato implica que
Vamos assumir que o retângulo tenha lados w1, h1, w2 e h2.8 Sua inva-
riante de classe será:
w1 = w2 and h1 = h2
larguraMáximaPermitida ≥ w1 * fatorDeEscala
w1 = old w1 * fatorDeEscala
10.6 Resumo
Este capítulo introduziu as propriedades de uma classe: espaço-estado e com-
portamento permitido. O espaço-estado de uma classe A é a totalidade dos es-
8. Você talvez esteja se perguntando por que minha classe Retângulo tinha todos os quatro la-
dos de um retângulo, quando apenas largura e altura resolveriam. Eu fiz isso para prover um
simples exemplo de uma invariante de classe. Nas seções 13.1 e 13.2 examino, em detalhes,
diversos desenhos para Retângulo.
Cap. 10 ESPAÇO-ESTADO E COMPORTAMENTO 277
10.7 Exercícios
1. Suponha uma classe ContaDeCartãoDeCrédito com duas dimensões (entre
outras) denominadas saldoAtual e limiteDeCrédito. Por que não nomeamos
simplesmente a dimensão saldoAtual após sua classe Dinheiro?
2. O que é a dimensionalidade do espaço-estado da classe Retângulo? Em ou-
tras palavras, quantas dimensões tem a classe Retângulo? Antes de você
responder: “Duas! Altura e largura. Isso é fácil —, deixe-me salientar que
a questão é indefinida até que você tenha uma boa idéia de qual é a abs-
tração realmente representada por Retângulo. Assim, deixe-me definir Re-
tângulo como uma classe cujos objetos são retângulos capazes de girar,
sofrer expansões na altura e largura e vagar pelo plano.
Portanto, quantas dimensões a classe Retângulo, com essa definição,
realmente tem? Em outras palavras, quantos graus de liberdade tem
realmente cada objeto Retângulo? Se você fosse desenhar a implementa-
ção interna de Retângulo, quantos graus de liberdade seu desenho interno
permitiria? Esse número poderia ser diferente em sua primeira resposta?
Em caso afirmativo, qual a razão?
3. Como o comportamento de uma subclasse poderia ser considerado mais
restrito (com relação a sua superclasse) se uma subclasse tivesse dimen-
sões extras em seu espaço-estado, tendo-se em vista os espaços-estados de
suas superclasses?
4. As invariantes de classe para as classes TriânguloRetângulo, TriânguloIsós-
celes e TriânguloIsóscelesRetângulo eram complexas e ainda similares
umas às outras. Você poderia decompor essas similaridades de alguma
forma, e, portanto, também simplificar a expressão das invariantes?
10.8 Respostas
1. Embora saldoAtual possa pertencer à classe Dinheiro, nomear a dimensão
saldoAtual como Dinheiro causaria certa ambigüidade. Por exemplo, visto
que pelo menos duas dimensões do espaço-estado de ContaDeCartãoDeCré-
dito são monetárias, como saberíamos que dimensão era o saldoAtual e
que dimensão representaria o limiteDeCrédito?
Incidentalmente, poderíamos optar por conferir a saldoAtual uma clas-
se mais sofisticada, digamos SaldoDeCartão (a qual se basearia na classe
mais fundamental Dinheiro). Isso poderia constituir uma solução extrava-
gante, porque ela nos proporcionaria outra classe em nossa biblioteca re-
querendo manutenção. Apenas seria recompensador atuar dessa forma se
Cap. 10 ESPAÇO-ESTADO E COMPORTAMENTO 279
283
284 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
1. Incidentalmente, um subtipo, de modo idêntico, não necessita ser uma subclasse. Mesmo em
uma linguagem isenta de uma hierarquia de herança do tipo superclasse/subclasse, você ain-
da pode desenhar e implementar subtipos à mão (por meio de código tediosamente duplicado).
Entretanto, deve-se obedecer ao princípio da conformidade de tipo, o qual corresponde ao tó-
pico central da próxima seção.
2. Para mais detalhes sobre o princípio da conformidade de tipo em um meio orientado a objeto,
veja Meyer, 1992 e LaLonde e Pugh, 1991. O princípio da conformidade de tipo é muito simi-
lar ao que autores tais como Barbara Liskov denominam princípio da substitutibilidade (Lis-
kov e outros, 1981).
Cap. 11 CONFORMIDADE DE TIPO E COMPORTAMENTO FECHADO 287
sim, qualquer operação que estiver esperando receber uma elipse como um ar-
gumento em uma mensagem deverá estar muito feliz por obter um círculo.3
Embora tivéssemos visto na seção 11.1 que subclasse e subtipo são con-
ceitos distintos, estou agora torcendo minhas palavras e dizendo:
3. Naturalmente, isso não funciona se a operação tentar ampliar o círculo! Isso explica o aviso
“quando qualquer operação de acesso for executada” (que implica que nenhum objeto muda
de estado) em minha definição de conformidade. Dificuldades como essa transmitem um bo-
cado de incitamento, conforme discuto na seção 11.3.
288 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
4. Os termos mais forte e mais fraco nas restrições acima não descrevem de maneira alguma
qualidade ou robustez. Mais forte não é melhor e mais fraco não quer dizer pior.
Cap. 11 CONFORMIDADE DE TIPO E COMPORTAMENTO FECHADO 289
A Figura 11.4 mostra as faixas válidas e ilegais para %DeBônus sob forma
gráfica.
7. Para mais ramificações deste exemplo funcionário/gerente, veja o exercício 1 no final do capí-
tulo.
294 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
tentando esticar a mangueira para alcançar uma árvore mais distante, notei
que meu vizinho também estava regando a parte correspondente ao terreno
dele.
“Saudações, amigo esguichador!” — anunciei cordialmente por sobre nos-
sa cerca tosca comum.
“O que você quer desta vez”, ele retrucou, com seu jeito amistoso habi-
tual. Ele já parecia saber o que eu desejava, pois recordava a última vez que
me dirigira a ele, e pedindo emprestado seu cortador de grama. (Aquilo tinha
sido um verdadeiro “golpe de misericórdia”.)
Após uma breve negociação, tomei emprestada sua mangueira para es-
tender meu insuficiente tubo de borracha até o canto do jardim. Infelizmente,
entretanto, sua mangueira tinha um diâmetro não padronizado de 5/8 polega-
das, ao passo que a minha era de 3/4 polegadas — um calibre normal. Por sor-
te, eu tinha adquirido recentemente na loja de ferragens local, da HoseHut,
um conjunto de adaptadores para tubos apropriado para viagens ao exterior.
Após voltar correndo do barracão de ferramentas, acoplei as duas mangueiras
e rapidamente terminei de jogar água em minha plantação de cenouras.
Minha experiência em conectar mangueiras inspirou-me para que eu de-
senhasse a ilustração da Figura 11.6:
Cap. 11 CONFORMIDADE DE TIPO E COMPORTAMENTO FECHADO 295
8. Para tornar essa descrição bem compreensível, usei o conceito de uma faixa mais ampla, em
lugar do conceito mais geral de supertipo com ilustração de uma precondição mais fraca.
296 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
9. O exercício 4 no final do capítulo acompanha esse último ponto e sugere uma modificação
mais drástica — porém mais robusta — para o desenho de herança.
300 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
11.4 Resumo
Este capítulo introduziu os princípios da conformidade de tipo e do comporta-
mento fechado, que nos ajudam a identificar problemas potenciais em nossos
desenhos de hierarquias de classes.
O princípio da conformidade de tipo diz que a classe B é um subtipo da
classe A somente se um objeto da classe B for aceitável em qualquer contexto
em que um objeto da classe A é esperado, e caso não for executada qualquer
operação modificadora. Uma hierarquia superclasse/subclasse robusta seguirá
este princípio: se a classe B é uma subclasse da classe A, então B se conforma
à A.
Para se atingir essa propriedade, você deverá assegurar o seguinte: a in-
variante de cada subclasse é, no mínimo, tão forte quanto a invariante de sua
superclasse; cada operação na superclasse tem uma correspondente operação
na subclasse com o mesmo nome e assinatura; cada precondição da operação
da subclasse não é mais forte do que a correspondente operação na superclas-
se; e cada pós-condição da operação da subclasse é pelo menos tão forte quanto
a operação correspondente na superclasse. Estes últimos dois princípios são
denominados, respectivamente, princípio da contravariação e da covariação;
eles são muito importantes quanto à determinação das classes corretas para
os argumentos de operações de subclasses.
Cada classe em uma hierarquia de classes sadia igualmente obedecerá ao
princípio do comportamento fechado. Ele requer que o comportamento que
uma subclasse herda, de uma ou de várias superclasses, respeite a invariante
da subclasse. Um desenhista de subclasse pode alcançar esse princípio ao efe-
tuar o seguinte: não permitindo heranças de comportamento conflitante; su-
primindo operações herdadas com comportamento conflitante; ou migrando
para outra classe um objeto que tenha violado sua invariante de classe.
Ainda que você possa ter achado que alguns dos tópicos neste capítulo se-
jam difíceis ou obscuros, você descobrirá que eles, em breve, se tornarão bas-
tante familiares se você se referir aos mesmos continuamente quando
construir hierarquias de classes e código de desenho orientado a objeto. Feliz-
mente, você descobrirá ainda que 90% das situações de desenho orientado a
objeto com as quais você se depara são claras, sem apresentar dificuldades, e
não requerem um conhecimento muito profundo de desenho. Mas, quando
Cap. 11 CONFORMIDADE DE TIPO E COMPORTAMENTO FECHADO 301
11.5 Exercícios
1. O que aconteceria se, no exemplo da classe Funcionário com a subclasse
Gerente da seção 11.2.2, você não conseguisse desenhar a precondição de
calcularBônus no mínimo tão fraca e sua pós-condição no mínimo tão forte
na subclasse? (Por exemplo, as demandas de negócio dos usuários podem
dominar os clamores da sua consciência de desenho.) Uma vez que isso
pode significar que Gerente não constitui um subtipo verdadeiro de Fun-
cionário, você, em conseqüência, deve recompor sua estrutura de classe?
2. Dado que uma operação abstrata pode, por definição, jamais ser invoca-
da, faz algum sentido para uma operação desse tipo ter uma precondição
e uma pós-condição? (Dica: considere as precondições e pós-condições das
operações concretas nas subclasses que omitem a operação abstrata.)
3. Tome a classe Retângulo e considere uma de suas operações, girar, que
roda um retângulo em seu próprio plano segundo um certo ângulo:
girar (ânguloDeRotação)
escalaHorizontal (fatorDeEscala)
11.6 Respostas
1. Eis aqui uma solução. Se a discrepância em calcularBônus for a única saí-
da da classe Gerente por ser um subtipo verdadeiro, então você provavel-
mente não incomodaria a hierarquia de herança de classe. Mas você deve
documentar bem a discrepância — tanto na descrição completa da classe
como na operação transgressora específica. Verifique cuidadosamente
quanto a este tipo de problema ao desenhar certos ensaios; pois se ele es-
corregar entre os dedos, mais cedo ou mais tarde causará uma interrup-
ção polimórfica.
Mas, cuidado, porque essa solução é como brincar com fogo. Seria pru-
dente de sua parte recompor a hierarquia de classes, conforme mostrado
na Figura 11.10, e enfrentar esse problema de uma vez por todas, mesmo
se algumas aplicações existentes necessitarem de modificação para utili-
zar o novo nome de classe, NãoGerente.
Observe que agora os não gerentes têm a própria classe deles, ao lado
da classe dos gerentes. Visto que o particionamento entre as duas classes
é completo, nunca precisaremos criar um objeto da classe Funcionário, e
Funcionário pode, por conseguinte, razoavelmente ser desenhada como
abstrata. Presumivelmente, a operação calcularBônus também poderá ser
abstrata, porque, com base na descrição do plano direcionado a funcioná-
rios neste capítulo, não parece existir um algoritmo que calcule bônus de
gerentes e de não gerentes.
Graças à estrutura utilizada na Figura 11.10, não há qualquer proble-
ma de covariação ou contravariação entre NãoGerente.calcularBônus e Ge-
rente.calcularBônus, porque Gerente não é uma subclasse de NãoGerente.
Cap. 11 CONFORMIDADE DE TIPO E COMPORTAMENTO FECHADO 303
2. Suponha que uma operação abstrata C.op, com precondição pré e pós-con-
dição pós, seja suprimida pelas operações concretas op1, op2,...,opn nas
respectivas subclasses de C. As seguintes assertivas são, portanto, verda-
deiras:
será utilizado em sua aplicação atual, bem como de que forma ele poderá
ser reutilizado em aplicações futuras.
4. Polígono deveria ter duas subclasses, PolígonoDeLadoFixo e PolígonoDeLa-
doVariável. Triângulo (e classes similares) deveria ser uma subclasse de
PolígonoDeLadoFixo. Essa abordagem permite que um desenhista posicio-
ne a operação acrescentarVértice no PolígonoDeLadoVariável e remova a ne-
cessidade de se suprimir acrescentarVértice em classes representativas de
polígonos de lados fixos. (Se PolígonoDeLadoVariável tem subclasses, então
você deveria nomeá-las TriânguloMutável, RetânguloMutável e assim por
diante, caso pudessem ser acrescentados ou removidos vértices.)
Observe que essa solução, com suas subclasses decompostas, se parece
com a da Figura 11.10.
O s perigos da herança e do
polimorfismo
FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
OS PERIGOS DA HERANÇA E DO POLIMORFISMO
12.Os Perigos da Herança e do Polimorfismo
306
Cap. 12 OS PERIGOS DA HERANÇA E DO POLIMORFISMO 307
2. Tudo bem, sei que um panda é uma espécie de guaxinim, mas, por favor, queira ser compla-
cente comigo.
Cap. 12 OS PERIGOS DA HERANÇA E DO POLIMORFISMO 311
Seja qual for o meio como Urso.espécie obtém seu valor, os objetos da clas-
se Urso poderiam procurar pelas suas espécies — e as propriedades associadas
de suas espécies — em run-time, com o código tal como:
self.espécie.estáEmRiscoDeExtinção;
self.espécie.pesoMáximo;
Dessa vez, Quarto tem uma variável de instância formato, que aponta
para um objeto da classe FormatoFechado3D (ou de uma classe de suas sub-
classes, tais como Cubóide, Cilindro ou Tetraedro). Em outras palavras, a classe
Quarto contém a declaração:
outra classe. Como substituto, você tem de visar esse acesso, mensagem por
mensagem, para os atributos e operações da outra classe que você procura.
Talvez você queira rememorar as dores de cabeça que os desenhos originais
baseados em herança provocaram, e conferir como esse desenho baseado em
transmissão de mensagens as conseguiu debelar.5
5. Alguns autores utilizam o termo delegação (delegation) para transmissão de mensagens. En-
tretanto, evitei esse termo, porque ele é utilizado geralmente mais para um conceito orientado
a objeto que está além do escopo deste livro. (Contudo, também defino “delegation” no Glos-
sário.)
318 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
6. Um dos meus alunos refere-se a A como “cabeça de cone do polimorfismo”. Não me deixe apa-
nhá-lo utilizando esse termo!
Cap. 12 OS PERIGOS DA HERANÇA E DO POLIMORFISMO 319
No primeiro caso, tudo está bem com o desenho. Não importa para qual
objeto objdestinatário está apontando, esse objeto será de uma classe que “en-
tende” a mensagem operdestinatária. O segundo caso, entretanto, representa
um desenho miserável e não robusto. Esse desenhista está brincando com o
diabo, visto que é bem possível que em run-time, objdestinatário aponte para
um objeto sobre o qual a operdestinatária da classe não está definida. Se isso
ocorrer, então o programa provavelmente irá “sofrer interrupção” com um erro
de run-time.
Para um exemplo específico dos possíveis relacionamentos entre o SOP de
uma variável e o SOP de uma operação, considere a mensagem dispositivoDe-
Fábrica.ligar. Conforme mencionado a seguir, há dois casos:
Caso 1: A mensagem dispositivoDeFábrica aponta sempre para
um objeto da classe Torneira, Motor ou Luz, todos os quais podem
ser ligados (ou aberta — no caso da torneira). Assim, o SOP de
dispositivoDeFábrica está dentro do SOP de ligar (abrir), e tudo
estará bem. Entretanto, eu sugeriria alterar o nome da variável
para dispositivoOperável ou dispositivoAlterável para indicar que
ele se refere a um dispositivo que pode ser operado ou ligado
(aberto). Utilizo um complemento como alterável em meus no-
324 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
que cria um novo objeto, uma árvore de classe referida por árvoreDeNú-
meroReal, que deterá números reais em seus nós. Também poderíamos escre-
ver:
árvoreDeCliente := ÁrvoreDeClasse<Cliente>.Novo;
Cap. 12 OS PERIGOS DA HERANÇA E DO POLIMORFISMO 325
que deteria objetos da classe Cliente em seus nós. Dentro da classe Árvo-
reDeClasse, escreveríamos declarações tais como:
nó := ClasseDeNó.Novo;
nó.imprimir
if novoItem.menorQue (nóAtual)
// cada um destes, novoItem e nóAtual, aponta para um objeto da
ClasseDeNó
then...
12.3 Resumo
A herança e o polimorfismo trazem poder e concisão ao software orientado a
objeto. Eles, de maneira idêntica, trazem perigos. O principal perigo da heran-
ça reside na utilização excessiva dela ou, mais precisamente, em sua utiliza-
ção inadequada em situações nas quais outras construções orientadas a objeto
se sairiam melhor.
Neste capítulo, examinamos os quatro abusos comuns de herança. A pri-
meira é o uso da herança em que a agregação é requerida. Esse é um erro ele-
mentar, raramente cometido pelos desenhistas experientes voltados à
orientação a objeto. O segundo abuso é a inversão da hierarquia da herança
de classe, causada muitas vezes pelo engodo de uma estrutura desencaminha-
dora do mundo real.
A terceira má utilização de herança é a confusão de classe com instância.
Isso tende a ocorrer em desenhos que precisam lidar tanto com grupos (tais
como espécies ou companhias) quanto com indivíduos (tais como animais ou
funcionários). Visto que o problema é geralmente sutil, um desenhista poderia
inicialmente fazer vistas grossas ao mesmo. Contudo, quando o desenho im-
perfeito é transformado em código, torna-se óbvio que o código não pode tra-
balhar como pretendido. A quarta utilização inadequada envolve a utilização
de herança, sendo que a transmissão de mensagens (message forwarding) pro-
veria uma construção de desenho mais apropriada.
O termo polimorfismo aplica-se tanto às operações como às variáveis.
Uma operação polimórfica é definida em diversas classes diferentes. Uma va-
riável polimórfica pode, a qualquer hora, apontar para objetos pertencentes a
328 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
12.4 Exercícios
1. No exemplo do Panda da seção 12.1.3, sugeri que você poderia fazer de es-
táEmRiscoDeExtinção: Booleano um atributo de instância (uma constante)
em EspécieNãoAmeaçadaDeExtinção e EspécieAmeaçadaDeExtinção. Mas es-
sas subclasses são realmente necessárias? Será que não conseguiríamos
dar um jeito apenas com Espécie, conforme sugeri em uma nota de rodapé
na seção 12.1.3?
2. Que modificações você teria de fazer na Figura 12.6, caso as duas classes
LinhaDeProdutoManufaturado e LinhaDeProdutoComprado estivessem so-
brepondo, em vez de decompondo, subclasses de LinhaDeProduto? (Em ou-
tras palavras, algumas linhas de produto são manufaturadas dentro da
fábrica e compradas de distribuidores externos.)
3. Como você provavelmente sabe, uma pilha do tipo “último dentro, primei-
ro fora” é uma estrutura que suporta uma série de objetos, com apenas
um deles podendo ser acessado (lido) ou removido (extraído) de cada vez.
Esse objeto é considerado o cabeça da pilha; é o objeto que foi, mais re-
centemente, agregado à (ou retirado da) pilha.
Cap. 12 OS PERIGOS DA HERANÇA E DO POLIMORFISMO 329
4. Será que alguns dos princípios de desenho orientado a objeto que vimos
nos capítulos anteriores são pertinentes aos problemas de herança cober-
tos neste capítulo? Em caso afirmativo, quais deles?
5. Na seção 12.2.1, quando defini o escopo de polimorfismo de uma operação,
implicitamente tratei só de hierarquias de classes de herança simples.
Que questões adicionais, se existirem, surgiriam com o SOP de uma ope-
ração caso a herança múltipla estivesse presente?
6. Suponha que uma operação op seja uma operação abstrata de uma classe
C (a qual é uma classe abstrata). Suponha além disso que op seja concre-
tamente definida sobre todas as subclasses de C, e não abstratas. Você po-
deria considerar C o vértice de polimorfismo de op, embora C.op, na
realidade, não seja implementada?
7. Suponha que uma operação op seja definida sobre uma classe A e que
seja herdada por todos os descendentes de A. Normalmente, isso signifi-
caria que A e seus descendentes formam um cone de polimorfismo para
op, com A no vértice. Mas em que situação esse grupo de classes poderia
formar um SOP imperfeito em vez de um cone completo?
330 FUNDAMENTOS DO DESENHO ORIENTADO O OBJETO COM UML
8. Imagine que você pudesse fechar seus olhos e solicitar uma ferramenta
automatizada para ajudá-lo a avaliar seu desenho ou programa orientado
a objeto. Que recursos sua ferramenta poderia proporcionar para estimar
se o SOP de uma variável iria ou não cair dentro do SOP de uma operação?
12.5 Respostas
1. Nós, possivelmente, poderíamos remover a classe EspécieNãoAmeaçada-
DeExtinção. Entretanto, a classe EspécieAmeaçadaDeExtinção é necessária
se quisermos registrar, digamos, dataDoPrimeiroRiscoDeExtinção, organiza-
çãoDePreservaçãoResponsável, e assim por diante. (De forma contrária, se
esses atributos fossem definidos somente para algumas instâncias de Es-
pécie, então Espécie teria coesão de instância mista.) Se esses atributos
não forem relevantes, poderíamos trabalhar apenas com Espécie. Portan-
to, poderíamos fazer de estáEmRiscoDeExtinção um atributo de Espécie.
O valor de estáEmRiscoDeExtinção não seria constante, porque espécies
se movem para dentro e para fora do risco de extinção. Todavia, o movi-
mento de uma espécie para dentro e para fora do risco de extinção torna-
se um pouco mais difícil de desenhar se Espécie tem as subclasses
EspécieNãoAmeaçadaDeExtinção e EspécieAmeaçadaDeExtinção. Uma abor-
dagem de desenho é esta: suprima um objeto de uma subclasse (digamos,
EspécieNãoAmeaçadaDeExtinção) após salvar suas informações; então gere
um objeto da outra classe (EspécieAmeaçadaDeExtinção), que possa contar
com quaisquer informações relevantes sobre as espécies.
2. Se as duas classes LinhaDeProdutoManufaturado e LinhaDeProdutoCompra-
do se sobrepõem, então o atributo éComprado, com seus valores mutua-
mente exclusivos true e false, não mais faria sentido. De preferência,
necessitaríamos de dois atributos booleanos: éComprado e éManufaturado.
Também deveríamos acrescentar uma outra classe, denominada LinhaDe-
ProdutoManufaturadaEComprada (ou algo menos embaraçoso), que é her-
dada, por herança múltipla, de LinhaDeProdutoManufaturado, e
LinhaDeProdutoComprado. Essa nova classe geraria objetos que represen-
tariam linhas de produto que fossem tanto manufaturadas como compra-
das.
A questão de migração da resposta 1 (anterior) também se apresenta
neste caso: o que aconteceria se determinada linha de produto mudasse,
por exemplo, de comprada para manufaturada? Novamente, como na
questão anterior, poderíamos suprimir o objeto de sua subclasse e reini-
ciarmos sua geração em outra subclasse. Mas, desta vez, eu gostaria de
Cap. 12 OS PERIGOS DA HERANÇA E DO POLIMORFISMO 331
if self.éComprado
then preçoMáximo := self.aspectoComprado.limiteDePreçoUnitário
...
335
336 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
negócio, na verdade, poderia ser qualquer um que utilizasse uma fatura sim-
ples para apresentar uma conta pela venda de vários itens.) A classe agregada
Fatura e sua classe constituinte ItemDeFatura aparecem na Figura 13.1.
Tudo está tranqüilo, mas o desenho da Figura 13.2 ainda limita a reuti-
lização da operação de fax, a qual nós até mesmo denominamos FaturaPorFax.
Que vergonha termos toda essa tecnologia de fax modem meio escondida e in-
disponível para nós quando queremos passar fax de itens distintos de faturas,
tais como: agradecimentos, cumprimentos, ameaças e assim por diante.!
Nesse momento é que aparece uma classe mista para nos socorrer, con-
forme mostrado pela Figura 13.3.
338 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Embora a Figura 13.3 seja apenas sutilmente distinta da Figra 13.2, a di-
ferença é importante. Neste desenho, “decompomos” uma classe mista, Docu-
mentoEnviável, a qual têm toda a presteza em executar serviços de fax e de
e-mail. De forma relevante, entretanto, DocumentoEnviável não tem qualquer
conhecimento sobre faturas; ela é uma classe geral, capaz de enviar por fax
ou por e-mail qualquer documento. Portanto, agora, vamos seguir, tintim por
tintim, como o desenho integral funciona.
Quando queremos criar um objeto para representar uma nova fatura, in-
vocamos FaturaEnviável.Nova. Inicializamos esse objeto — vamos nos referir a
ele como faturaEnviável — fornecendo a este itens de fatura (e quaisquer infor-
mações de cabeçalho) e vinculando-o ao objeto Cliente responsável. Tudo isso
Cap. 13 TÉCNICAS PARA ORGANIZAR OPERAÇÕES 339
repeat
obter a próxima linha de fatura; // — F
until não mais linhas de fatura
converter para formato de texto o textoDeLinha;
self.anexarTextoADocumento (textoDeLinha) // — DE
endrepeat
end criarDocumento;
1. Você pode utilizar o atributo doc, somente para leitura (read-only), para acessar esse docu-
mento de texto.
2. • Chave: a anotação “— DE” significa “via herança a partir de DocumentoEnviável”; a ano-
tação “— F” significa “via herança a partir de Fatura”.
• N.T.: No original, em vez de DE temos SD, e em vez de F temos I. A troca foi feita para a
compreensão ser mais imediata no caso do leitor de língua portuguesa. DE significa “documen-
to enviado e F significa “fatura”.
340 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
case cliente.meioComercialDePreferência
“CORREIO”: self.documentoDeCorreio (cliente.nome, cliente.endereço); // —DE
“E-MAIL”: self.documentoDeE-Mail (cliente.nome, cliente.endereço eletrônico); // — DE
“FAX”: self.documentoDeFax (cliente.nome, cliente.número DeFax); // — DE
else...; //erro!
endcase;
end enviarParaCliente;
class Retângulo;
class FormatoEmMoldura;
class RetânguloEmMoldura;
inherits from FormatoEmMoldura, Retângulo;
...
public operation moverDentroDeMoldura (increm.DeMovimento: Vetor2-D);
begin
var increm.DeMovimentoPermitido:=Vetor2-D:=Vetor2-D.Novo; // deterá o efetivo
// movimento permitido
then
if increm.DeMovimento.x > 0 // para a direita nesta convenção
then increm.DeMovimentoPermitido.x:= mín (increm.DeMovimento.x,
self.molduraCircundante.direita-self.direita);
else increm.DeMovimentoPermitido.x:= máx (increm.DeMovimento.x,
self.molduraCircundante.esquerda-self.esquerda);
endif;
endif;
end moverDentroDeMoldura;
...
endclass RetânguloEmMoldura;
Observe agora que o desenhista utiliza uma mensagem para invocar mo-
ver, em lugar de “extrair” diretamente o valor da variável centro. Mas por que
o desenhista simplesmente não extraiu centro diretamente por codificação, por
exemplo,
3. Para fins de clareza, limitei minha explicação a dois anéis, mas na prática poderá haver di-
versos anéis de operação.
Cap. 13 TÉCNICAS PARA ORGANIZAR OPERAÇÕES 347
Agora temos uma resposta mais completa para a questão mais antiga:
por que o desenhista da operação moverDentroDeMoldura (na classe Retângu-
loEmMoldura) não atualizou a variável centro diretamente? Devido ao perigo
de se ter operações da subclasse RetânguloEmMoldura confundindo as variá-
veis da superclasse Retângulo. (Essa é a terceira razão listada anteriomente.)
Considere o que teria acontecido se o desenhista da operação moverDen-
troDeMoldura, de fato, diretamente manipulasse o centro do retângulo, e se
nosso distribuidor de classes tivesse nos remetido uma nova versão da classe
Retângulo, versão essa que armazena os (em lugar de fazer os cálculos dos)
quatro vértices do retângulo, conforme mostrado no código a seguir.
Repare que agora a operação mover é mais complicada, pelo fato de ela
ter de manter a informação redundante retida por v1, v2, v3 e v4. (A informa-
ção é redundante porque os quatro vértices podem ser calculados a partir das
variáveis centrais de representatividade, centro, altura, largura e orientação).
Se o sistema tivesse sido simplesmente recompilado e vinculado novamente,
então a operação moverDentroDeMoldura exibiria um defeito: separaria os can-
tos de um retângulo do seu centro.
Cap. 13 TÉCNICAS PARA ORGANIZAR OPERAÇÕES 349
13.3 Resumo
Este capítulo tratou da organização e do desenho de operações. A primeira
abordagem de desenho que exploramos utilizou classes mistas para livrar-se
de outras classes de abstrações que não pertencem a suas interfaces. Vimos
que uma classe mista é uma construção relativamente simples, normalmente
abstrata. Assim, um desenhista utiliza a abstração ou o mecanismo que a clas-
se mista personifica, via herança, para criar uma nova classe de combinação.
Esta nova classe, com suas diversas avenidas de herança, pode então possuir
abstrações gerais (digamos, do domínio de negócio) e abstrações mais especí-
ficas (digamos, do domínio de arquitetura).
Ao restabelecer abstrações restritivas de uma classe de negócio em uma
classe mista, um desenhista aperfeiçoa a coesão da classe de negócio, o grau
de dependência e a reutilização. Visto que a mesma classe mista talvez possa
ser útil em diversas situações de desenho, pode-se eliminar a presença de có-
digo supérfluo nas aplicações e nas bibliotecas de classes. A reutilização das
habilidades da classe mista também é melhorada.
A segunda abordagem de desenho neste capítulo tratou da organização
de operações em anéis para criar camadas de encapsulamento dentro de uma
única classe. Essa abordagem utiliza a ocultação de informações e implemen-
tações nas operações do “anel interno” para proteger as operações do “anel ex-
terno” do conhecimento desnecessário da forma como as variáveis são
implementadas. Por conseguinte, se o desenhista tivesse que modificar, diga-
mos, os nomes, classes ou outros detalhes de certas variáveis, menos opera-
ções precisariam ser escritas de novo.
13.4 Exercícios
1. No desenho da Figura 13.3, a classe FaturaEnviável apresenta coesão de
domínio misto, porque ela conhece faturas (do domínio de negócio) e co-
municação (do domínio de arquitetura). Essa coesão de domínio misto re-
presenta algum problema para esse desenho?
4. Retorno a esse exemplo e ao assunto de desenho para fins de eficiência no exercício 4, no final
deste capítulo.
350 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
13.5 Respostas
1. A coesão de domínio misto da classe FaturaEnviável não chega a ser na
realidade um problema, porque não esperamos reutilização de FaturaEn-
viável. Essa classe está lá apenas para “sofrer as conseqüências”, sendo
sua tarefa aperfeiçoar a reutilização de DocumentoEnviável e Fatura.
2. A razão é fácil de ser compreendida: um retângulo não é uma moldura.
(Coincidentemente, entretanto, as molduras nessa aplicação eram retân-
gulos.) Se estivéssemos prestes a capacitar Retângulo como herdeira de
Moldura, então violaríamos o princípio de conformidade de tipo que foi
discutido no Capítulo 11.
3. A Figura 13.7 mostra QuartoCubóide, desenhado de forma a ser herdeiro,
por herança múltipla, de Cubóide e Quarto.
Esse desenho trabalhará para que os objetos da classe QuartoCubóide enten-
dam a mensagem volume (ou obterVolume, se você preferir esse estilo). Eles exe-
cutarão a operação volume definida em Cubóide (em que, presumivelmente,
comprimento, largura e altura também residem.) Entretanto, como vimos no Ca-
pítulo 12, esse desenho implica que QuartoCubóide herde um comportamento des-
necessário de Cubóide (girar, por exemplo). Para evitar que alguém utilize girar
em um quarto cubóide, o desenhista de QuartoCubóide deverá suprimir essa ope-
ração (e quaisquer outras que forem desnecessárias ou perigosas).
Cap. 13 TÉCNICAS PARA ORGANIZAR OPERAÇÕES 351
Outro problema com esse desenho é que você não pode substituir a classe
Cubóide por Formato3D, cujas subclasses incluem Cubóide, Cilindro e assim por
diante. O melhor modo de entender o porquê de não se poder utilizá-la é ima-
ginar que o desenhista de Formato3D tenha feito de volume uma operação abs-
trata, o que seria muito plausível. Agora, uma subclasse como um
QuartoDeFormato3D herdaria uma operação volume sem qualquer implemen-
tação. O que não é de muita utilidade!
Certamente, ter QuartoCubóide herdado de Cubóide viola o princípio da
conformidade de tipo. Você poderia afastar todas essas dificuldades tendo uma
classe como Quarto3D herdada de algo parecido com FormatoFixado3D e Quar-
to.5 Sucessivamente, a classe FormatoFixado3D teria uma variável que se refe-
re a um objeto da classe Formato3D. Isso é exatamente análogo ao desenho de
RetânguloEmMoldura da Figura 13.5, mas trata-se de algo demasiadamente
elaborado quando comparado ao desenho do Quarto da Figura 12.9.
4. A seguir temos o código para uma das operações de RetânguloEmMol-
dura, moverDentroDeMoldura, que, agora, move o retângulo dentro da
moldura circundante por meio de transmissão de mensagens para um
objeto Retângulo referido como retângulo, em vez de por via herança a
partir de Retângulo (como no desenho mostrado na Figura 13.5).
5. FormatoFixado3D trata-se de uma classe que se assemelha a Formato3D, mas carece (por
exemplo) de uma operação escala.
352 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
class RetânguloEmMoldura;
...
var retângulo:Retângulo; // será inicializada para apontar para o retângulo movível
var molduraCircundante: Moldura // será inicializada para apontar para a moldura
// circundante
...
public operation moverDentroDeMoldura (incrementoDeMovimento: Vetor-2D);
begin
355
356 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
gue alcançar. Por exemplo, vamos supor que, devido ao desenho pobre da
classe Retângulo, todos os retângulos deverão apresentar larguras maio-
res do que suas respectivas alturas. Em outras palavras, não poderiam
ser criados retângulos com formato quadrado, e nem retângulos “altos”.
De acordo com minha experiência, esse tipo de imperfeição de interfa-
ce de desenho ocorre menos freqüentemente do que a imperfeição de in-
terface devido a estados ilegais. Mas, observe que uma interface pode ter
um problema duplo: ela é capaz de suportar estados ilegais e estados in-
completos.
3. Estados inapropriados
Um desenho de interface de classe com estados inapropriados normal-
mente propicia aos usuários do lado de fora de um objeto alguns estados
que não são formalmente partes da abstração de classe do objeto. Por
exemplo, suponha que um desenhista tenha criado uma classe Pilha desse
tipo (“último dentro”, “primeiro fora”). Ele implementou a pilha por meio
de um arranjo e de um identificador de arranjo. Até o momento, sem pro-
blemas. Mas o que aconteceria se, agora, fizéssemos com que o identifi-
cador de arranjo fosse publicamente visível? Dessa forma, poderíamos
dizer que ele criou uma interface com estados inapropriados, pois um
identificador de arranjo não constitui parte de uma abstração de pilha.
(Os usuários de uma pilha deveriam ver somente o último elemento, e
identificar se a pilha está vazia ou cheia.) Como outro exemplo desse tipo
de transgressão de interface, o desenhista pode permitir aos usuários de
uma pilha verificar, por exemplo, o décimo sétimo elemento dela.
(Aqui, estou assumindo que o desenhista não vá permitir aos usuários
da pilha efetivamente alterar o identificador de arranjo ou o décimo séti-
mo elemento. Se isso fosse permitido, conseqüentemente o desenhista te-
ria criado uma interface que suportasse estados ilegais. Por exemplo, um
usuário poderia atribuir ao identificador de arranjo um número negativo
ou um número extremamente grande.)
Entretanto, a questão dos estados inapropriados torna-se algo um tan-
to intrincado em muitos projetos. Por exemplo, a profundidade de uma
pilha constitui uma informação que deve ser levada a conhecimento pú-
blico? A maioria das pessoas que estuda o conceito de pilhas iria respon-
der. “Não, somente o topo de uma pilha é relevante”. Entretanto,
considere uma classe Fila do tipo (“primeiro dentro”, “primeiro fora”). Nes-
se caso, muitos desenhistas considerariam a extensão atual de uma fila
altamente relevante para o usuário da mesma.
358 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
1. Percival na realidade tinha uma classe Pilha perfeitamente boa na biblioteca de seu local de
trabalho. Entretanto, deixou de utilizá-la, porque, de maneira acertada, ela não suportou o
tipo de travessuras ruidosas que ele pensava ser necessário.
360 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
3. Comportamento irrelevante
O comportamento irrelevante em uma interface de classe é o comporta-
mento que simplesmente não pertence a essa classe e a seus objetos. Por
exemplo, se PedidoDeCliente contivesse uma operação denominada calcu-
larRestituiçãoDeEmpréstimo, conseqüentemente PedidoDeCliente teria com-
portamento irrelevante. O comportamento não é relevante a essa classe,
porque ele não tem qualquer relação com pedidos de clientes: não atuali-
za qualquer objeto PedidoDeCliente e nem mesmo acessa quaisquer variá-
veis de PedidoDeCliente.
Não, você não entendeu mal; incluir comportamento irrelevante em
uma interface corresponde a um desenho estúpido. Afortunadamente
para o mundo orientado a objeto, isso raramente ocorre. O grande repre-
sentante do comportamento irrelevante é um camarada notoriamente fal-
so, chamado Genghis, o Perverso, que trabalha em uma grande
companhia muito distante de sua empresa (Eu espero!). Genghis, com um
certo contentamento, colocaria uma operação calcularDiferençaEntreDatas
na interface de Cliente e determinarMelhorRotaDeTransporte na interface
de Produto. Não me perguntem o motivo. Também não faz qualquer sen-
tido para mim. Pergunte ao Genghis — acho que ele saberá responder!
4. Comportamento incompleto
Uma interface de classe com comportamento incompleto não permite todo
o comportamento que deveria ser posto em prática por objetos dessa clas-
se. Por exemplo, vamos assumir que um pedido de cliente tenha o estado
de aprovado, mas que esse cliente, repentinamente, vá à falência. É in-
teiramente razoável que os usuários do departamento de contabilidade
mudem o estado do pedido mais uma vez, agora para não aprovado. Mas,
em um sistema que revisei recentemente, vi PedidoDeCliente desenhado
de forma tal que, uma vez que um pedido tivesse atingido um estado de
aprovado, não haveria qualquer meio possível de retorná-lo para o estado
de não aprovado. (O desenhista tinha simplesmente ignorado um dos re-
quisitos de análise.)
Com o comportamento incompleto, não temos um caso de uma inter-
face suportando comportamento de forma desastrada ou via estados ile-
gais. Uma interface desse tipo efetivamente veta qualquer
comportamento válido, pelo fato de que nem todas de transições válidas
entre esses estados são suportadas. Observe, entretanto, que uma classe
com uma interface que suporta comportamento incompleto pode ainda
suportar estados ideais, porque (no exemplo anterior) um pedido poderia,
de qualquer forma, ser capaz de atingir um estado de não aprovado; o pro-
362 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
blema apenas é que ele não poderia atingir esse estado vindo de outro,
aprovado.
5. Comportamento inábil
Um objeto cuja classe tenha uma interface com comportamento inábil
pode requerer duas ou mais mensagens para pôr em prática uma única
etapa de comportamento válido. O comportamento inábil se assemelha ao
comportamento perigoso (descrito anteriormente), pois naquele são ne-
cessárias várias mensagens para efetivar um único comportamento de
um objeto. Entretanto, com o comportamento inábil, nenhuma das men-
sagens conduz o objeto a um estado ilegal.
Por exemplo, vamos dizer que um pedido de cliente com um estado de
aprovado pode tornar-se cumprido quando ele tiver estoque e uma data de
remessa atribuídos ao mesmo. É perfeitamente razoável que um pedido
cumprido tenha sua data de remessa alterada. Entretanto, uma interface
poderá ser desenhada de forma que o único meio de se fazer isso seja es-
pecificar o estado do pedido de volta a aprovado, e em seguida restabele-
cê-lo para efetivado com a nova data de remessa. Você, portanto, deverá
enviar duas mensagens a fim de modificar uma data de remessa.
Repare que o objeto passa por meio de um estado adulterado, se bem
que válido. O estado é adulterado porque ele não corresponde à realidade:
agora, o pedido em questão é efetivado e não mais meramente aprovado.
O desenhista deixou de suportar o comportamento requerido para a alte-
ração da data de remessa. Ou, para ser justo com o desenhista, ele pode
ter deixado de suportar o comportamento requerido porque não foi entre-
gue a ele a especificação completa referente a PedidoDeCliente.
Entretanto, desenhar uma interface com comportamento inábil não
chega a ser um pecado capital. De fato, nem sempre fica suficientemente
claro se um objeto deveria ser capaz de mover-se de um estado para outro
em uma única etapa. Por exemplo, seríamos capazes de mover um retân-
gulo para a direita e girá-lo 30o com uma única mensagem, ou isso deve-
ria ser considerado duas etapas de comportamento? Ou, se pudermos
reduzir de novo a escala de um retângulo, com seu centro posicionado no
mesmo lugar, a interface de Retângulo suportaria igualmente essa nova
redução de escala com um vértice mantido no lugar? (Afinal de contas,
tudo isso poderia ser feito invocando-se reduzirDeNovoEscalaNoCentro e
em seguida mover.)
A melhor maneira de responder a essas questões é estudar as neces-
sidades do problema e visualizar como a classe pretende ser utilizada. Se
você puder prever o futuro de maneira infalível, sempre obterá as respos-
Cap. 14 COESÃO DE CLASSE, SUPORTE DE ESTADOS E DE COMPORTAMENTOS 363
2. Em um ensaio sobre esse tema, um colega sugeriu que fosse acrescentada ainda outra opera-
ção replicada — virarNoSentidoAnti-Horário — com o pretexto de que invocar virarNoSenti-
doHorário com um ângulo negativo era artificial. O que você acha?
Cap. 14 COESÃO DE CLASSE, SUPORTE DE ESTADOS E DE COMPORTAMENTOS 365
4. Isso é derivado da classe genérica Pilha, na qual o parâmetro de classe formal C estava fadado
a ser o parâmetro de classe efetivo Aparelho neste exemplo.
Cap. 14 COESÃO DE CLASSE, SUPORTE DE ESTADOS E DE COMPORTAMENTOS 367
rações deverão ter uma boa coesão (da mesma forma que os módulos de pro-
cedimento individuais em SD).5
Em SD, a coesão realça o propósito do desenhista em criar um módulo
particular — quer ele tenha visto uma forte razão baseada na aplicação para
o módulo, quer ele tenha simplesmente apinhado algumas linhas aleatórias de
código em um procedimento. Por exemplo, determinarPontoParaNovoPedidoE-
mInventário muito provavelmente tem uma alta coesão, ao passo que façaAlgo-
Miscelânea provavelmente tem uma baixa coesão.
Uma pobre coesão de operações resulta da combinação mal orientada de
operações que deveriam ter sido mantidas separadas. As duas versões desse
tipo de combinação mal gerada provocam, respectivamente, coesão alternada
e coesão múltipla, ambas descritas a seguir. Eu então concluo esta seção com
uma observação mais positiva, descrevendo coesão funcional, ou ideal: aquela
que é atingida ao se manterem operações distintas separadas de maneira ha-
bilidosa.
1. Coesão alternada
A coesão alternada aparece quando um desenhista combina diversas eta-
pas de comportamento em uma única operação, que, no recebimento de
uma mensagem, aplica somente uma etapa de comportamento do objeto.6
Em outras palavras, alguém enviando uma mensagem para invocar a
operação deverá prover um sinalizador (ou um switch) que informe à ope-
ração qual etapa de comportamento deverá ser executada desta vez.
Por exemplo, Retângulo poderia ter uma operação
14.4 Resumo
Este capítulo abordou a qualidade da interface externa de uma classe em ter-
mos de quão bem as suas operações suportam o espaço-estado, o comporta-
mento e a coesão dessa classe.
A interface de uma classe pode suportar o espaço-estado desta de acordo
com quatro modos: estados ilegais, em que a interface permite a um objeto
atingir estados que são ilegais para sua classe (ou seja, estados que violam a
invariante de classe); estados incompletos, em que a interface não permite a
um objeto atingir alguns estados que são válidos para sua classe; estados ina-
propriados, em que a interface manifesta que alguns estados não são adequa-
dos para a abstração da classe; e estados ideais, em que a interface permite a
um objeto atingir todos os estados que são válidos para sua classe, e somente
os estados que são válidos para sua classe. Todo desenhista de classes deve vi-
sar uma interface de classe que suporte estados ideais.
A interface de uma classe pode suportar o comportamento desta sob sete
modos: comportamento ilegal, em que a interface permite a um objeto execu-
tar transições de estado que são ilegais para sua classe; comportamento peri-
goso, em que a interface de uma classe pede que um objeto execute algumas
transições de estado via diversas mensagens que conduzem o objeto por esta-
dos intermediários (porém ilegais); comportamento irrelevante, em que a inter-
face de uma classe suporta comportamento extrínseco à classe;
comportamento incompleto, em que a interface não permite a um objeto exe-
cutar algumas transições de estado que são válidas para sua classe; compor-
tamento inábil, em que a interface pede que um objeto execute algumas
transições de estado via diversas mensagens que conduzem o objeto por meio
de estados intermediários (porém válidos); comportamento replicado, em que
a interface suporta o mesmo comportamento segundo diversos modos; e com-
portamento ideal, em que a interface permite a um objeto executar, segundo
um único modo, transições de estado que são válidas para sua classe. Todo de-
senhista de classes deve visar uma interface de classe que suporte comporta-
mento ideal.
Uma operação particular tem três coesões possíveis: coesão alternada, em
que um desenhista combina em uma operação diversas etapas de comporta-
mento a serem executadas alternativamente, dependendo do valor de um si-
nalizador; coesão múltipla, em que um desenhista combina em uma operação
diversas etapas de comportamento a serem executadas em conjunto; e coesão
funcional (ou ideal), em que um desenhista cria uma operação dedicada a pôr
em prática uma única etapa de comportamento. A força e a clareza do nome
372 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
14.5 Exercícios
1. Na seção 14.2, na qual tratei de interfaces com comportamento replicado,
sugeri que qualquer desenhista vez por outra poderia deliberadamente
introduzir comportamento replicado em uma interface de classe, acres-
centando operações especializadas para utilidade dos usuários. Como
você poderia utilizar anéis de operações, os quais vimos no Capítulo 13,
para essa finalidade? De que forma a idéia de sobreposição, discutida na
seção 1.8, também poderia auxiliar?
2. Muitos princípios de desenho estruturado ainda se aplicam a desenho
orientado a objeto. Escolha um princípio de desenho estruturado e, de
maneira concisa, explique como ele poderia ser conformado ao desenho
orientado a objeto.
3. Suponha uma aplicação referente à expedição na qual os usuários reme-
tem engradados de pacotes aos clientes. A Figura 14.1 mostra a estrutura
de agregação de um objeto da classe UnidadeDeRemessa. A estrutura com-
preende um conjunto de pacotes (o conteúdo propriamente dito da remes-
sa) e um engradado-recipiente contendo esses pacotes.
Cap. 14 COESÃO DE CLASSE, SUPORTE DE ESTADOS E DE COMPORTAMENTOS 373
Desenho D (veja a Figura 14.6): O desenhista criou uma classe Dono, que,
ao contrário de PessoaPossuindoCachorro no desenho C, adquire suas pro-
priedades não por meio de herança mas por referir-se a dois objetos, um
da classe Pessoa e outro da classe DonoDeCachorro. (Note que a última é
uma classe concreta, em contraste a seu complemento no desenho C.)
Esse desenho assume a função de “dividir” uma única coisa do mundo
real (uma pessoa possuindo um cachorro) em vários aspectos, função essa
376 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
14.5 Respostas
1. Um desenhista poderia posicionar as operações mais especializadas no
anel externo e as operações mais gerais no anel interno. As operações ex-
ternas, em seguida, seriam implementadas por meio de mensagens para
que utilizassem as operações internas.
A sobreposição capacita operações diferentes na mesma interface a te-
rem o mesmo nome. Isso pode ser útil para a implementação de compor-
tamento replicado nos casos em que uma operação geral na interface tem
Cap. 14 COESÃO DE CLASSE, SUPORTE DE ESTADOS E DE COMPORTAMENTOS 377
CachorrosPossuídosPorFred := fred.númeroDeCachorrosPossuídos
Entretanto, a maior desvantagem desse desenho reside no fato de a
classe Pessoa apresentar grau de dependência com a classe Cachorro.
8. Incidentalmente, esse par de desenhos ilustra a diferença entre um atributo e uma variável.
Ambos os desenhos implementam o atributo peso. Entretanto, o primeiro desenho não tem
qualquer variável correspondente a peso, e o valor do peso é calculado por sua operação obter
“às carreiras”. O segundo desenho tem uma variável explícita correspondente a peso, denomi-
nada pesoTotal nesse exemplo.
Cap. 14 COESÃO DE CLASSE, SUPORTE DE ESTADOS E DE COMPORTAMENTOS 379
(Como vimos na seção 9.3.3, isso gera coesão de papel misto em Pessoa.)
Em termos práticos, significa que Pessoa necessita de muitas “operações
envolvendo cachorros” em sua interface para manipular, por exemplo, a
aquisição de cachorros e cachorros perdidos. Suponha que você queira co-
locar Pessoa em nossa biblioteca de classes e, mais tarde, a reutilize em
uma aplicação pessoal. Os reutilizadores de Pessoa ficariam muito surpre-
sos ao encontrar todas as referências de Cachorro em Pessoa. Na verdade,
poderia até mesmo existir um problema ao tomar-se a classe Pessoa para
compilar ou vincular em uma aplicação que nem disponha de Cachorro!
Eu, portanto, recomendaria o desenho A somente em situações em que
você não teria qualquer intenção de reutilizar a classe Pessoa em outras
aplicações.
Desenho B: O desenho B soluciona o problema de coesão de papel mis-
to de Pessoa existente no desenho A, criando uma classe (PosseEntrePes-
soaECachorro) para vincular uma pessoa e os cachorros dessa pessoa.
Cada objeto dessa classe vincula um objeto Pessoa com um objeto da clas-
se Conjunto<Cachorro>. Está perfeito para PosseEntrePessoaECachorro ser
sobrecarregada com Pessoa e Cachorro, visto que a “condição de pessoa” e
a “condição de cachorro” são intrínsecas à noção da posse de cachorros.
De maneira idêntica, e falando-se intuitivamente, númeroDeCachorrosPos-
suídos é melhor situado como um atributo do relacionamento entre pes-
soas e cachorros do que como um atributo de pessoa.
Entretanto, sempre que mostro o desenho B para qualquer programa-
dor orientado a objeto típico, a reação dele é: “Este desenho é esquisito!”
A razão para essa reação é que, para se encontrar o número requerido de
cachorros, nós não mais simplesmente invocamos uma operação de ins-
tância sobre um objeto escrevendo fred.númeroDeCachorrosPossuídos. Em
seu lugar, devemos invocar uma operação de classe sobre uma classe, es-
crevendo PosseEntrePessoaECachorro.Núm.DeCachorrosPossuídos (fred). O
método para essa operação perscruta minuciosamente uma tabela (uma
variável de classe dentro de PosseEntrePessoaECachorro, que detém iden-
tificadores para todos os objetos da classe) para encontrar o objeto refe-
renciado por fred e o número de cachorros no conjunto associado. Para
programadores acostumados somente com operações de instância, essa
abordagem pode parecer artificial.
Outra objeção origina-se da palavra perscruta no parágrafo anterior.
Qualquer programador poderia exclamar um tanto aflito: “Você quer di-
zer que o método terá de examinar cuidadosamente uma tabela inteira?
380 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
Puxa, isso é contraproducente!” Essa objeção talvez seja válida; ela de-
pende de quão habilmente você desenhou as partes intrínsecas de Pos-
seEntrePessoaECachorro. (Note que você talvez queira tentar desenhar
essa classe para minimizar esses problemas de eficiência.)
O desenho B promove reutilização ao remover os graus de dependência
desnecessários da classe Pessoa. Entretanto, poderá sofrer em termos de efi-
ciência. Também poderá sofrer de uma sintaxe de mensagem embaraçosa
(ao menos algumas pessoas consideram assim): a sintaxe de mensagens de
classes, preferentemente à sintaxe de mensagens de instâncias.
(Um exercício resultante: a classe PosseEntre PessoaECachorro poderia
ser generalizada de forma a suportar relacionamentos binários, inclusive
relacionamentos de “muitos-para-muitos”, em lugar dos relacionamentos
entre pessoas e cachorros? Em caso afirmativo, o conceito de generalidade
— isto é, de classes parametrizadas — comprovaria sua utilidade?)
DesenhoC: Este desenho, que utiliza a classe de associação DonoDeCa-
chorro para implementar a maquinaria para a posse de cachorros, apre-
senta o mérito de ser flexível. Por exemplo, poderíamos facilmente criar
a classe EmpresaDePosseDeCachorros, com essa classe sendo criada via he-
rança a partir de DonoDeCachorro e Empresa. Esse desenho também nos
habilita a enviar uma mensagem diretamente para fred (agora, da classe
PessoaPossuindoCachorro) para descobrir quantos cachorros possui essa
pessoa. (Observe que o objeto fred executará a operação obter númeroDe-
CachorrosPossuídos via herança de DonoDeCachorro). Outra vantagem
desse desenho é que, uma vez que nem Pessoa nem Cachorro tem coesão
de papel misto, muito provavelmente as classes serão de fácil reutilização.
Mas o que aconteceria se Fred comprasse um barco, um carro e um
gato? Para lidar com a posse de cachorros, gatos, barcos e carros segundo
esse desenho, talvez tivéssemos de criar até 15 classes! (Exemplos in-
cluem PessoaPossuindoCachorroEGato e PessoaPossuindoCachorroECarroE-
Barco.)9
9. O problema básico aqui, em minha opinião, é que todas as linguagens orientadas a objeto de
tendência dominante atuais contêm um defeito fundamental: elas não suportam a habilidade
de um objeto de adquirir ou perder associação de classe ou de deter diversas associações de
classe (salvo o que é dado a entender pela hierarquia de classe) de cada vez. Uma abordagem
de desenho capaz de prover essas habilidades é um work-around para um defeito de lingua-
gem orientada a objeto. Espero que as linguagens orientadas a objeto de tendência dominante
brevemente possuam recursos de migração de classes, como algumas linguagens de banco de
dados orientadas a objeto já possuem. (A linguagem Iris apresenta esse recurso.) Entretanto,
admito que a migração geral de classes não é um tema fácil. Para acompanhar esse tópico de
pesquisa, veja, por exemplo, Bertino e Martino, 1993, e Zdonik, 1990.
Cap. 14 COESÃO DE CLASSE, SUPORTE DE ESTADOS E DE COMPORTAMENTOS 381
10. Nós igualmente encontramos esse problema de migração no caso de uma espécie animal mo-
vendo-se para dentro e para fora do risco de ser extinta. (Veja exercício 1 do Capítulo 12.)
382 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
11. Dividir um objeto em aspectos (como no desenho D) é uma excelente abordagem de desenho
para tratar de “subtipos migrantes”. Por exemplo, considere PedidoDeCliente uma instância
que migra de PedidoDeTentativa para PedidoAprovado, para PedidoEfetivado e assim por
diante. A classe composta PedidoDeCliente poderia se referir a quaisquer classe ou a todas
as classes componentes PedidoDeTentativa, PedidoAprovado e assim por diante. Veja Odell
e Martin, 1995, para mais detalhes sobre esse exemplo.
Cap. 14 COESÃO DE CLASSE, SUPORTE DE ESTADOS E DE COMPORTAMENTOS 383
384
Cap. 15 DESENHANDO UM COMPONENTE DE SOFTWARE 385
2. Note que algumas tecnologias de componentes não insistem sobre essa propriedade. Algumas
permitem gerações de componentes; cada geração com seu próprio estado persistente.
Cap. 15 DESENHANDO UM COMPONENTE DE SOFTWARE 387
«interface»
ServiçosDeTipoDeRecurso
obterNúmeroTotal(): NúmeroInteiro
obterNúmerosDeInstâncias (id: IdDoTipoDeRecurso), NúmeroInteiro
obterNome (id: IDDoTipoDeRecurso, nome: String): out nome: String): Booleano
especificarNome (id: IDDoTipoDeRecurso, nome String): Booleano
éPresente (nome: String): Booleano
obterID (nome: String, out id: IDDDoTipoDeRecurso): Booleano
obterÉHumano (id: IDDoTipoDeRecurso): Booleano
especificarÉHumano (id: IDDoTipoDeRecurso, éHumano): Booleano
acrescentar (nome: String, out id: IDDoTipoDeRecurso): Booleano
remover (id: IDDoTipoDeRecurso): Booleanmo
...
A razão pela qual cada tipo de recurso tenha um ID (como 1), bem como
um nome (como Funcionário) é para facilitar a alteração do nome do tipo de re-
curso. As duas operações obterID e obterNome permitem traslados entre ID e
nome. Se a operação obterÉHumano retorna true, então as instâncias desse tipo
de recurso podem se comunicar e tomar decisões (por favor, sem qualquer ci-
nismo dilbertiano).
A operação acrescentar permite que novos tipos de recursos sejam acres-
centados e inicializados. Ela não faz coisa alguma e retorna um valor false se,
por exemplo, o tipo de recurso já existir. Criar o componente GerenciadorDeRe-
cursos para sua primeira utilização implica invocar repetidamente acrescentar
até que todos os tipos de recursos requeridos estejam presentes.4
A operação remover permite que um tipo de recurso desnecessário seja
eliminado. Ela não faz nada e retorna um valor false se ainda existir a pre-
sença de instâncias desse tipo de recurso. (Para evitar esse problema, você po-
deria primeiramente invocar removerTodasDeUmTipo, que descrevo a seguir.)
4. Repare que exibo simplesmente um argumento de entrada de dados para acrescentar, a saber
nome: String. Na prática, são necessários outros argumentos, tais como éHumano: Booleano.
392 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
«interface»
ServiçosDeInstânciaDeRecurso
obterNúmeroTotal(): NúmeroInteiro
obterNome (id: IDDaInstânciaDeRecurso), out nome: String:) Booleano
especificarNome (id: IDDaInstânciaDeRecurso, nome: String:) Booleano
obterTipo (id: IDDaInstânciaDeRecurso, out tipo: IDDoTipoDeRecurso): Booleano
especificarTipo (id: IDDaInstânciaDeRecurso, tipo: (IDDoTipoDeRecurso): Booleano
obterID (nome: String, out id: IDDaInstânciaDeRecurso): Booleano
obterEndereçoDeE-Mail (id: IDDaInstânciaDeRecurso): String
especificarEndereçoDeE-Mail (id: IDDaInstânciaDeRecurso, endereçoDeE-Mail:
String)
obterÉDisponível
(id: IDDaInstânciaDeRecurso, horário: Horário/Data): Disponibilidade
especificarÉDisponível
(id: IDDaInstânciaDeRecurso, horário: Horário/Data, disponível: Disponibilidade)
acrescentar (...): booleano
remover (id: IDDaInstânciaDeRecurso): Booleano
removerTodas ()
removerTodasDeUmTipo (id: IDDoTipoDeRecurso)
...
«interface»
ServiçosDeAgrupamentoDeRecursos
obterNúmeroTotal(): NúmeroInteiro
obterNome (idDoGrupo: IDDoGrupoDeRecursos, out nome: String): Booleano
especificarNome (idDoGrupo: IDDoGrupoDeRecursos, nome: String): Booleano
obter ID (nome: String, out id: IDDoGrupoDeRecursos): Booleano
obterStatusDeProgramação (idDoGrupo: IDDoGrupoDeRecursos): StatusDoGrupo-
DeRecursos
obterHorário (idDoGrupo: IDDoGrupoDeRecursos, horário: Horário/Data): Booleano
criar: IDDoGrupo: IDDoGrupoDeRecursos)
acrescentarRecurso
(idDoGrupo: IDDoGrupoDeRecursos, idDaInstância: IDDaInstânciaDeRecurso,
obrigatória: Booleano): Booleano
liberarRecurso
(idDoGrupo: IDDoGrupoDeRecursos, idDaInstância: IDDaInstânciaDeRecurso):
Booleano
obterHoráriosPossíveis
(idDoGrupo: IDDoGrupoDeRecursoss, início, fim: Horário/Data): RelaçãoDe-
Horários
programar
(idDoGrupo: IDDoGrupoDeRecursos, horário: Horário/Data): StatusDoGrupoDe-
Recursos
nãoprogramar (idDoGrupo: IDDoGrupoDeRecursos)
...
«interface»
ServiçosDeCalendário
obterMenorIncrementoDeTempo(): NúmeroInteiro
especificarMenorIncrementoDeTempo (incrementoDeTempo: NúmeroInteiro)
especificarDisponibilidadeDeDefault (data: Data, status: Disponibilidade)
...
mas, para ser breve, vamos nos mover adiante para a interface final do com-
ponente GerenciadorDeRecursos.
A interface ServiçosPadrões (veja a Figura 15.6) proporciona as operações
que todos os componentes devem ter por razões técnicas (mais do que por razões
de negócio). Cada tecnologia de componente possui um conjunto diferente dessas
operações; os próximos parágrafos descrevem um desses conjuntos típicos.
«interface»
ServiçosPadrões
obterContagemDeReferências(): NúmeroInteiro
obterIdentificador
(cliente: Cliente, nome: NomeDeInterface,
out identificador: IdentificadorDeInterface): Booleano
desconectar (cliente: Cliente, identificador: IdentificadorDeInterface): Booleano
...
5. Ou, conforme componentes mais filosóficos propuseram, “Me utuntur, ergo exto”. (Estou sendo
utilizado, portanto existo.)
Cap. 15 DESENHANDO UM COMPONENTE DE SOFTWARE 397
Caso eu não fosse uma pessoa piedosa, e tivesse de lhe apresentar, mais
à frente, detalhes solenes de como as operações da interface de GerenciadorDe-
Recursos interagem com as classes subjacentes no DomínioDeNegócio, confor-
me mostrado na Figura 15.7, examinaria o seguinte:
8. Para ser preciso, de forma até pedante, são os métodos que implementam operações nas in-
terfaces os quais interagem com as operações de classe de TipoDeRecurso e operações de ins-
tância dos objetos da classe TipoDeRecurso.
402 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
9. Como você talvez recorde da seção 1.5.4, uma mensagem que invoca uma operação para in-
formar a um objeto — ou, aqui, uma interface — que algo ocorreu é denominada mensagem
informativa.
404 FUNDAMENTOS DO DESENHO ORIENTADO A OBJETO COM UML
nho é que ele não exige uma operação de notificação de eventos na in-
terface do signatário. A desvantagem é que o objeto signatário perde
um bocado de tempo verificando eventos (se ele contar os registros
freqüentemente) ou ele poderá esperar um tempo longo para saber so-
bre uma ocorrência de evento (se ele contar os registros raras vezes).
Há duas abordagens para se desenhar o requisito 5: