Você está na página 1de 586

Página em branco

UML e C++
Guia Prático de Desenvolvimento
Orientado a Objeto

Richard C. Lee
William M. Tepfenhart

Tradução:
Celso Roberto Paschoa
Revisão Técnica:
José Davi Furlan
Consultor em UML e autor da Makron Books
Do original:
UML and C++ — A Practical Guide to Object-Oriented Development

Copyright 2001, by Richard Lee. Publicado sob acordo com a Prentice Hall, Inc., uma
empresa da Pearson Education.
Copyright © 2002, MAKRON Books Ltda.

Todos os direitos para a língua portuguesa reservados pela MAKRON Books Ltda. Ne-
nhuma parte desta publicação poderá ser reproduzida, guardada pelo sistema “retrie-
val” ou transmitida de qualquer modo ou por qualquer outro meio, seja este eletrônico,
mecânico, de fotocópia, de gravação ou outros, sem prévia autorização, por escrito, da
Editora.

EDITOR: MILTON MIRA DE ASSUMPÇÃO FILHO

Gerente de Produção:
Silas Camargo
Editora Assistente:
Gisélia do Carmo Costa
Produtora Editorial:
Salete del Guerra
Capa:
Marcelo da S. Françozo

Editoração Eletrônica: ERJ Composição Editorial e Artes Gráficas

Dados de Catalogação na Publicação


UML and C++ — A Practical Guide to Object-Oriented Development
Tradução: Celso Robeto Paschoa, Revisão Técnica: José Davi Furlan
São Paulo: MAKRON Books, 2001

ISBN: 85.346.1364-8
SUMÁRIO

1. O Dilema da Administração de Informações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1


O Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
As Organizações Modernas Estão a Caminho de um Desastre . . . . . . . . . . . . . . . . . . . . . . 2
O Que o Cliente Deseja? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Por Que o Método Orientado a Objeto É Importante aos Desenvolvedores . . . . . . . . . . . . . 5
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2. Administrando a Complexidade: Análise e Desenho . . . . . . . . . . . . . . . . . . . . . . . . . 7


Mecanismos de Abstração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Tipos de Dados Abstratos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Classes/Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Passagem de Mensagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Generalização/Especialização e Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Novos Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Associações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Agregação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Comportamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Comportamento Estático . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Comportamento Dinâmico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
REGRAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Sistemas Complexos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3. Programação Orientada a Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23


O Que É Programação Orientada a Objeto? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Não É a Solução Mágica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Paradigma Avançado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Conceitos Básicos da Programação Orientada a Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Linguagens de Programação Orientadas a Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Programação Baseada em Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

v
vi UML E C++

Programação Baseada em Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29


Programação Orientada a Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Programação OO Avançada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Programação Orientada a Objeto de Ponta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Por Que C++? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Modos de Organizar a Realidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Modelo de Simulação da Computação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Modo Orientado a Objeto de Organizar a Realidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4. Delimitando o Domínio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Introdução aos Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Atores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Grupos de Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Documentando Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Diagrama de Caso de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Diagrama de Seqüência: Documentando os Detalhes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Descrição Textual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Diretrizes para o Desenvolvimento de Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Evitar Paralisia de Análise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Identificar Atores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Identificar Casos de Uso Essenciais e de Alto Nível . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Estabelecer Grupos de Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Desenvolver Detalhes de Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Identificar Casos de Uso Compatíveis (de Suporte) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Desenvolver Casos de Uso-Limite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Contratos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

5. Descobrindo os Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Análise Orientada a Objeto: Modelo de um Domínio de Aplicação . . . . . . . . . . . . . . . . . . . 74
Construindo o Modelo OO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Identificação de Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Técnicas Atuais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Utilizar os Itens a Serem Modelados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Utilizar as Definições de Objetos e Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Utilizar Decomposição de Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Utilizar Generalização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Utilizar Subclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Utilizar Análise de Domínio Orientada a Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Reutilizar uma Estrutura de Aplicação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Reutilizar Hierarquias de Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Reutilizar Objetos e Classes Individuais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
SUMÁRIO vii

Utilizar Subconjuntos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Utilizar Experiência Pessoal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Técnicas Tradicionais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Utilizar Nomes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Utilizar Diagramas Tradicionais de Fluxo de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Utilizar Cartões de Colaboração e Responsabilidade de Classe (CRC —
Class-Responsibility-Collaboration) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Abordagens Recomendadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

6. Identificando Responsabilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
O Que É um Objeto? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
O Que É um Atributo? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Atributos Descritivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Atributos de Identificação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
O Que É um Serviço? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
O Que É um Método? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Identificação de Atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Especificando Atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Identificando Serviços . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Especificando Serviços . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

7. Especificando Comportamento Estático . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103


O Que É Comportamento? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Técnicas de Especificação de Comportamento Estático . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Técnicas de Especificação de Controle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Técnicas de Documentação de Controle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Diagramas de Atividade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Diagrama de Colaboração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Diagrama de Seqüência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Técnicas de Documentação de Comportamento Estático . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Pré e Pós-condições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Confecção de Diagramas de Fluxo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Diagramas de Fluxo de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Linguagem Estruturada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Enfoque Recomendado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

8. Comportamento Dinâmico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119


Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Técnicas de Identificação de Comportamento Dinâmico . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Formas Comuns de Ciclo de Vida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
viii UML E C++

Modelos de Captura de Ciclo de Vida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123


Identificando e Especificando Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Caso de Uso e Cenário . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Diagrama de Seqüência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Especificando o Comportamento Dinâmico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Lista de Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Tabela de Transição de Estado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
Documentando o Comportamento Dinâmico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Diagramas de Estado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Enfoque Recomendado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

9. Identificando Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145


Acessando Serviços de Outro Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Generalização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Identificando e Especificando Generalização/Especialização . . . . . . . . . . . . . . . . . . . . . . . . 149
Agregação de Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Classificação da Agregação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Conjunto-Partes (Composição Componente-Todo) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Composição Material-Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
Composição Porção-Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
Composição Local-Área . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Composição Conjunto-Membros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Contêiner-Conteúdo (Composição Membro-Grupo) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Composição Membro-Parceria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Objetos e Relacionamentos de Agregação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Vínculos entre Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Identificando e Especificando Vínculos e Agregações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Administrando Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
Documentando Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Enfoque Recomendado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

10. Regras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167


Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
Regras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
Identificando Asserções Declarativas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
Especificando e Documentando Regras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
Mapeando (Traduzindo) Regras para o Conceito OO Apropriado . . . . . . . . . . . . . . . . . . . 172
Documentando as Regras pela Utilização da UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Implementando Regras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
SUMÁRIO ix

11. O Modelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177


Conceitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
Conceitos e Modelo Orientado a Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Associação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Agregação de Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Generalização/Especialização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
Instanciamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Documentando Conceitos pelo Uso da UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Conceito de Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Associação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
Agregação de Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
Generalização/Especialização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
Instanciamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
Refinando o Modelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Subsistemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Domínio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Ponte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Organizando Subsistemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
Camadas Horizontais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
Partições Verticais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
Combinação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Identificando Subsistemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Documentando Subsistemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
Refinamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Subsistemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197

12. DESENHO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199


Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Desenho do Sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Subsistemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Estruturas Arquiteturais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
Documentando Desenho do Sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
Desenho Detalhado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Desenho de Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
Desenho de Associações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Generalização e Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Delegação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
Tratado de Orlando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Herança Múltipla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
Documentando Desenho Detalhado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
x UML E C++

13. Fundamentos de C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215


História . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Elementos de Programação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
Palavras-Chave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Identificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Literais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Pontuadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Tipos de Dados Nativos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Tipos de Dados Básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Valores Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Variáveis Simbólicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Tipos Ponteiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Tipos Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Tipos de Referência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Tipos de Enumeração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Tipos de Matriz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Nomes Typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
O Que É uma Declaração? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Expressões . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Declarações Compostas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Controle de Fluxo de Declarações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Instrução If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Instrução For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
O Que É uma Função? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Invocação de Funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Definição da Função . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Protótipo da Função . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Inlining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Classe de Armazenamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Auto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Extern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Register . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Static . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
Volatile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
Conversão de Tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
static_cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
const_cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
dynamic_cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
reinterpret_cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
Namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
Enfoque Recomendado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235

14. Implementando Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237


Componentes de uma Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Definição de Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
SUMÁRIO xi

Corpo da Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238


Visibilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Membros de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Funções Membros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
Generalização Utilizando Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
Exemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244

15. Bibliotecas C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245


Bibliotecas C Padrão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
<cassert> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
<cctype> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
<cerrno> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
<cfloat> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
<ciso646> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
<climits> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
<clocale> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
<cmath> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
<csetjmp> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
<csignal> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
<cstdarg> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
<cstddef> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
<cstdio> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
<cstdlib> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
<cstring> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
<ctime> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
<cwchar> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
<cwctype> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Bibliotecas de Classes C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
<bits> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
<bitstream> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
<complex> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
<defines> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
<dynarray> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
<exceptions> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
<fstream> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
<iomanip> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
<ios> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
<iostream> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
<istream> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
<new> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
<ostream> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
<ptrdynarry> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
<sstream> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
<streambuf> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
<string> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
<strstream> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
xii UML E C++

<typeinfo> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
<wstring> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Biblioteca Standard Template Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
<algorithm> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
<bitset> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
<complex> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
<deque> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
<functional> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
<iterator> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
<list> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
<map> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
<memory> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
<numerics> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
<queue> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
<set> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
<stack> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
<utility> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
<valarray> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
<vector> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263

16. Implementando Comportamento Estático . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265


Definição de Função . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
Tipo de Retorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
Comando Return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
Lista de Argumentos de uma Função . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
Passagem de Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Passagem por Valor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Argumento como Ponteiro ou Referência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Tipo de Retorno como Referência ou Ponteiro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
Casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Const e Defaults . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Inicializadores Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Escopo de Identificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Definição no Arquivo .h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Definição no Arquivo .C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280

17. Implementando Comportamento Dinâmico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283


Elementos do Comportamento Dinâmico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
Diagramas de Estado Simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
Diagramas de Estados Aninhados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Diagramas de Estados Concorrentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
SUMÁRIO xiii

Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296


RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297

18. Instanciando e Eliminando Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299


Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
Construtores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
Destrutores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
A Utilização Adequada de Construtores e Destrutores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
Generalização e Construtores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
Criar um Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
Destruir um Objeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
Diretrizes de Codificação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310

19. Implementando Generalização/Especialização . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313


Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
Especificando uma Classe Derivada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
Herança de uma Classe Derivada e Implementando Associação . . . . . . . . . . . . . . . . . . 316
Inserindo Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Classe Abstrata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Herança Múltipla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
Destrutores Virtuais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
Visibilidade da Classe Derivada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329

20. Implementando Mais Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331


Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
Implementando Associações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
Implementando Atributos de uma Associação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
Implementando Agregação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
Ponteiros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
Matrizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
Friends (Amigos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
Membros Estáticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
Implementando Associação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
Associação Binária . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
Associação do Tipo Muitos-para-Um . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
Associação do Tipo Muitos-para-Muitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
Implementando Friends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
Classe como Friend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
Função como Friend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
Implementando uma Associação Um-para-Muitos Utilizando Método Friend . . . . . . 344
Implementando Agregação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
Ponteiros Ocultos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
Objetos Incorporados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
Implementando Membros Estáticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
xiv UML E C++

Abordagem Recomendada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350


RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350

21. Introdução aos Estudos de Caso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351


Estudo de Caso No 1: Breakout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
Requisitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
Adquirindo Conhecimento do Domínio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
Know-How dos Especialista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Serviços Tecnológicos Prestados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
Estudo de Caso No 2: Forno de Microondas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
Definição do Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
Descrição Geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358

22. Estudo de Caso: o Jogo Breakout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361


Etapa 1: Encontrando os Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
Etapa 2: Identificando Responsabilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Análise de Adjetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Respondendo à Pergunta No 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Respondendo à Pergunta No 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
Respondendo à Pergunta No 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
Análise dos Serviços . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
Etapa 3: Especificando Comportamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
Etapa 4: Especificando Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
Etapa 5: Refinamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
Etapa 6: Desenho . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
Etapa 7: Implementação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
Implementando Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
Implementando Comportamento Estático . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
Instanciando Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
Implementando Herança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
Implementando Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459

23. Estudo de Caso: Forno de Microondas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469


Casos de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
Caso de Uso No 1: Preparo de Alimentos sem Interrupção . . . . . . . . . . . . . . . . . . . . . . . 470
Caso de Uso No 2: Cancelar Preparo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
Caso de Uso No 3: Interromper Preparo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
Solução No 1: O Desenho com a Classe Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
Etapa 1: Descobrindo os Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
Etapa 2: Identificando Responsabilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
Etapa 3: Especificando Comportamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Etapa 4: Especificando Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
Etapa 5: Refinamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
Discussão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
Solução No 2: Responsabilidade Distribuída com Alto Acoplamento . . . . . . . . . . . . . . . . 483
Etapa 1: Identificando os Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
Etapa 2: Identificando Responsabilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
SUMÁRIO xv

Etapa 3: Especificando Comportamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485


Etapa 4: Especificando Relacionamentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
Etapa 5: Refinamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
Discussão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
Solução No 3: Responsabilidade Distribuída Utilizando o Mecanismo do Observador . 490
Etapa 5: Refinamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
Discussão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503

APÊNDICE A — Linguagem de Modelagem Unificada (UML) . . . . . . . . . . . . . . . . . . . . . 505


Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
O Que É a UML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
O Que Não É a UML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Quais São os Objetivos da UML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Por Que Utilizar a UML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Quais São os Diagramas da UML? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506
Quais São os Diagramas Mais Importantes da UML? . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
Diagramas da UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
Diagrama de Caso de Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
Diagrama de Classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510
Diagrama de Seqüência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
Diagrama de Colaboração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
Diagrama de Estado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
Diagrama de Atividade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
Diagrama de Componente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
Diagrama de Implantação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
GLOSSÁRIO DA UML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530

Bibliografia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535

Referências . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537

Índice Analítico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541


Página em branco
PREFÁCIO DA SEGUNDA EDIÇÃO
Prefácio da Segunda Edição

Prefácio da Segunda Edição

A xiomas na Filosofia não são considerados como tais até que sejam cons-
tatados pelos nossos próprios meios: lemos coisas agradáveis, mas nunca
as sentimos totalmente até passarmos pelas mesmas fases que o autor.

John Keats (1795 — 1821), Carta para


J. H. Reynolds, 3 de maio de 1818

Este livro continua sendo dirigido a atarefados analistas e desenvolvedores profissionais


de software que trabalham com sistemas de grande porte, especialmente àqueles que pre-
cisam integrar seus novos sistemas a sistemas legados. Caso você ainda não tenha tido
tempo de freqüentar algum curso e nem incrementado seus conhecimentos sobre tecno-
logia orientada a objeto (OO) utilizando a Linguagem de Modelagem Unificada (Unified
Modeling Language, UML) e C++, este livro é um guia de auto-aprendizado dirigido a
preencher suas necessidades. Ele contribuirá para que você entenda as diferenças entre
análise, desenho e programação OO. Nossos objetivos na primeira edição foram:
■ Ensiná-lo a construir uma aplicação orientada a objeto utilizando C++ e tomar as
decisões balanceadas e corretas para atender às suas necessidades de negócio.
■ Esclarecer os conceitos básicos associados à tecnologia orientada a objeto.
■ Prover suficiente profundidade quanto à abrangência para que alunos e profissio-
nais ingressem no campo e fiquem aptos a desenvolverem-se na matéria.
■ Expor alguns dos mitos que cercam a tecnologia OO enquanto sua praticidade é fo-
cada como uma ferramenta de engenharia de software.
■ Dotá-lo de uma “receita”, ou de um guia do tipo passo-a-passo, para que você possa
cumprir todas as etapas da tecnologia orientada a objeto.

xvii
xviii UML E C++

■ Defender a teoria de que conceitos baseados em regras, lógica obscura, multimídia


e modelagem de dados OO, integrados em um modelo único, podem tratar dos de-
safios de negócios atuais e futuros para as organizações voltadas à tecnologia da in-
formação.
■ Prover uma abordagem prática para a análise, desenho e programação na tecnolo-
gia OO.
■ Mostrar como implementar tecnologia OO utilizando C++ (que, embora não seja
uma linguagem orientada a objeto, é uma linguagem de multiparadigmas extrema-
mente poderosa).
■ Contrabalançar um pouco de teoria e prática com exercícios de aplicação extraídos
da literatura existente.
Nesta segunda edição, estendemos aqueles objetivos iniciais incluindo:
■ Proporcionar uma abordagem prática para o desenvolvimento de casos de uso
como parte da análise OO.
■ Prover uma maior cobertura dos diagramas da UML.
■ Introduzir bibliotecas fundamentais C++ que provêem importante funcionalidade
para suporte da implementação de um modelo OO em C++.
Esta segunda edição também aperfeiçoou os seguintes tópicos:
■ Modelagem de comportamento dinâmico.
■ Implementação do modelo de estado.
■ Projetos de classes.
Da mesma forma, conforme citado anteriormente, não é preciso ter conhecimento
sobre ciência da computação ou matemática avançada para entender os importantes con-
ceitos e tópicos orientados a objeto em profundidade. Até mesmo os capítulos de progra-
mação não requerem experiência em C++; eles ilustram como o trabalho de codificação
pode ser desempenhado nesta linguagem.

Tecnologia Orientada a Objeto


Somos desenvolvedores de software de sistemas de grande porte. Acreditamos que a tec-
nologia OO é a (r)evolução de software mais importante da década de 90. Ela está mu-
dando a forma como construímos software e o modo como aplicações se intercomunicam
ao longo de redes de alcance mundial e pelos computadores de vários distribuidores.
Mais do que isso, o modelo OO está modificando a forma como desenhamos processos de
negócios e o modo como pensamos sobre uma empresa. Nos dias de hoje, a maioria das em-
presas precisa ser redesenhada para superar os futuros desafios na área de negócios.
O redesenho de processos nas empresas é uma das mais importantes funções de
uma organização voltada à tecnologia da informação. Um modelo que capture os proces-
sos, procedimentos, diretrizes e regras das empresas facilita o desenho. Ferramentas que
traduzam o modelo em um sistema operacional aceleram a implementação do redesenho.
Quando o mercado ou a condição de negócio se alterarem, esses sistemas poderão ser re-
generados para refletir essas transformações ao atualizarem o modelo e utilizarem aque-
las ferramentas. A ciência (engenharia) da informação tem nos levado mais adiante e com
maior velocidade do que qualquer outra ciência nas décadas anteriores. Entretanto, ela
precisa de uma atualização constante e melhorada, métodos mais refinados para resolver
PREFÁCIO DA SEGUNDA EDIÇÃO xix

nossas demandas de negócios e nossos problemas com modelagem e programação OO.


Cada vez mais pessoas acreditam que a tecnologia OO porá um fim na crise do software,
significando que os mecanismos de OO serão para o software o que os parafusos e as vi-
gas representam para os projetos na construção civil e, da mesma maneira, o que o chip re-
presenta para o desenho do hardware de computadores. Essa crença origina-se dos seguintes
pontos:
■ A proficiência de um modelo OO de nível mais alto proverá ao desenhista de soft-
ware componentes programáveis, do mundo real, reduzindo com isso os custos re-
lativos a desenvolvimento de software.
■ Sua capacidade em compartilhar e reutilizar código com técnicas OO reduzirá o
tempo para desenvolvimento de uma aplicação.
■ Sua habilidade de localizar e minimizar os efeitos de modificações por meio de pro-
gramação de mecanismos de abstração capacitará um desenvolvimento mais acen-
tuado e rápido, e irá prover software mais confiável e robusto.
■ Sua capacidade de administrar a complexidade possibilitará a desenvolvedores tra-
tarem de aplicações mais difíceis.
A coleção de conceitos OO é um conjunto de ferramentas para modelagem da rea-
lidade. Esse conjunto de ferramentas OO dota os desenvolvedores com os melhores recursos
para gerenciamento da complexidade. Certos conceitos OO auxiliam os desenvolvedores no
tocante a produzir software flexível e de mais fácil manutenção.

Por Que a UML?


Como profissionais da tecnologia OO, sabemos que todos os métodos, se praticados adequada-
mente, resultam no mesmo ou em modelo similar. Durante muitos anos, a grande quan-
tidade de diferentes notações para linguagens de modelagem constituiu impedimentos ao
progresso, e adotamos a UML antes de ser amplamente aceita ou suportada por ferra-
mentas de modelagem. A ampla aceitação da UML tem eliminado a maior parte desses
problemas — diferentes notações para linguagens de modelagem e escassez de ferramen-
tas de modelagem.
A base para a razão do sucesso da UML é que ela nos proporciona todos os ícones
de desenho necessários para capturar a maioria dos conceitos ou mecanismos que consi-
derarmos valiosos para a resolução dos problemas reais de negócios. E mais, ela provê
todos os diagramas de que precisamos, diagramas esses vitais para a documentação de
nossos modelos. E, para finalizar, ela é uma linguagem “viva” que oferece a capacidade
de estender a notação para mecanismos ainda não definidos pelo distinto grupo formado
por Grady Booch, James Rumbaugh e Ivor Jacobson, da Rational Software Corporation.

Por Que a Linguagem C++?


Trata-se de uma idéia equivocada que C++ seja, exclusivamente, uma linguagem de pro-
gramação OO. C++ é uma linguagem de multiparadigmas que suporta diversos paradig-
mas de programação, inclusive o paradigma procedural, o tipo de dado abstrato e o
paradigma OO. Mostraremos a você como mapear (traduzir) seu modelo OO para as
construções C++ utilizando o paradigma OO em C++. Também lhe mostraremos como
utilizar outros conceitos não orientados a objetos da linguagem no contexto do desenho
OO para ajudá-lo a resolver seus problemas de negócios.
xx UML E C++

C++ é nossa linguagem de escolha por duas razões práticas. Primeiro e acima de
tudo, a maioria dos desenvolvedores tem de abordar restrições reais: construir interfaces
para sistemas legados e limitações técnicas sobre bancos de dados, armazenamento e de-
sempenho. C++ dota os desenvolvedores com múltiplos paradigmas, e estes podem ser
ajustados finamente quando necessário. Em segundo lugar, fornecedores de ferramentas
e compiladores investiram todo o seu dinheiro na linguagem C++.

Nosso Enfoque da Tecnologia Orientada a Objeto


Não somos puristas nem teóricos OO. Somos desenvolvedores dispostos a utilizar qual-
quer boa idéia que nos auxilie a atingir duas metas críticas nos negócios: menores custos
de desenvolvimento e redução do tempo de colocação no mercado de versões mais aper-
feiçoadas de software. Acreditamos que estes objetivos técnicos — confiabilidade, man-
teneabilidade e flexibilidade — são críticos para cumprir nossas metas nos negócios.
Nosso enfoque para utilização da tecnologia OO é o de administrar a complexidade
de desenvolver software de modo que ele seja um produto confiável, de fácil manutenção
e flexível. Administrar complexidade é a chave para se obter tais objetivos e, por conse-
guinte, nossas metas nos negócios. Para o gerenciamento de complexidade em domínios
de problemas intrincados, constatamos que os desenvolvedores necessitam saber como os
objetos, classes, relacionamentos e regras se inserem no paradigma orientado a objeto.
Quando modelamos a maioria dos domínios de problemas complexos, descobrimos ob-
jetos, classes e muitos relacionamentos entre objetos. Além disso, necessitamos capturar
as regras (diretrizes) existentes dentro daquele domínio. Portanto, precisamos empregar
técnicas de modelagem estática muito ricas para capturar os relacionamentos entre os da-
dos (objetos).
Muitos especialistas OO consideram os relacionamentos como algo “ruim” porque
violam o princípio de encapsulamento. De nossa perspectiva, eles contribuem para que
administremos a complexidade do domínio do problema e atinjamos nossos objetivos nos
negócios. Assim, os utilizamos com satisfação e buscamos mais mecanismos e suporte de lin-
guagens nesta área. No Capítulo 9 sobre semântica declarativa, apontamos que regras e
diretrizes deverão ser capturadas como parte integral de nosso modelo e não em algumas
extensões especiais do subsistema.
Utilizar mecanismos para auxiliar-nos a modelar domínios de problemas complexos
é consistente com nossas opções pela UML, como nossa linguagem de modelagem, e pela
C++, como nossa linguagem de programação. Ambas, UML e C++, capacitam-nos a de-
finir qualquer mecanismo necessário que nos ajude a criar software mais gerenciável.
Abordamos comportamentos (dinâmicos e estáticos) e polimorfismo para capturar
os aspectos procedurais do modelo. O uso de uma máquina de estado finita ou de algum
outro modelo de estado contribui para que gerenciemos complexidade procedural en-
quanto tratamos de ajustamento de tempo, sincronização e interrupções. Essas idéias ge-
ralmente são ignoradas ou omitidas por uma grande parcela dos livros OO. Não tratamos
desses temas em profundidade, mas lançamos a pedra fundamental de forma que o leitor
possa se utilizar da semântica inserida na UML.
Cremos que o segredo do sucesso ao construir sistemas extensos OO reside no fato
de os desenvolvedores e/ou programadores saberem mais do que é ensinado na maioria
dos livros OO. Construir sistemas extensos requer o uso de mecanismos promovidos por
alguns especialistas nessa teoria, porém, nem sempre aceitos por todos eles. Desenvolve-
dores profissionais precisam pelo menos compreender como esses aspectos do domínio
PREFÁCIO DA SEGUNDA EDIÇÃO xxi

do problema podem ser tratados antes de tornarem-se um integrante de uma equipe pro-
dutiva. Este livro não fará de você um especialista. Ainda serão precisos especialistas
e/ou consultores para desenvolver o sistema. Aplicando-se a regra dos 80/20, este livro
propicia os 80% que podem torná-lo produtivo e ainda entender como os especialistas so-
lucionam as dificuldades contidas nos 20% restantes.
Neste livro não tratamos das tendências ou “modas” mais recentes relativas à tec-
nologia OO, incluindo modelos de desenho e computação distribuída de objetos. Embora
elas sejam interessantes, não estamos convencidos de que possam contribuir significati-
vamente para nosso objetivo de prover uma estrutura prática que capacite os desenvol-
vedores novatos na programação OO a obterem um conhecimento extremamente rápido
sobre essa matéria. Modelos de desenho são técnicas comprovadas que aperfeiçoam boas
práticas OO e não são dirigidos a ser um paradigma em seus próprios termos. Ainda
acreditamos que aprender os fundamentos básicos da tecnologia OO é essencial para a
aplicação correta dos modelos de desenho. Computação distribuída está se tornando a
mais transparente possível para desenvolvedores de aplicações por meio de middleware
bem-escrito. Esperamos que os fornecedores de infra-estrutura eventualmente possam
tornar esse ponto tecnológico transparente ao domínio da aplicação.
E, para finalizar, não concordamos com a afirmação da maioria dos especialistas de
que a tecnologia OO seja uma tecnologia madura. Cremos mais que ela esteja no começo
de sua vida; o que nos impressiona é o grau de realizações que poderemos atingir com
essa tecnologia tão nova. A tecnologia orientada a objeto tem o enorme potencial de au-
xiliar-nos na administração da complexidade, fator esse inexistente nas tecnologias mais
antigas (procedural, funcional, baseada em regras etc.).

De Que Forma este Livro Está Organizado


Conduzimos o leitor para uma viagem pelos caminhos de nosso raciocínio na aplicação
de técnicas e métodos orientados a objeto. Eles não constituem um conjunto de regras ab-
solutas. Nosso objetivo é fazê-lo pensar sobre bons conceitos OO e bons princípios de de-
senho quando do desenvolvimento de software e da programação em C++. No estudo de
caso, elaboramos um projeto passando por todas as etapas da análise, desenho e codifi-
cação OO, utilizando técnicas específicas OO e aplicando conceitos fundamentais OO. O
desenho é implementado em C++ com as técnicas de aperfeiçoamento de desempenho
desta linguagem.
Este livro foi escrito originalmente para ser um guia auto-explicativo que deveria
ser lido na ordem seqüencial; este aspecto foi mantido. Cada capítulo discute uma etapa
importante de nosso enfoque da tecnologia OO. A maioria dos capítulos termina com um
guia passo-a-passo ou “receita”. Esperamos que o leitor utilize essas etapas unicamente
como diretrizes; confiando sempre no bom senso em lugar de seguir cegamente etapas
prescritas previamente.

Capítulo 1 apresenta as razões pelas quais as empresas estão interessadas em OO e o mo-


tivo pelo qual um profissional voltado a desenvolvimento de software deve conhecer a
tecnologia OO.
Capítulo 2 aborda o ramo de software e a necessidade de se administrar a complexidade.
Capítulo 3 descreve como encontrar a terminologia básica e os conceitos principais da
tecnologia orientada a objeto.
xxii UML E C++

Capítulo 4 descreve como empregar casos de uso para delimitar a fronteira a objetos re-
levantes.
Capítulo 5 descreve como descobrir objetos potenciais, utilizando-se como primeira etapa
o emprego da tecnologia orientada a objeto.
Capítulo 6 descreve como diferenciar entre objetos “reais” e “falsos” pela identificação
de atributos (dados) e serviços associados a objeto.
Capítulo 7 demonstra como capturar o comportamento de um objeto.
Capítulo 8 descreve como identificar e descrever comportamento dinâmico.
Capítulo 9 descreve os vários relacionamentos (generalização/especialização, vínculo,
agregação de objetos etc.) disponíveis para organizar todos os objetos de um sistema.
Capítulo 10 descreve como incorporar fatos declarativos no modelo OO sobre conheci-
mento de objetos e um mecanismo baseado em regras para suas implementações.
Capítulo 11 descreve como transformamos objetos em classes para nos beneficiarmos dos
mecanismos de C++.
Capítulo 12 aborda algumas questões de desenho com o desenvolvimento de sistemas
OO.1
Capítulo 13 provê os elementos básicos necessários de C++ para efetivar a programação
OO utilizando C++. (Os leitores que estiverem familiarizados com C++ podem pular este
capítulo.)
Capítulo 14 ensina como implementar uma classe, a qual é um modelo para se criar ob-
jetos.
Capítulo 15 introduz as bibliotecas de C++ para uso nos capítulos subseqüentes.
Capítulo 16 ensina como implementar as especificações de comportamento desenvolvi-
das no Capítulo 6.
Capítulo 17 ensina como implementar o comportamento dinâmico desenvolvido no Ca-
pítulo 8.
Capítulo 18 aborda como criar e destruir objetos utilizando o mecanismo de classe de
C++.
Capítulo 19 aborda como implementar o relacionamento de generalização/especialização
(um dos conceitos mais importantes de OO) pela utilização do mecanismo de derivação
de classes de C++.
Capítulo 20 aborda como implementar outros relacionamentos não suportados por C++.
Capítulo 21 introduz os dois estudos de caso.
Capítulo 22 apresenta um estudo de caso baseado no jogo Breakout.
Capítulo 23 apresenta um estudo de caso para um forno de microondas.
Apêndice A apresenta um resumo da Linguagem de Modelagem Unificada (UML).

Utilização deste Livro para Fins Didáticos


Este livro surgiu de cursos industriais freqüentados por programadores competentes que
desconheciam os tópicos de OO e C++. Este material foi apresentado em dois cursos de
uma semana de duração. O primeiro curso abrangia o material dos primeiros 13 capítu-
los, e o segundo tratava dos demais. No final do segundo curso, os alunos tinham um
programa funcional completo que refletia o desenho obtido no final do primeiro curso.
O estudo de caso no final deste livro refere-se ao projeto implementado pelos alunos do
curso. Este livro continua sendo um recurso didático para esse tipo de ensino.

1. Desenho é um processo muito complexo e constitui um tópico separado da tecnologia orientada a


objeto.
PREFÁCIO DA SEGUNDA EDIÇÃO xxiii

No momento, um dos autores ministra um curso de um semestre que abrange os


primeiros 13 capítulos do livro para estudantes universitários que já conhecem a lingua-
gem C++. Ele continua a utilizar este material em conjunto com um projeto, empregando
muito mais o projeto do que as tarefas de casa para reforçar os conceitos. Utilizar um pro-
jeto significativo possibilita ao aluno aplicar os conceitos à medida que eles são aprendi-
dos. Acompanhar o processo total de análise e desenho OO para um projeto
aparentemente funciona muito mais do que um grande número de problemas não-corre-
lacionados. Um modelo de análise e desenho significativo pode ser finalizado em um se-
mestre. O segundo semestre é necessário para a implementação do modelo.
A seleção de um projeto com escala adequada permite que um aluno domine todos
os conceitos mais importantes. Bill Tepfenhart, o instrutor, destina um jogo extenso de
aventuras como projeto e deixa os estudantes desenvolverem seus próprios temas para a
sua conclusão. Projetos finalizados normalmente consistem de mais de 100 classes e um
número que chega a essa monta de relacionamentos. Os jogos tipicamente incorporam
muitos tipos diferentes de terrenos, armas, monstros, tesouros e personagens. Alguns dos
jogos têm uma complexidade equivalente a muitos produtos disponíveis no mercado.
Uma equipe razoável de projetos consiste de três ou quatro estudantes. (Uma equi-
pe maior tende a gastar muito tempo chegando a acordos sobre o tema do jogo, e uma
equipe menor tende a ficar sobrecarregada.) Esse tamanho de equipe tem a vantagem ex-
tra de que os seus integrantes experimentam um pouco o que significa tomar parte nos
trabalhos de uma equipe de desenvolvimento. Eles precisam trabalhar no projeto sema-
nalmente a fim de concluí-lo dentro de um cronograma preestabelecido.
A seguir apresentamos um programa sugerido para as atividades do curso. É uma
programação padrão de 15 semanas com um exame final que deverá ser feito na última
semana. Uma característica-chave desta programação é que ela permite um tempo gene-
roso de “up front” para se definir o jogo e desenvolver os casos de uso. Aulas práticas fi-
zeram com que essas atividades fossem executadas pelas equipes em prazo inferior a três
semanas. Considerou-se tal procedimento vantajoso porque ajuda a evitar que alunos co-
metam muitos erros, tais como confundir estado de objeto com atributos de objetos.

TABELA A.1 Programação de Aulas para a Análise e Desenho OO


Semana Capítulos Atividades do Projeto

1 1,2 Formação das equipes


2 3 Desenvolvimento da idéia do jogo
3 4 Início da escrita dos casos de uso
4 5 Continuação da escrita dos casos de uso
5 6 Continuação da escrita dos casos de uso
6 7 Identificação de objetos e seus atributos
7 8 Identificação de comportamentos estáticos de objetos
8 9 Identificação de comportamento dinâmico
9 10 Identificação de relacionamentos
10 11 Revisão de modelos
11 12 Desenho
12 12 Desenho
13 Revisão Término do Projeto
14 — Apresentações de classe
xxiv UML E C++

Agradecimentos
Devemos tanto a tantas pessoas. O estímulo para a criação deste livro é proveniente dos
milhares de alunos que participaram do curso oferecido por Richard Lee, seus amigos e
colegas. Ele apreciou a persistência e o encorajamento por parte deles, ignorando os con-
tratempos pessoais.
Pelo fato de sermos desenvolvedores (e não pesquisadores, acadêmicos ou escrito-
res), alavancamos o trabalho de pesquisadores OO (que originaram todas as idéias) e de
autores de OO (que nos apresentaram essas idéias em documentos no passado). Nós sim-
plesmente aplicamos essas idéias para a construção de aplicações reais de um modo pro-
veitoso. A todos que conceberam essas idéias, conceitos, mecanismos e técnicas, e aos
escritores de renome orientados a objeto que surgiram antes de nós, prestamos nosso re-
conhecimento e o agradecimento; sem vocês, a conclusão deste livro seria impraticável.
Teorias e idéias são algo maravilhoso; todavia, do ponto de vista dos profissionais,
a experiência é o melhor mestre. Não poderíamos ter realizado este livro sem nossas ex-
periências em aplicar a tecnologia OO e os métodos em projetos reais. Agradecemos aos
nossos superiores hierárquicos, antigos e atuais, que tiveram a coragem de permitir-nos
aplicar desenvolvimento de software de ponta (muitas das vezes com grande esforço e
sacrifício). Sem o suporte deles, não teríamos sido capazes de testar o que foi escrito.
Richard Lee agradece ao grande número de pessoas que trabalharam para ele e que
foram os pioneiros na aplicação das idéias escritas neste livro em projetos da vida real.
Essas pessoas compartilharam com ele tanto a empolgação como a angústia de serem os
“primeiros” a aplicar tecnologia OO em projetos de grande porte em suas respectivas em-
presas. (Ou isso caracterizou uma certa estupidez por parte deles: a de seguir sua lide-
rança no sentido de correr riscos que nenhuma outra pessoa da empresa desejava?) A
todas essas pessoas, Richard presta seus votos de agradecimento.
William Tepfenhart agradece a seus parceiros de trabalho, do passado e do presen-
te, cada um deles contribuindo para seu melhor entendimento da ciência da computação.
Ele agradece a Bill Case, Freid Elliot e Dayton Eden, que o capacitaram na transição de
escrever modelos de sistemas físicos em FORTRAN a modelos de inteligência artificial di-
rigidos aos estudos de sistemas físicos. Ele agradece a seus colegas de trabalho que am-
pliaram seu repertório de modelos para incluir objetos, relacionamentos e regras.
A segunda edição não teria sido possível sem a colaboração das seguintes pessoas:
■ Terrance Cleary, AT&T Labs
■ Dan Dvorak, Jet Propulsion Laboratory
■ John Eddy, AT&T Labs
■ Bruce Handelman, AT&T
■ Rolf Kampo, AT&T
■ David Lundin, pcorder.com
■ David Simen, AT&T
Estas pessoas contribuíram com uma parcela significativa de tempo e esforço revi-
sando o manuscrito. Seus comentários e idéias sobre o material apresentado são profun-
damente apreciados.
Uma grande parcela de crédito tem de ser dirigida a nossos gerentes, Dick Machol,
Raj Warty, Moses Ling e Raj Dube, da AT&T e Lucent Bell Laboratories, por seus supor-
tes. Se não tivessem facilitado o uso de ferramentas e recursos dos computadores fora dos
horários de trabalho, não teríamos conseguido finalizar este livro. Ainda, agradecimentos
PREFÁCIO DA SEGUNDA EDIÇÃO xxv

vão para a administração da Monmouth University, que apoiou financeiramente Bill


na produção da 2a edição. Todavia, não inferimos que a AT&T ou a Lucent Bell Labo-
ratories estivessem corroborando nosso trabalho. Este livro consiste de nossa visão
particular da tecnologia OO, baseada fundamentalmente nos 35 anos de experiência
de Richard Lee no ramo de desenvolvimento de software e em suas experiências com
a tecnologia OO. William Tepfenhart forneceu um grande número de excelentes “re-
ceitas” e organizou o material de modo que desenvolvedores pudessem rapidamente
se tornar membros colaboradores de uma equipe de desenvolvimento.
Finalmente, reconhecemos e apreciamos a valiosa contribuição de nossos revisores:
Rex Jaeschke, presidente do comitê ANCI C, consultor autônomo e escritor, e Robert Tay-
lor, da Taylor Computing. Na qualidade de autores, estamos perfeitamente satisfeitos em
assumir a responsabilidade por possíveis erros, omissões, imprecisões, inveracidades,
idéias confusas e quaisquer boas qualidades apresentadas por este livro. Todos os comentá-
rios construtivos serão bem-vindos. Os destrutivos, felizmente para nós, serão ignorados!

Richard C. Lee
William M. Tepfenhart
Página em branco
PREFÁCIO DA PRIMEIRA EDIÇÃO
Prefácio da Primeira Edição

Prefácio da Primeira Edição

A xiomas na Filosofia não são considerados como tais até que sejam cons-
tatados pelos nossos próprios meios: lemos coisas agradáveis, mas nunca
as sentimos totalmente até passarmos pelas mesmas fases que o autor.

John Keats (1795 — 1821), Carta para


J. H. Reynolds, 3 de maio de 1818

Este livro é dirigido a atarefados analistas e desenvolvedores profissionais de software


que trabalham com sistemas de grande porte, especialmente àqueles que precisam inte-
grar seus novos sistemas a sistemas legados. Caso você ainda não tenha tido tempo de
freqüentar algum curso e nem incrementado seus conhecimentos sobre tecnologia orien-
tada a objeto (OO) utilizando a Linguagem de Modelagem Unificada (Unified Modeling
Language, UML) e C++, este livro é um guia de auto-aprendizado dirigido a preencher
suas necessidades. Ele contribuirá para que você entenda as diferenças entre análise, de-
senho e programação OO. Nossos objetivos são:
■ Ensiná-lo a construir uma aplicação orientada a objeto utilizando C++ e tomar as
decisões balanceadas e corretas para atender às suas necessidades de negócio.
■ Esclarecer os conceitos básicos associados à tecnologia orientada a objeto.
■ Prover suficiente profundidade quanto à abrangência para que alunos e profissio-
nais ingressem no campo e fiquem aptos a desenvolverem-se na matéria.
■ Expor alguns dos mitos que cercam a tecnologia OO enquanto sua praticidade é fo-
cada como uma ferramenta de engenharia de software.
■ Dotá-lo de uma “receita”, ou de um guia do tipo passo-a-passo, para que você possa
cumprir todas as etapas da tecnologia orientada a objeto.
■ Defender a teoria de que conceitos baseados em regras, lógica obscura, multimídia
e modelagem de dados OO, integrados em um modelo único, podem tratar dos de-

xxvii
xxviii UML E C++

safios de negócios atuais e futuros para as organizações voltadas à tecnologia da in-


formação.
■ Prover uma abordagem prática para análise, desenho e programação na tecnologia
OO.
■ Mostrar como implementar tecnologia OO utilizando C++ (que, embora não seja
uma linguagem orientada a objeto, é uma linguagem de multiparadigmas extrema-
mente poderosa).
■ Contrabalançar um pouco de teoria e prática com exercícios de aplicação extraídos
da literatura existente.
Não é preciso ter conhecimento sobre ciência da computação ou matemática avan-
çada para entender os importantes conceitos e tópicos orientados a objeto em profundi-
dade. Até mesmo os capítulos de programação não requerem experiência em C++; eles
ilustram como o trabalho de codificação pode ser desempenhado nesta linguagem.

Tecnologia Orientada a Objeto


Somos desenvolvedores de software de sistemas de grande porte. Acreditamos que a tec-
nologia OO é a (r)evolução de software mais importante da década de 90. Ela está mu-
dando a forma como construímos software e o modo como aplicações se intercomunicam
ao longo de redes de alcance mundial e pelos computadores de vários distribuidores.
Mais do que isso, o modelo OO está modificando a forma como desenhamos processos
de negócios e o modo como pensamos sobre uma empresa. Nos dias de hoje, a maioria das
empresas precisa ser redesenhada para superar os futuros desafios na área de negócios.
O redesenho de processos nas empresas é uma das mais importantes funções de
uma organização voltada à tecnologia da informação. Um modelo que capture os proces-
sos, procedimentos, diretrizes e regras das empresas facilita o desenho. Ferramentas que
traduzam o modelo em um sistema operacional aceleram a implementação do redesenho.
Quando o mercado ou condição de negócio se alterarem, esses sistemas poderão ser re-
generados para refletir essas transformações ao atualizarem o modelo e utilizarem aque-
las ferramentas. A ciência (engenharia) da informação tem nos levado mais adiante e com
maior velocidade do que qualquer outra ciência nas décadas anteriores. Entretanto, ela
precisa de uma atualização constante e melhorada, métodos mais refinados para resolver
nossas demandas de negócios e nossos problemas com modelagem e programação OO.
Cada vez mais pessoas acreditam que a tecnologia OO porá um fim na crise do software,
significando que os mecanismos de OO serão para o software o que os parafusos e as vi-
gas representam para os projetos na construção civil e, da mesma maneira, o que o chip
representa para o desenho do hardware de computadores. Essa crença origina-se dos se-
guintes pontos:
■ A proficiência de um modelo OO de nível mais alto proverá ao desenhista de soft-
ware componentes programáveis do mundo real reduzindo com isso os custos re-
lativos a desenvolvimento de software.
■ Sua capacidade em compartilhar e reutilizar código com técnicas OO reduzirá o
tempo para desenvolvimento de uma aplicação.
■ Sua habilidade de localizar e minimizar os efeitos de modificações por meio de pro-
gramação de mecanismos de abstração capacitará um desenvolvimento mais acen-
tuado e rápido, e irá prover software mais confiável e robusto.
PREFÁCIO DA PRIMEIRA EDIÇÃO xxix

■ Sua capacidade de administrar a complexidade possibilitará a desenvolvedores tra-


tarem de aplicações mais difíceis.
A coleção de conceitos OO é um conjunto de ferramentas para modelagem da rea-
lidade. Esse conjunto de ferramentas OO dota os desenvolvedores com os melhores re-
cursos para gerenciamento da complexidade. Certos conceitos OO auxiliam os
desenvolvedores no tocante a produzir software flexível e de mais fácil manutenção.

Por Que a UML?


Como profissionais da tecnologia OO, sabemos que todos os métodos, se praticados ade-
quadamente, resultam no mesmo ou em modelo similar. Diferentes notações para lingua-
gens de modelagem podem ser impedimentos ao progresso. Estamos interessados em
resultados que nos auxiliem a produzir software de manutenção mais simples, a menor
custo e de uma maneira mais programada. A UML nos proporciona todos os ícones de
desenho necessários para capturar a maioria dos conceitos ou mecanismos que considerar-
mos valiosos para a resolução dos problemas reais de negócios. E mais, ela provê todos
os diagramas de que precisamos, diagramas esses vitais para a documentação de nossos
modelos. E, finalmente, ela é uma linguagem “viva” que oferece a capacidade de estender
a notação para mecanismos ainda não definidos pelo distinto grupo formado por Grady
Booch, James Rumbaugh e Ivor Jacobson, da Rational Software Corporation.

Por Que a Linguagem C++?


Trata-se de uma idéia equivocada que C++ seja, exclusivamente, uma linguagem de pro-
gramação OO. C++ é uma linguagem de multiparadigmas que suporta diversos paradig-
mas de programação, inclusive o paradigma procedural, o tipo de dado abstrato e o
paradigma OO. Mostraremos a você como mapear (traduzir) seu modelo OO para as
construções C++ utilizando o paradigma OO em C++. Também lhe mostraremos como
utilizar outros conceitos não orientados a objetos da linguagem no contexto do desenho
OO para ajudá-lo a resolver seus problemas de negócios.
C++ é nossa linguagem de escolha por duas razões práticas. Primeiro e acima de
tudo, a maioria dos desenvolvedores tem de abordar restrições reais: construir interfaces
para sistemas legados e limitações técnicas sobre bancos de dados, armazenamento e de-
sempenho. C++ dota os desenvolvedores com múltiplos paradigmas, e estes podem ser
ajustados finamente quando necessário. Em segundo lugar, fornecedores de ferramentas
e compiladores investiram todo o seu dinheiro na linguagem C++.

Nosso Enfoque da Tecnologia Orientada a Objeto


Não somos puristas nem teóricos OO. Somos desenvolvedores dispostos a utilizar qual-
quer boa idéia que nos auxilie a atingir duas metas críticas nos negócios: menores custos
de desenvolvimento e redução do tempo de colocação no mercado de versões mais aper-
feiçoadas de software. Acreditamos que estes objetivos técnicos — confiabilidade, man-
teneabilidade e flexibilidade — são críticos para cumprir nossas metas nos negócios.
Nosso enfoque para utilização da tecnologia OO é o de administrar a complexidade
de desenvolver software de modo que ele seja um produto confiável, de fácil manutenção
e flexível. Administrar complexidade é a chave para se obter tais objetivos e, por conse-
guinte, nossas metas nos negócios. Para o gerenciamento de complexidade em domínios
xxx UML E C++

de problemas intrincados, constatamos que os desenvolvedores necessitam saber como os


objetos, classes, relacionamentos e regras se inserem no paradigma orientado a objeto.
Quando modelamos a maioria dos domínios de problemas complexos, descobrimos ob-
jetos, classes e muitos relacionamentos entre objetos. Além disso, necessitamos capturar
as regras (diretrizes) existentes dentro daquele domínio. Portanto, precisamos empregar
técnicas de modelagem estática muito ricas para capturar os relacionamentos entre os da-
dos (objetos).
Muitos especialistas OO consideram os relacionamentos como algo “ruim” porque
eles violam o princípio de encapsulamento. De nossa perspectiva, eles contribuem para
que administremos a complexidade do domínio do problema e atinjamos nossos objeti-
vos nos negócios. Assim, os utilizamos com satisfação e buscamos mais mecanismos e su-
porte de linguagens nesta área. No Capítulo 9 sobre semântica declarativa, apontamos
que regras e diretrizes deverão ser capturadas como parte integral de nosso modelo e não
em algumas extensões especiais do subsistema.
Utilizar mecanismos para auxiliar-nos a modelar domínios de problemas complexos
é consistente com nossas opções pela UML, como nossa linguagem de modelagem, e pela
C++, como nossa linguagem de programação. Ambas, UML e C++, capacitam-nos a de-
finir qualquer mecanismo necessário que nos ajude a criar software mais gerenciável.
Abordamos comportamentos (dinâmicos e estáticos) e polimorfismo para capturar
os aspectos procedurais do modelo. O uso de uma máquina de estado ou de algum outro
modelo de estado contribui para que gerenciemos complexidade procedural enquanto
tratamos de ajustamento de tempo, sincronização e interrupções. Essas idéias geralmente
são ignoradas ou omitidas por uma grande parcela dos livros OO. Não tratamos desses
temas em profundidade, mas lançamos a pedra fundamental de forma que o leitor possa
se utilizar da semântica inserida à UML.
Cremos que o segredo do sucesso ao construir sistemas extensos OO reside no fato
de os desenvolvedores e/ou programadores saberem mais do que é ensinado na maioria
dos livros OO. Construir sistemas extensos requer o uso de mecanismos promovidos por
alguns especialistas nessa teoria, porém, nem sempre aceitos por todos eles. Desenvolve-
dores profissionais precisam pelo menos compreender como esses aspectos do domínio
do problema podem ser tratados antes de tornarem-se um integrante de uma equipe pro-
dutiva. Este livro não fará de você um especialista. Ainda serão precisos especialistas
e/ou consultores para desenvolver o sistema. Aplicando-se a regra dos 80/20, este livro
propicia os 80% que podem torná-lo produtivo e ainda entender como os especialistas so-
lucionam as dificuldades contidas nos 20% restantes.
Neste livro não tratamos das tendências ou “modas” mais recentes relativas à tec-
nologia OO, incluindo modelos de desenho e computação distribuída de objetos. Embora
elas sejam interessantes, não estamos convencidos de que possam contribuir significati-
vamente para nosso objetivo de prover uma estrutura prática que capacite os desenvol-
vedores novatos na programação OO a obterem um conhecimento extremamente rápido
sobre essa matéria.
Finalmente, não concordamos com a afirmação da maioria dos especialistas de que
a tecnologia OO seja uma tecnologia madura. Cremos mais que ela esteja no começo de
sua vida; o que nos impressiona é o grau de realizações que poderemos atingir com essa
tecnologia tão nova. A tecnologia orientada a objeto tem o enorme potencial de auxiliar-
nos na administração da complexidade, fator esse inexistente nas tecnologias mais anti-
gas (procedural, funcional, baseada em regras etc.).
PREFÁCIO DA PRIMEIRA EDIÇÃO xxxi

De Que Forma este Livro Está Organizado


Conduzimos o leitor para uma viagem pelos caminhos de nosso raciocínio na aplicação
de técnicas e métodos orientados a objeto. Eles não constituem um conjunto de regras ab-
solutas. Nosso objetivo é fazê-lo pensar sobre bons conceitos OO e bons princípios de de-
senho quando do desenvolvimento de software e da programação em C++. No estudo de
caso, elaboramos um projeto passando por todas as etapas da análise, desenho e codifi-
cação OO, utilizando técnicas específicas OO e aplicando conceitos fundamentais OO. O
desenho é implementado em C++ com as técnicas de aperfeiçoamento de desempenho
desta linguagem.
Este livro foi escrito originalmente para ser um guia auto-explicativo que deveria
ser lido na ordem seqüencial. Adotamos um método que Richard vem utilizando por vá-
rios anos no ensino de habilidades básicas e conceitos OO; nós não defendemos esta prá-
tica como um método para se construir sistemas orientados a objeto. Cada capítulo
discute uma etapa importante de nosso enfoque da tecnologia OO. A maioria dos capí-
tulos termina com um guia passo-a-passo ou “receita”. Esperamos que o leitor utilize es-
sas etapas unicamente como diretrizes; confiando sempre no bom senso em lugar de
seguir cegamente etapas prescritas previamente.

Capítulo 1 apresenta as razões pelas quais as empresas estão interessadas em OO e o mo-


tivo pelo qual um profissional voltado a desenvolvimento de software deve conhecer a
tecnologia OO.
Capítulo 2 aborda o ramo de software e a necessidade de se administrar a complexidade.
Capítulo 3 descreve como encontrar a terminologia básica e os conceitos principais da
tecnologia orientada a objeto.
Capítulo 4 descreve como descobrir objetos potenciais, utilizando como primeira etapa o
emprego da tecnologia orientada a objeto.
Capítulo 5 descreve como diferenciar entre objetos “reais” e “falsos” pela identificação
de atributos (dados) e serviços associados a objeto.
Capítulo 6 demonstra como capturar o comportamento de objetos.
Capítulo 7 descreve como identificar e descrever comportamento dinâmico.
Capítulo 8 descreve os vários relacionamentos (generalização/especialização, vínculo,
agregação de objetos etc.) disponíveis para organizar todos os objetos de um sistema.
Capítulo 9 descreve como incorporar fatos declarativos no modelo OO sobre conheci-
mento de objetos e um mecanismo baseado em regras para suas implementações.
Capítulo 10 descreve como transformamos objetos em classes para nos beneficiarmos dos
mecanismos de C++.
Capítulo 11 aborda algumas questões de desenho com o desenvolvimento de sistemas
OO.1
Capítulo 12 provê os elementos básicos necessários de C++ para efetivar a programação
OO utilizando C++. (Os leitores que estiverem familiarizados com C++ podem pular este
capítulo.)
Capítulo 13 ensina como implementar uma classe, a qual é um modelo para se criar ob-
jetos.
Capítulo 14 ensina como implementar as especificações de comportamento desenvolvi-
das no Capítulo 6.

1. Desenho é um processo muito complexo e constitui um tópico separado da tecnologia orientada a


objeto.
xxxii UML E C++

Capítulo 15 aborda como criar e destruir objetos utilizando o mecanismo de classe de


C++.
Capítulo 16 aborda como implementar o relacionamento de generalização/especialização
(um dos conceitos mais importantes de OO) pela utilização do mecanismo de derivação
de classes de C++.
Capítulo 17 aborda como implementar outros relacionamentos não suportados por C++.
Capítulo 18 introduz um exemplo de estudo de caso demonstrando como utilizar o mé-
todo dos Capítulos 4 a 17.
Capítulo 19 apresenta a abordagem de uma equipe para fazer a análise de objetos do es-
tudo de caso.
Capítulo 20 apresenta a abordagem de uma segunda equipe para fazer a análise de ob-
jetos do estudo de caso.
Capítulo 21 demonstra como o modelo do objeto apresentado no Capítulo 20 é traduzido
em um desenho de objeto e, depois, implementado em C++.

Agradecimentos
Devemos tanto a tantas pessoas. O estímulo para a criação deste livro é proveniente dos
milhares de alunos que participaram do curso oferecido por Richard Lee, seus amigos e
colegas. Ele apreciou a persistência e o encorajamento por parte deles, ignorando os con-
tratempos pessoais.
Pelo fato de sermos desenvolvedores (e não pesquisadores, acadêmicos ou escrito-
res), alavancamos o trabalho de pesquisadores OO (que originaram todas as idéias) e de
autores de OO (que nos apresentaram essas idéias em documentos no passado). Nós sim-
plesmente aplicamos essas idéias para a construção de aplicações reais de um modo pro-
veitoso. A todos que conceberam essas idéias, conceitos, mecanismos e técnicas, e aos
escritores de renome orientados a objeto que surgiram antes de nós, prestamos nosso re-
conhecimento e o agradecimento; sem vocês, a conclusão deste livro seria impraticável.
Teorias e idéias são algo maravilhoso; todavia, do ponto de vista dos profissionais,
a experiência é o melhor mestre. Não poderíamos ter realizado este livro sem nossas ex-
periências em aplicar a tecnologia OO e os métodos em projetos reais. Agradecemos a to-
dos nossos chefes, antigos e atuais, que tiveram a coragem de deixar-nos aplicar
desenvolvimento de software de ponta (muitas das vezes com grande esforço e sacrifí-
cio). Sem o suporte deles, não teríamos sido capazes de testar o que foi escrito.
Richard Lee agradece ao grande número de pessoas que trabalharam para ele e que
foram os pioneiros na aplicação das idéias escritas neste livro em projetos da vida real.
Essas pessoas compartilharam com ele tanto a empolgação como a angústia de serem os
“primeiros” a aplicar tecnologia OO em projetos de grande porte em suas respectivas em-
presas. (Ou isso caracterizou uma certa estupidez por parte deles: a de seguir sua lide-
rança no sentido de correr riscos que nenhuma outra pessoa da empresa desejava?) A
todas essas pessoas, Richard presta seus votos de agradecimento.
William Tepfenhart agradece a seus parceiros de trabalho, do passado e do presen-
te, cada um deles contribuindo para seu melhor entendimento da ciência da computação.
Ele agradece a Bill Case, Freid Elliot e Dayton Eden, que o capacitaram na transição de
escrever modelos de sistemas físicos em FORTRAN a modelos de inteligência artificial di-
rigidos aos estudos de sistemas físicos. Ele agradece a seus colegas de trabalho que am-
pliaram seu repertório de modelos para incluir objetos, relacionamentos e regras.
PREFÁCIO DA PRIMEIRA EDIÇÃO xxxiii

Agradecemos a Barry Peiffer, David Siemen, John Eddy e Dan Dvorak por comparti-
lharem seu tempo e feedback. Steve Ruder fez um excelente trabalho como editor técnico.
Uma grande parcela de crédito tem de ser dirigida a nossos gerentes, Dick Machol,
Raj Warty, Moses Ling e Raj Dube, por seus suportes. Se não tivessem facilitado o uso de
ferramentas e recursos dos computadores fora dos horários de trabalho, não teríamos
conseguido finalizar este livro. Todavia, não inferimos que a AT&T ou a Bell Laboratories
estivessem corroborando nosso trabalho. Este livro consiste de nossa visão particular da
tecnologia OO, baseada fundamentalmente nos 30 anos de experiência de Richard Lee no
ramo de desenvolvimento de software e em suas experiências com a tecnologia OO.
William Tepfenhart forneceu um grande número de excelentes “receitas” e organizou o
material de modo que desenvolvedores pudessem rapidamente se tornar membros cola-
boradores de uma equipe de desenvolvimento.
Finalmente, reconhecemos e apreciamos a valiosa contribuição de nossos revisores:
Rex Jaeschke, presidente do comitê ANCI C, consultor autônomo e escritor, e Robert Tay-
lor, da Taylor Computing. Na qualidade de autores, estamos perfeitamente satisfeitos em
assumir a responsabilidade por possíveis erros, omissões, imprecisões, inveracidades,
idéias confusas e quaisquer boas qualidades apresentadas por este livro. Todos os comen-
tários construtivos serão bem-vindos. Os destrutivos, felizmente para nós, serão ignorados!

Richard C. Lee
William M. Tepfenhart
Página em branco
O Dilema da Administração
de Informações
1.O Dilema da Administração de Informações
O DILEMA DA ADMINISTRAÇÃO DE INFORMAÇÕES

T arefas e problemas em dobro;


Fogo ardendo e o borbulhar no caldeirão.

William Shakespeare, Macbeth

A tualmente, há uma grande dose de excitação e interesse acerca das técnicas orienta-
das a objeto nas organizações e na indústria da tecnologia da informação. No meio
de todo esse alarido, a maioria dos profissionais de engenharia de software precisa en-
tender qual o real significado de todo esse entusiasmo.

O Problema
As organizações estão se convertendo em organizações baseadas em informações que de-
pendem de um fluxo contínuo de dados para lidar com, virtualmente, todos os aspectos
de suas operações. A informação está se tornando cada vez mais crucial a todas as deci-
sões e oportunidades de negócios. Até mesmo organizações, que historicamente não vi-
nham focando-se na informação, estão hoje em dia extremamente dependentes dela.
Entretanto, o volume de informações está crescendo mais rapidamente do que a capaci-
dade de processá-las e de fazer um bom uso delas. Dessa maneira, as organizações estão
se “afundando” em seus próprios dados.
O problema é que:
■ Os custos de projeto de software estão crescendo e os custos relativos a hardware
diminuindo.

1
2 UML E C++ CAP. 1

■ O prazo para desenvolvimento de software está se tornando mais longo e os custos


de manutenção maiores, ao passo que, simultaneamente, o tempo de desenvolvi-
mento de hardware está encurtando e se tornando mais barato.
■ Os erros de software estão se tornando mais freqüentes, enquanto os erros de hard-
ware, praticamente inexistentes.
■ O software é desenvolvido utilizando-se um processo rigidamente estruturado, que
é inflexível.
Esses dados são confirmados por alguns estudos, conforme mostrado nas Tabelas
1.1 e 1.2.
Fica claro que nossos métodos atuais de criação de software resultam em sistemas
com manutenção muito dispendiosa. As organizações estão despendendo uma grande
parcela de seus recursos financeiros em testes e manutenção de sistemas. Infelizmente, es-
ses métodos atuais não são eficazes nos estágios da análise de requisitos e desenho, em
que o custo para correção é muito baixo. Além disso, a Tabela 1.2 revela que 85% de nos-
sos erros são cometidos durante os dois estágios citados anteriormente. Aperfeiçoar essas
duas etapas é o modo mais rentável de melhorar a qualidade do software.

TABELA 1.1 Custos em Projeto de Software por Fase de Desenvolvimentoa


Etapa de Trabalho %

Análise de Requisitos 3
Desenho 8
Programação 7
Testes 15
Manutenção 67
a
Fonte: Butler Bloor

TABELA 1.2 Custos para Correção de Erros de Software


Fase de Desenvolvimento do % de Erros Erros Custo
Software Desvios ($) Introduzidos Encontrados Relativo para
(%) (%) Correção

Análise de Requisitos 5 55 18 1,0


Desenho 25 30 10 1,0 - 1,5
Teste do Código e da Unidade 10
Teste de Integração 50 10 50 1,0 - 5,0
Validação e Documentação 10
Manutenção Operacional 5 22 10 - 100
a
Fonte: Hughes DoD Composite Software Error History

As Organizações Modernas Estão a Caminho de um Desastre


Se combinarmos esses problemas de software com a crescente taxa de mudanças existente
nas condições das organizações nos dias de hoje, tem-se uma receita ideal para a ocorrên-
cia de desastres. Ainda que as contas a receber de uma organização possam ser muito
promissoras no presente, as decisões baseadas nos resultados de software inadequado
poderão ameaçar o potencial de um negócio ser financeiramente sólido no futuro.
CAP. 1 O DILEMA DA ADMINISTRAÇÃO DE INFORMAÇÕES 3

Hoje em dia, uma grande parte do software corporativo está obsoleta muito antes
de sua colocação como produto no mercado, e não pode receber desenvolvimentos no
sentido de satisfazer as demandas empresariais futuras. Em um estudo das organizações
MIS, conduzido pelo Standish Group, os projetos de software eram caracterizados por
propósitos, recomeços, excedentes de custo e prazo e por deficiências no conteúdo.
Esse grupo de pesquisas constatou que existiam três categorias básicas de projetos:
Bem-Sucedidos, Contestados e Refutados. Projetos bem-sucedidos resultam em funciona-
lidade plena no prazo adequado e segundo o orçamento acordado. Projetos contestados
demandam, em vez de plena funcionalidade, um orçamento maior do que o proposto e,
também, mais tempo. Os projetos refutados são cancelados durante a fase de desenvol-
vimento. A questão principal: apenas 16,2% dos projetos são bem-sucedidos. A maioria
dos projetos, 52,7%, é contestada. Os restantes, 31,1%, são cancelados antes de sua colo-
cação no mercado. Estima-se que os custos referentes a projetos contestados e refutados
para o ano de 1995 foi de US$ 140 bilhões.
Muitos projetos são iniciados com objetivos errados e se vêem tendo de começar no-
vamente desde o princípio. Esse processo não suporta a colocação do produto no merca-
do na data original prevista. O Standish Group constatou que para cada 100 projetos
iniciados há 94 recomeços. Isso não significa que todos esses recomeços são para projetos
distintos — alguns projetos têm múltiplos recomeços.
Aproximadamente 28% dos projetos exibem excedentes de custo de 150% a 200% de
seus custos estimados originais. O excedente de custo médio encontrado nas organiza-
ções é de 189% do custo original estimado. A Tabela 1.3 ilustra a distribuição dos exce-
dentes de custo. Não são necessários mais de 200% de excedente nos custos para
ocasionar a falência de uma organização. Infelizmente, as organizações de menor porte
tendem a ter excedentes de custo maiores — com uma média de 214%.
Os números para os excedentes de prazo são igualmente sombrios. O excedente mé-
dio para os mesmos 28% dos projetos é de 222% do prazo estimado original. Os números
para os excedentes de prazo são apresentados na Tabela 1.4. Na época de hoje, quando
um dos principais objetivos é a rápida colocação de produtos no mercado, um excedente
de prazo de 200% poderá ter um impacto muito sério na faceta competitiva de uma or-
ganização.

TABELA 1.3 Excedentes de Custo


% Excedente de Custo % de Respostas

<20% 15,5%
21% - 50% 31,5%
51% - 100% 29,6%
101% - 200% 10,2%
201% - 400% 8,8%
>400% 4,4%
4 UML E C++ CAP. 1

TABELA 1.4 Excedentes de Prazo


% Excedente de Prazo % de Respostas

<20% 13,9%
21% - 50% 18,3%
51% - 100% 20,0%
101% - 200% 35,5%
201% - 400% 11,2%
>400% 1,1%

TABELA 1.5 Colocação no Mercado de Recursos


% de Recursos (Funções) % de Respostas

<25% 4,6%
25% - 49% 27,2%
50% - 74% 21,8%
75% - 99% 39,1%
100% 7,3%

Existe uma declaração subliminar comum sobre como se processa a distribuição de


software a clientes, “Você quer o software no prazo determinado ou quer tê-lo com fun-
cionalidade plena?”. Infelizmente, o Standish Group dispõe dos dados para mostrar que
não se trata de piada. O projeto típico não resulta em funcionalidade plena. A Tabela 1.5
ilustra a porcentagem de respostas dos projetos em comparação à porcentagem de fun-
cionalidade disponibilizada aos clientes.

O Que o Cliente Deseja?


O cliente deseja uma solução (sistema) que:
■ Atenda às necessidades funcionais.
■ Adapte-se ao ambiente de rápidas mudanças dos negócios.
■ Enquadre-se às limitações de tempo de execução (run-time) [tempo/espaço].
O cliente quer um software que seja:
■ De fácil manutenção.
■ Desenvolvido dentro dos recursos propostos (tempo, espaço, materiais, pessoas).
■ Projetado tendo-se em mente uma longevidade apropriada.
Devido ao fato de que nossos métodos clássicos de desenvolvimento (métodos es-
truturados, modelagem de dados, adhoc etc.) não atenderam às expectativas de nossos
clientes, consultores orientados a objeto expuseram à gerência das organizações que a
aplicação da tecnologia orientada a objeto irá satisfazer muito mais essas necessidades do
que os métodos anteriores. Agora, que entendemos a razão pela qual as organizações es-
tão excitadas diante da tecnologia orientada a objeto, permita-nos examinar por que os
engenheiros de software de ponta estão tão entusiasmados com essa tecnologia.
CAP. 1 O DILEMA DA ADMINISTRAÇÃO DE INFORMAÇÕES 5

Por Que o Método Orientado a Objeto É Importante aos


Desenvolvedores
Na década de 60, os desenvolvedores de software estavam construindo aplicações/siste-
mas pequenos e relativamente simples. Eles utilizaram algumas linguagens muito sim-
ples (linguagem Assembly, FORTRAN e COBOL), projetadas especificamente para uso
próprio. Os desenvolvedores não utilizavam outro método a não ser a própria “criativi-
dade”. A programação era considerada uma atividade criativa, e os desenvolvedores
eram contratados com base em seus espíritos independentes. Infelizmente, isso originou
um código “spaghetti” e os famosos GOTOs no código, horríveis de receber manutenção.
Nos anos 70, Al Constantine e Ed Yourdon apareceram com um método de desen-
volver software que utilizava a função como seu bloco de construção. Esse método, co-
nhecido de muitos desenvolvedores como análise e desenho estruturado, fez com que os
desenvolvedores organizassem o software por funções. Àquela época, isso parecia muito
natural porque as organizações eram organizadas funcionalmente. A maioria das aplica-
ções e sistemas para computador era escrita para uma específica organização funcional,
de modo que as aplicações tornavam-se uma extensão daquela organização.
Essa abordagem parecia ser um melhor modo de construir software, especialmente
se, além de análise e desenho estruturados, fosse adotada a programação modular. A pro-
gramação modular eliminou os GOTOs praticamente ingovernáveis. O método da análise
e desenho estruturados cooperou com os desenvolvedores unicamente no tocante à ad-
ministração de funções. Todavia, esse método contribuiu muito pouco para que os desen-
volvedores pudessem administrar dados.
Apesar de ser desenvolvido inicialmente para “aplicações empresariais”, o método
da análise e desenho estruturados se adequou melhor às “aplicações científicas”. Na
maioria das aplicações científicas, as funções são muito estáveis; uma grande parte delas
é determinada por leis da natureza, que raramente se modificam. Em aplicações empre-
sariais, entretanto, as funções são definidas pelo homem e estão sujeitas a mudanças a
todo instante.
Muito embora esse método tenha sido aplicado com sucesso em muitas aplicações
científicas, ele originou importantes organizações voltadas à manutenção de aplicações
empresariais. Para muitas dessas aplicações, os profissionais de software tinham de mu-
dar continuamente os blocos de construção (ou seja, mudar as funções). Mudanças nesse
nível tão fundamental requerem basicamente a reconstrução da aplicação/sistema; por-
tanto, os profissionais de software reconheceram a necessidade de disporem de outros
métodos de desenvolvimento de software.
Nos anos 80, Peter Chen (que desenvolveu o diagrama de entidade e relacionamen-
to) e Ed Codd (desenhista do banco de dados relacional) dotaram os desenvolvedores
com os fundamentos para um novo modo de desenvolver software baseado em uma co-
leção de itens de dados, denominada entidade, como seu bloco de construção. Essa des-
coberta parecia ser apropriada para o estabelecimento de uma nova metodologia de
desenvolvimento de software para aplicações empresariais porque os profissionais e pes-
quisadores de software daquela época acreditavam que os dados eram a parte mais está-
vel de uma “aplicação empresarial”.
Como resultado da crença amplamente reconhecida de que as entidades eram está-
veis, e pelo fato de os bancos de dados relacionais terem uma excelente base matemática,
a maioria das organizações nos anos 80 começou a utilizar métodos de modelagem de da-
dos para desenvolver software. Todavia, os métodos de modelagem de dados tinham a
6 UML E C++ CAP. 1

deficiência inversa dos métodos estruturados. Os métodos estruturados assistiram aos


desenvolvedores quanto à administração de dados, mas os métodos de modelagem de
dados não colaboraram para que aqueles obtivessem o mesmo resultado com as funções.
A teoria era de que todas as funções poderiam ser definidas pela utilização de constru-
ções de linguagem consistentes com o cálculo pressuposto em primeira ordem. Infeliz-
mente, a maioria dos problemas que precisava ser solucionada não poderia utilizar-se
desse tipo de cálculo.
No momento, requer-se um método de desenvolvimento de software que resolva as
deficiências dos métodos anteriores. Esses métodos utilizavam apenas uma visão do sis-
tema como seus blocos de construção e, realmente, não acomodavam outras visões. Por
exemplo, o método da análise e desenho estruturados focava a função como o bloco cons-
trutivo de um sistema. A organização de dados é suportada muito debilmente nos dia-
gramas de fluxo de dados. Similarmente, no método da análise de dados (diagrama de
entidade e relacionamento) o bloco de construção era uma entidade, mas as funções ne-
cessárias para satisfazer os requisitos do sistema eram virtualmente ignoradas nele. Ne-
nhum dos métodos lida bem com a captura do comportamento dinâmico. A semântica
assertiva (regras) e os mecanismos de tratamento de exceções foram totalmente ignorados.
O método orientado a objeto é o único que conhecemos que provê aos desenvolve-
dores de software um paradigma que suporta todas as visões de um sistema de forma
igualmente boa, e assim o faz de maneira ortogonal. Como resultado, o desenvolvedor
pode administrar a complexidade da situação.
Na programação orientada a objeto, a aplicação (sistema) é uma rede dinâmica de
objetos colaboradores. Com a adição de regras ao paradigma de uma maneira ortogonal,
a semântica declarativa pode ser integrada com a semântica procedural em um único sistema.
Partindo-se do ponto de vista pragmático, o método orientado a objeto permite aos
desenvolvedores de software administrar a complexidade do domínio de problema e sua
tecnologia de suporte. Quando os desenvolvedores conseguem administrar muitos aspec-
tos do domínio de problema, eles podem produzir software mais flexível e de manuten-
ção mais fácil.

■ ■ RESUMO
Na atualidade estamos deparando com uma crise em termos de desenvolvimento de soft-
ware. Essa crise abarca custo, tempo de chegada ao mercado, erros e funcionalidade.
Quanto mais cedo forem identificados os erros no processo de desenvolvimento de soft-
ware, maiores as economias no tocante aos custos. As técnicas orientadas a objeto pro-
porcionam vantagens capazes de reduzir custos, tempo requerido para desenvolver um
produto e erros, e provêem flexibilidade no processo de desenvolvimento do produto.
Administrando a Complexidade:
Análise e Desenho
2.Administrando a Complexidade: Análise e Desenho
ADMINISTRANDO A COMPLEXIDADE: ANÁLISE E DESENHO

O que não pode ser curado deve ser tolerado.

Francis Rabelais

N o início dos anos 60, os desenvolvedores não utilizavam outro método a não ser a
própria criatividade. Desempenho e utilização de menos núcleo (memória) eram as
limitações mais importantes. Escrever código “spaghetti” e utilizar os abomináveis GO-
TOs constituíam formas aceitáveis de aumentar o desempenho e utilizar menos núcleo.
A maioria dos programas não era extensa ou complexa comparada com os padrões dos
dias de hoje. Entretanto, mesmo assim os desenvolvedores tinham dificuldades em recor-
dar todas as informações necessárias para saber de que forma desenvolver, depurar e efe-
tuar manutenção de software.
Por exemplo, quando um dos autores iniciou-se pela primeira vez nesse campo, nos
primórdios dos anos 60, ele escreveu alguns softwares de missão crítica para seu empre-
gador. Esse código ainda hoje está sendo utilizado. Há aproximadamente 10 anos, seu ex-
empregador chamou-o e pediu-lhe para que o ajudasse a efetuar modificações nesse
código. Os seus programadores tinham estudado o código, mas não conseguiam desco-
brir como o software funcionava. Conforme dito sucintamente pelo seu ex-empregador,
“Nós sabemos que o programa funciona, pois o temos utilizado durante 20 anos, mas
quando estudamos o código não conseguimos entender como você pode fazê-lo operar”.
Esse ex-patrão enviou o código ao autor para que o estudasse. Após algumas semanas, o
autor concluiu que não poderia auxiliar seu ex-patrão a desenhar a modificação, pois
também não sabia como tinha conseguido torná-lo executável 20 anos atrás. Dessa ma-

7
8 UML E C++ CAP. 2

neira, seu ex-patrão não tinha outra opção a não ser deixar o código intocado e fazer as
modificações em um diferente nível com um código que fosse compreensível.
No final dos anos 60 e início dos anos 70, foram introduzidas linguagens de nível
mais alto (COBOL, FORTRAN, ALGOL) para cooperar na resolução de alguns desses
problemas. Essas linguagens certamente ajudaram a automatizar o gerenciamento de va-
riáveis locais e realizaram a combinação implícita entre argumentos e parâmetros.
Junto com essas novas linguagens, os desenvolvedores utilizaram um método mais
estruturado para desenhar e desenvolver software. Lembra-se do método da análise e de-
senho estruturados? Lembra-se da programação modular? Essas técnicas aumentaram as
expectativas do que um computador poderia ser capaz de fazer em prol da comunidade
de usuários. À medida que desenvolvedores tentaram satisfazer suas comunidades de
usuários, buscando resolver problemas mais complexos com a utilização de computado-
res, as tarefas tornaram-se tão longas e complexas que até mesmo os melhores programa-
dores não conseguiam compreendê-las. Assim nós movemos a programação de uma
atividade individual criativa para uma atividade de equipe estruturada.
Quando isso ocorreu, notamos que um programa que, supostamente, um progra-
mador conseguiria escrever em três anos não poderia ser escrito por três programadores
que trabalhassem durante um ano. É esse fenômeno que originou a memorável frase de
Fred Brooks: “O processo de dar à luz uma criança leva nove meses, não importa quantas
mulheres recebam essa missão”. Certamente que a complexidade do software era a prin-
cipal razão por detrás do comportamento não-linear do esforço de desenvolvimento.
Na programação imperativa, as interconexões entre os componentes de um softwa-
re são muito complicadas e uma grande quantidade de informações precisa ser comuni-
cada entre os vários membros da equipe para que eles as tenham de forma correta.1 A
questão-chave é, “O que causa essa complexidade?”. O tamanho absoluto em si não pode
originar complexidade, pois o tamanho em seus próprios termos não é um impedimento
para o conceito de particionar o software em diversas partes. Na realidade, o método da
análise e desenho estruturados assumia que os programas maiores diferiam dos peque-
nos somente no tamanho. Se o tamanho fosse o ponto crítico da questão, o método da
análise e desenho estruturados teria solucionado essa dificuldade.
Infelizmente, o aspecto do desenvolvimento de software em utilizar o modelo de
programação imperativa, que o coloca entre as tarefas mais complexas para o homem, é
o seu alto grau de acoplamento (coupling). O acoplamento refere-se à dependência de
uma porção de código, quer com uma outra seção de código e/ou com algum armazena-
mento de dados. Um alto grau de acoplamento é uma propriedade inerente à programa-
ção imperativa.
No projeto de programas imperativos, subdividimos nosso programa em sub-roti-
nas (tarefas essenciais). Entretanto, se essas sub-rotinas forem úteis para outras partes do
programa, deverá haver algum tipo de comunicação de informações — seja dentro ou
fora dessas sub-rotinas. Lembre-se de que os dados não são gerenciados. Dessa maneira,
um completo entendimento do que está acontecendo geralmente requer conhecimento da
sub-rotina e de todas as rotinas que a utilizam. Isso é fraca coesão (cohesion). A coesão re-
fere-se ao grau de qualidade com que um conjunto de códigos e seus dados associados
se encaixam. Isso é especialmente verdadeiro quando forem considerados os dados que,
porventura, serão necessários. Na maioria das linguagens de programação imperativas,

1. F. Brooks Jr. abordou esse tópico em The mythical man-month.


CAP. 2 ADMINISTRANDO A COMPLEXIDADE: ANÁLISE E DESENHO 9

os nomes de variáveis (meios de acesso a dados) podem ser compartilhados somente se


estiverem em um grupamento comum.
Em resumo, muito da complexidade da programação imperativa se origina do alto
grau de acoplamento e da fraca coesão com que construímos software.2 Agora, fica-nos
aparente que a utilização de métodos clássicos no desenvolvimento de software quase
sempre resultará em sistemas construídos com fraca coesão e alto grau de acoplamento;
isso torna o sistema inflexível e de difícil gerenciamento. Em nossa opinião, é preciso ter-
se um melhor método para o desenvolvimento de software; método esse que proporcione
aos desenvolvedores a oportunidade de satisfazer as necessidades dos clientes. Para con-
seguir atingir isso, os desenvolvedores não podem utilizar os mesmos blocos construtivos
(funções, entidades, ou regras) utilizados nos métodos clássicos. Eles precisam utilizar
um mecanismo de abstração mais poderoso que funcione como o bloco de construção do
software. Esse mecanismo de abstração é uma classe (class). A classe servirá como nosso
esquema de fabricação de objetos (objects), que serão os blocos de construção dos sistemas.

Mecanismos de Abstração
Para entendermos como o paradigma orientado a objeto aplica o mecanismo de abstração
para administração da complexidade, inicialmente examinaremos as diversas formas com
as quais os engenheiros/programadores de software tinham usado a abstração anterior-
mente ao paradigma orientado a objeto. Partindo-se de uma perspectiva histórica, a uti-
lização orientada a objeto do mecanismo de abstração talvez seja uma progressão natural
da abstração orientada a funções, módulos, para abstrair tipos de dados e, em seguida, a
objetos.

Funções
Com o advento das linguagens de programação imperativas, as funções e os procedimen-
tos tornaram-se os primeiros mecanismos de abstração amplamente utilizados para se es-
crever programas. As funções possibilitaram que tarefas que eram utilizadas em muitos
locais, e até mesmo em diferentes aplicações, pudessem ser coletadas em um local e reu-
tilizadas. Os procedimentos permitiram que programadores organizassem tarefas repeti-
tivas em um único local. Ambas essas abstrações evitaram que os códigos pudessem ser
copiados em diversos lugares. Um programador escreve uma função ou um conjunto de
funções que serão utilizados por muitos outros programadores. Os demais programado-
res não precisam conhecer os detalhes exatos da implementação; eles somente precisam
conhecer a interface. Infelizmente, as funções abstratas não são um mecanismo eficaz
para a ocultação de informações. Elas apenas resolvem parcialmente o problema de múl-
tiplos programadores fazerem uso dos mesmos nomes.
Para ilustrar esse ponto, examinaremos a forma como podemos escrever um conjun-
to de funções para implementar uma pilha (stack) simples. Primeiro, estabeleceremos nos-
sas interfaces visíveis: iniciar (init) [inicializa a pilha], acrescentar (push) [coloca alguma
coisa na pilha] e extrair (pop) [retira um item do topo da pilha]. Após definirmos a inter-
face, necessitamos selecionar alguma técnica de implementação como a de um arranjo
com identificador de topo de pilha, uma lista vinculada e assim por diante. Optamos por

2. A modelagem de dados ainda é programação imperativa porque a maioria das funções/transações


continua escrita utilizando-se uma linguagem procedural. Além do mais, sistemas baseados em
regras (ou seja, sistemas inteligentes artificiais) também têm questões de acoplamento e coesão.
10 UML E C++ CAP. 2

implementar a pilha pela utilização de um arranjo e prosseguimos na codificação das fun-


ções. É fácil constatar que os dados contidos na pilha em si não podem ser tornados locais
para quaisquer das funções porque todas elas necessitam utilizá-los; portanto, as variá-
veis devem ser compartilhadas.
Nas linguagens de programação imperativas, tais como COBOL ou C anteriormente
à introdução do modificador estático, as únicas opções de se manter dados são as variá-
veis locais e globais. Como resultado, os dados da pilha devem suportar variáveis globais
se quisermos os dados compartilhados por todas as funções. Infelizmente, não há ne-
nhum modo de limitar a acessibilidade ou visibilidade de nomes de variáveis globais.
Suponhamos que tivéssemos nomeado o arranjo para nossa pilha de arranjo de pilha.
Todos os outros programadores trabalhando no projeto que utilizassem nossas funções
deveriam tomar conhecimento desse nome, visto que eles não poderiam criar uma variá-
vel que utilizasse o mesmo nome. Isso seria verdadeiro ainda que os dados fossem utili-
zados somente pelas funções da pilha que escrevêssemos, e não poderiam ser utilizados
fora destas funções. Similarmente, os nomes iniciar, acrescentar e extrair são agora reser-
vados e não poderiam ser utilizados por outros programadores no projeto para outros
fins; mesmo que esse trecho de código não tivesse qualquer relação com a pilha.
Em linguagens de programação imperativas avançadas, tais como ALGOL e Pascal,
o mecanismo de extensão de blocos oferecia um controle ligeiramente melhor sobre a vi-
sibilidade do nome do que a utilização simples de nomes locais e globais. Todavia, esse
mecanismo não resolvia o problema da ocultação de informações que acabamos de
apresentar. Para solucioná-lo, era necessário desenvolver um mecanismo de abstração
diferente.

Módulos
O módulo é um mecanismo abstrato útil para a criação e controle de espaços de nomes3.
Em sua forma básica, um módulo oferece ao programador a capacidade de dividir o es-
paço de nome em duas partes: pública e privada. A parte pública é acessível a todas as
pessoas, enquanto que a parte privada somente é acessível dentro do módulo. Variáveis
(dados), funções, procedimentos e tipos podem todos ser definidos em qualquer parte do
módulo. Esse mecanismo abstrato foi popularizado por David Parnas, que propôs as se-
guintes diretrizes para a utilização de módulos:
1. O projetista do módulo deve proporcionar aos futuros usuários todas as infor-
mações necessárias para o seu uso correto, e nada mais.
2. O projetista do módulo deve proporcionar ao implementador todas as informa-
ções necessárias para completar (codificar) o módulo e nada mais.
Essas diretrizes são similares ao modo como os militares tratam de documentos se-
cretos por meio da orientação “necessidade de saber”. Caso você não precise conhecer
uma informação, não terá acesso a ela. Essa dissimulação explícita e intencional da infor-
mação é a ocultação de informações, que é o segundo princípio do paradigma orientado
a objeto.4

3. Um espaço de nome (name space) é um mecanismo para o controle de visibilidade e de nomes de


elementos de programas (por exemplo, função, atributo e nomes de classes).
4. Os princípios do paradigma orientado a objeto serão discutidos no próximo capítulo.
CAP. 2 ADMINISTRANDO A COMPLEXIDADE: ANÁLISE E DESENHO 11

O módulo, como um mecanismo abstrato, resolve nosso problema de ocultação de


informações. Agora podemos esconder os detalhes da pilha. Note que um módulo reforça
os dois princípios iniciais do paradigma orientado a objeto, ou seja: encapsulamento e
ocultação de informações. Quando um mecanismo procede dessa forma, ele isola uma
parte do sistema — por exemplo, o módulo — de todas as outras partes do sistema.
Quando isso é feito, nós aumentamos a manutenção do software gerado, porque o isola-
mento permite que o código seja modificado ou estendido e que bugs sejam fixados, sem
o risco de introduzirmos efeitos colaterais desnecessários ou involuntários.
Entretanto, o módulo apresenta uma deficiência muito grande. Examinaremos o
problema da pilha novamente. O que aconteceria se um usuário quisesse utilizar duas ou
mais pilhas? Não podemos tratar essa questão com um módulo. Na condição de um me-
canismo, o módulo não nos possibilita realizar o instanciamento (instantiation), que é a
habilidade de fazer múltiplas cópias das áreas de dados. Essa idéia de instanciamento é
o sexto princípio do paradigma orientado a objeto.
Para um melhor exemplo do porquê do instanciamento ser um recurso importante
e desejado, considere a seguinte situação: precisamos desenvolver um novo tipo de nú-
mero denominado complexo. Nós definimos as operações aritméticas (adição, subtração,
multiplicação etc.) para funções e números complexos para converter um número con-
vencional (integral, fracionário, duplo etc.) em um número complexo. Se estivéssemos
propensos a utilizar módulos como forma de capturar nosso novo tipo de número com-
plexo, teríamos um pequeno problema — poderíamos manipular apenas um número
complexo de cada vez. Um sistema de números complexos com uma restrição desse tipo
não seria muito proveitoso.

Tipos de Dados Abstratos


O tipo de dado abstrato é um tipo de dado definido pelo programador que pode ser ma-
nipulado de forma similar aos tipos de dados predefinidos das linguagens de programa-
ção. Semelhante a um tipo de dado predefinido, um tipo de dado abstrato corresponde a
um conjunto (talvez um conjunto praticamente infinito) de valores de dados legais e uma
série de funções que podem desempenhar nestes valores. Os programadores podem criar
instâncias deste tipo de dado abstrato atribuindo valores legais às variáveis. Além disso,
podem-se utilizar as funções para manipular os valores atribuídos a essas variáveis. Em
resumo, um mecanismo do tipo de dado abstrato deve ser capaz de:
1. Estender uma linguagem de programação pela adição de um tipo(s) definido(s)
pelo programador.
2. Disponibilizar a outro código um conjunto de funções definidas pelo programa-
dor que são utilizadas para manipular os dados de instância do tipo específico
definido por ele.
3. Proteger (ocultar) os dados de instância associados com o tipo e limitar o acesso
aos dados unicamente às funções definidas pelo programador.
4. Criar instâncias (ilimitadas) do tipo definido pelo programador.
Os módulos, conforme já definido, podem lidar diretamente só com os itens 2 e 3.
Com aptidões apropriadas de programação, alguns dos outros recursos também podem
ser tratados. Entretanto, os pacotes encontrados em certas linguagens, como é o caso da
Ada e CLU, são exemplos muito melhores de uma implementação do mecanismo do tipo
de dado abstrato.
12 UML E C++ CAP. 2

Portanto, podemos solucionar nosso problema de geração para a pilha e números


complexos utilizando o mecanismo do tipo de dado abstrato.

Classes/Objetos
Muitas pessoas consideram o Smalltalk uma “linguagem orientada a objeto”. Elas definiriam
uma classe como um diferente nome para um tipo de dado abstrato, e um objeto como uma
instância de um tipo de dado abstrato. Tecnicamente, é possível discutir que elas estejam cor-
retas; entretanto, nossa opinião é que essa definição não considera a parte mais importante
da programação orientada a objeto. Nos dias de hoje, a programação orientada a objeto tem-
se expandido baseada na idéia dos tipos de dados abstratos e acrescentado a eles inovações
muito importantes para melhor administrarmos a complexidade. São essas inovações com-
plementares que conferem à tecnologia orientada a objeto todo o seu poder.

Passagem de Mensagens
A programação orientada a objeto acrescentou diversas idéias novas ao conceito de tipos
de dados abstratos. Primeiro é a idéia da passagem de mensagens (message passing). Em
programação orientada a objeto, uma ação é iniciada por uma solicitação de serviço
(mensagem) enviada a um objeto específico, distintamente da programação imperativa
em que há uma chamada de função (function call) que se utiliza de dados específicos. Su-
perficialmente, essa diferenciação pode parecer somente uma mudança de ênfase. O estilo
de programação imperativa dá primariamente importância ao objeto (valor). Por exem-
plo, você requisita a função acrescentar (push) com uma pilha e um valor de dado, ou so-
licita para que a pilha adicione um valor de dado sobre ela própria?
Se todo esse processo correspondesse à passagem de mensagens, ele não seria um
mecanismo muito poderoso. Entretanto, esse processo nos fornece a habilidade de sobre-
por nomes e reutilizar software; o que não é possível utilizando-se o estilo de chamada
de função típico da programação imperativa. Implícita na passagem de mensagens está
a idéia de que a interpretação de uma mensagem pode variar com objetos em classes di-
ferentes. Ou seja, o comportamento (resposta que uma mensagem irá provocar) depende
da classe do objeto que recebe a mensagem. Portanto, nossa função acrescentar (push)
pode significar algo para nosso objeto pilha, mas uma coisa muito diferente para um ou-
tro objeto — um objeto de braço mecânico, por exemplo.
Em programação orientada a objeto, os nomes das funções são exclusivos somente
dentro de uma classe. Podemos usar nomes simples, diretos e representativos para nossas
funções ao longo das classes, originando códigos mais fáceis de serem lidos e entendidos.
Isso também proporciona uma melhor coesão no que tange a implementação em compa-
ração à linguagem de programação imperativa tradicional.

Generalização/Especialização e Polimorfismo
Além da passagem de mensagens, a programação orientada a objeto adicionou mais dois
mecanismos aos tipos de dados abstratos: generalização/especialização5 e polimorfismo.

5. A generalização/especialização é implementada pelo uso de herança em muitas linguagens


orientadas a objeto, inclusive C++. Se isso ajudar, é possível considerar os dois termos como
sinônimos. Entretanto, no capítulo do desenho (Capítulo 12), discutiremos herança como um
conceito diferente.
CAP. 2 ADMINISTRANDO A COMPLEXIDADE: ANÁLISE E DESENHO 13

A generalização/especialização possibilita às classes compartilharem o mesmo código.


Isso reduz o tamanho do código e provê ao software uma manutenção mais fácil. Além
disso, ajuda-nos fornecendo uma boa coesão e um menor grau de acoplamento em nossa
implementação. O polimorfismo possibilita que o código compartilhado seja feito sob
medida para atender às circunstâncias específicas de cada uma das classes individuais.
Esses mecanismos trabalham em conjunto para suportar a independência (baixo grau de
acoplamento) dos componentes individuais (objetos) que suportem um processo de de-
senvolvimento incremental (boa coesão).
Tão úteis como a generalização/especialização e o polimorfismo são para que orga-
nizemos (ou estruturemos) nossas classes e objetos6, esses mecanismos colaboram somen-
te no tocante a administrarmos relacionamentos hereditários. Na realidade, nós certamente
temos relacionamentos hereditários com nossos filhos, pais, avós etc. Todavia, também
temos outros tipos de relacionamentos que precisam ser administrados. Por exemplo,
pessoas casadas têm cônjuges que não podem ser modelados utilizando-se generaliza-
ção/especialização e/ou polimorfismo (muito embora muitas pessoas casadas talvez de-
sejassem poder “polimorfar” sua outra metade). De mais a mais, na maioria das
organizações, as pessoas trabalham em equipes para conseguir realizar o trabalho, e há
uma estrutura hierárquica de pessoas que constitui a organização. Novamente, essas es-
truturas não podem ser modeladas pela utilização dos poucos mecanismos discutidos.
Portanto, existe uma necessidade de mais mecanismos que possibilitem ao paradigma
orientado a objeto modelar corretamente nossa percepção da realidade.

Novos Relacionamentos
Na seção anterior fomos apresentados a um tipo de relacionamento; a saber, generaliza-
ção/especialização. Entretanto, esse tipo de relacionamento não é adequado para que
possamos modelar nossa percepção da realidade. Por exemplo, não podemos capturar o
relacionamento existente em casamentos ou o relacionamento encontrado em supervi-
sor/subordinado utilizando o princípio de generalização/especialização. Em vez disso,
necessitamos de novos mecanismos que nos auxiliem a modelar esses conceitos.

Associações
Examinemos uma instância de casamento mais minuciosamente. Suponha que Joe seja
casado com Jane. Da perspectiva de Joe, o relacionamento matrimonial captura o fato de
que Jane é sua esposa e provê um conjunto de serviços “próprios de esposa” a ele. Simi-
larmente, da perspectiva de Jane, o relacionamento matrimonial captura o fato de que Joe
é seu marido e provê um conjunto de serviços “próprios de marido” a ela. Portanto, é
muito comum termos nomes que representam papéis (marido, esposa) associados a um
relacionamento (matrimônio). De certo modo, esses nomes nos ajudam a definir que ser-
viços espera-se sejam acessíveis por meio de tal relacionamento. Por exemplo, se Jane
também é supervisora de Joe no trabalho, um segundo relacionamento, supervisor/su-
bordinado, deverá ser estabelecido para capturar os serviços “próprios de subordinado”
de Joe. Esse segundo relacionamento é necessário para manter a consistência semântica
do relacionamento. Joe pode cessar de ser subordinado de Jane no trabalho e continuar
casado com ela. Esse tipo de relacionamento, em que um objeto tem conhecimento de um
outro objeto para serviços específicos, é denominado vínculo (link).

6. Portanto, eles também nos ajudarão na administração da complexidade.


14 UML E C++ CAP. 2

Se definirmos o casamento como algo somente entre duas pessoas de sexos opostos,
então necessitamos classificar as pessoas em duas categorias (classes): Masculino e Femi-
nino. Note que Joe, um objeto, é uma instância de Masculino e que Jane, outro objeto, é
uma instância de Feminino. Agora, uma vez que todos os vínculos matrimoniais são en-
tre um objeto da classe Masculino e um outro objeto da classe Feminino, podemos cap-
turar todos os vínculos matrimoniais utilizando um conceito de nível mais alto
denominado de associação (association). Uma associação descreve um grupo de vínculos
com estrutura e semântica comuns. Todos os vínculos em uma associação devem conec-
tar objetos da mesma classe a objetos de uma segunda classe. Note que, da perspectiva
orientada a objeto, a semântica comum significa que os serviços prestados por cada objeto
na mesma classe são os mesmos. Ainda, lembre-se de que prover o mesmo serviço não
quer dizer que o comportamento será idêntico. Por exemplo, se definirmos “cortar a gra-
ma” como um dos serviços do marido, então todo objeto masculino deverá ter um serviço
de “cortar a grama”. Entretanto, o método que cada objeto masculino utiliza para cortar
a grama pode ser muito diferente (polimorfismo). Especificamente, Joe pode na verdade
cortar a grama sem a ajuda de ninguém, enquanto Jack poderá pagar a um de seus filhos
para cortar a grama e Jim poderá utilizar os serviços profissionais de uma organização
especializada em aparo e corte de gramados.
Com o relacionamento matrimonial, pode-se argumentar que não apenas o compor-
tamento (a forma como o serviço é prestado) é diferente, mas também os serviços provi-
dos por cada parceiro são diferentes para cada casamento. Isso, certamente, é mais
provável de acontecer nos dias de hoje do que na época em que as pessoas tinham esta-
belecido conceitos associados aos papéis de marido e esposa. Se isso for correto, não se-
ríamos capazes de abstrair cada um dos vínculos matrimoniais em um grupo; iríamos
requerer um modelo padrão para cada grupo de vínculos com diferentes serviços. Ou
seja, cada casamento seria uma associação diferente.
As associações são bidirecionais; assim, é comum dar-se um nome a uma associação
para cada direção. Em nosso exemplo do casamento, da direção do Feminino é o marido,
e da direção do Masculino é a esposa. A propósito, é prática comum nomear um relacio-
namento com o mesmo nome da classe com a qual ele está associado. Essa prática não é
recomendada, pois não captura nenhum significado semântico (fraca coesão). Isso é efe-
tivo apenas nos casos em que haja uma associação entre as classes; se houver mais do que
uma associação deverá ser usada uma convenção melhor. Por exemplo, considere o rela-
cionamento entre uma pessoa e uma organização. É muito comum para uma pessoa ser
tanto um funcionário como um acionista de uma organização, particularmente no presen-
te com o aumento do número de organizações de propriedade de funcionários. Para mo-
delarmos esses dois relacionamentos com precisão, criaremos duas associações entre a
classe Pessoa e a classe Organização. Uma associação representará o relacionamento em-
pregador/funcionário e a outra o relacionamento de posse.
Na teoria, as associações podem ser binárias (entre duas classes), ternárias (entre
três classes) ou de maior grau. Na prática, entretanto, a grande maioria das associações é
binária ou ternária; muito raramente temos utilizado as outras formas. As associações de
ordem mais alta são muito difíceis de serem desenhadas, implementadas e pensadas; elas
devem ser evitadas, se possível.
A noção de associação não é um conceito novo; ela tem sido amplamente utilizada
durante anos em modelagem de bancos de dados. Entretanto, bem poucas linguagens de
programação suportam explicitamente o mecanismo de associação. Não obstante, acredi-
CAP. 2 ADMINISTRANDO A COMPLEXIDADE: ANÁLISE E DESENHO 15

tamos que deveríamos modelar uma associação como uma abstração em separado con-
tendo informação que dependa de duas ou mais classes em vez de uma única classe.
Algumas pessoas poderiam argumentar que capturar uma associação como uma
abstração em separado viola o princípio de encapsulamento de uma classe. Todavia, há
informação de que necessitamos modelar o que naturalmente transcende uma classe in-
dividual. No relacionamento do casamento, por exemplo, a data de casamento e a igreja
onde foi realizada a cerimônia são exemplos de informação que naturalmente transcen-
dem a classe Feminino ou Masculino.
A captura imprecisa de uma classe fará com que os programas contenham hipóteses
e dependências ocultas que os tornarão de difícil manutenção. Por exemplo, em nosso
caso do relacionamento empregador/funcionário, o salário de uma pessoa não é realmen-
te informação sobre uma Pessoa. Considere o caso em que seja preciso modelar uma pes-
soa que tenha dois ou três empregos com diferentes empregadores.
De maneira conceitual e, particularmente, durante a análise, recomendamos que um
vínculo seja tratado como um objeto. Pelo fato de um objeto ter atributos (ou seja, poder
armazenar informações) e prover serviços, um vínculo também tem o potencial de arma-
zenar dados e prover serviços. Em nosso relacionamento do casamento, por exemplo, os
atributos poderiam ser a data de casamento e a igreja onde foi realizada a cerimônia. Em-
bora associações (e agregações) não sejam diretamente suportadas pelas linguagens de
programação atuais, nosso objetivo é o de demonstrar a utilidade destes mecanismos e
incentivar o desenvolvimento de linguagens de programação que os suportem para que
nos auxiliem na administração da complexidade.

Agregação
A partir dos exemplos precedentes, podemos constatar que a associação é uma relação
muito fraca. As pessoas normalmente mudam de funcionários. De fato, a gerência discute
sobre pessoas que necessitam mudar de empregos e de funcionários várias vezes durante
o período de atividade de suas carreiras. Além do mais, o casamento nos Estados Unidos
certamente não é algo permanente, conforme evidenciado pelos seguidos casamentos e
por pais divorciados solteiros.7 Portanto, uma associação é como um “casamento holly-
woodiano” — ela pode ser modificada muito rapidamente.
Mas existem outros relacionamentos, como um carro com suas peças ou uma ordem
de compra com seus itens de linha, que não possibilitam mudanças tão facilmente. Além
do mais, essas formas especializadas de associação apresentam propriedades especiais.
Gostaríamos de ter outro mecanismo de relacionamento para capturar essas formas mais
especializadas de associação porque precisamos atender a propriedades especiais. O
novo mecanismo é chamado agregação (aggregation).8 Nesse mecanismo, um objeto que
representa o todo é associado a um conjunto de objetos que representa seus componentes.

7. Na maioria das situações, modelaríamos o casamento como uma associação. Em algumas


sociedades, o casamento é permanente; não havendo divórcio.
8. Isto é também chamado de relacionamento “todo-partes”, “conjunto-partes” ou “parte-de”. Em
termos de um mecanismo de modelagem, podemos definir uma agregação como o relacionamento
de uma classe de conjuntos a um conjunto de classes de componentes. A agregação reduz a
complexidade, permitindo que tratemos muitos objetos como um objeto, e nos proporciona um
melhor mecanismo do que um vínculo para a modelagem de entidades específicas de domínio (por
exemplo, pedido de compra, carros, montagens).
16 UML E C++ CAP. 2

Um bom exemplo de relacionamento que, provavelmente, é melhor modelado por uma


agregação é o de uma relação de materiais e todos os itens de linha associados.
A agregação contém as seguintes propriedades:
Transitividade. Transitividade é a propriedade que denota que se um objeto A é
parte do objeto B e o objeto B é parte do objeto C, por conseguinte o objeto A é uma
parte do objeto C. Em nosso exemplo, considere a possibilidade de que um item de
linha possa ter itens de sublinha. Pela transitividade, os itens de sublinha são tam-
bém partes da relação de materiais.
Antissimetricidade. Antissimetricidade é a propriedade que denota que se um ob-
jeto A é parte do objeto B, por conseguinte o objeto B não pode ser uma parte do
objeto A. Mais uma vez, utilizando nosso exemplo, um item de linha não pode ser
parte de um item de sublinha.
Finalmente, alguns dos atributos e/ou métodos de montagem normalmente se pro-
pagam também para seus componentes. Em nosso exemplo, a relação de materiais nor-
malmente tem um número de pedido que é também utilizado por todos os itens de linha
e de sublinha.
Há pelo menos duas dificuldades no tratamento de agregações. Primeiro, os com-
ponentes devem ser todos do mesmo domínio semântico.9 Por exemplo, um terminal de
computador é composto de tubo de raios catódicos, teclado, cabos etc. Entretanto, um ter-
minal é também composto de vidro, silício, aço e plástico. A decomposição “made_of” do
terminal não é igual à decomposição “composed_of ” ; seria incorreto misturar os compo-
nentes nessas duas decomposições. A segunda dificuldade tem a ver com os “componen-
tes opcionais”. Por exemplo, um carro normalmente tem maçanetas de porta como parte
do seu todo. Se todas as maçanetas fossem removidas, o carro ainda seria um carro? Ago-
ra precisamos ser cuidadosos; parece que a retirada das maçanetas não eliminaria a exis-
tência do carro. Todavia, é verdadeiro que as maçanetas de porta são opcionais, mas o
que dizer dos pneus? A lógica pode então ser estendida para incluir todas as peças de
um carro. Mas se todas as peças forem opcionais, por conseguinte um carro poderá existir
isento de peças. Veremos que possibilitaremos somente uma certa parcela de flexibilidade
quando definirmos a agregação utilizando componentes condicionais.

Comportamento
Os mecanismos de relacionamento propiciam um modo de organizarmos classes/objetos
tanto nas estruturas par-a-par (associação) como nas hierárquicas (generalização/especi-
ficação e agregação). Esse trecho estrutural do modelo é chamado de modelo estático por
muitos especialistas orientados a objeto. Nós preferiríamos utilizar o termo de James Mar-
tin e de James Odell, e denominá-la de análise estrutural.
Entretanto, uma análise estrutural de uma aplicação/sistema não é adequada; nós
também necessitamos fazer uma análise comportamental. A análise comportamental é o
processo que utilizamos para verificar como cada objeto (classe) provê seus serviços (ou
seja, os métodos).

9. Em um capítulo posterior, veremos como é possível identificar essa situação pela identificação dos
diferentes tipos de agregações.
CAP. 2 ADMINISTRANDO A COMPLEXIDADE: ANÁLISE E DESENHO 17

Com o mecanismo classe/objeto, criamos os blocos de construção conceituais de


nosso modelo. O mecanismo de classe, como um esquema, define a estrutura de dados e
provê um índice para as funções do sistema. Os serviços (funções e procedimentos) estão
presos a uma classe específica, e um objeto (bem como seus dados associados) somente
pode ser manipulado pelas funções associadas com a classe da qual o objeto é uma ins-
tância. Sem classes e objetos não podemos definir dados nem utilizar quaisquer métodos
(código).
Quando especificamos “como um serviço é prestado”, estamos definindo “como
uma classe de objetos executará aquele serviço”. Partindo-se de uma perspectiva de aná-
lise, há dois tipos de comportamento: estático e dinâmico.

Comportamento Estático
No comportamento estático, a operação (código) dentro do método não será afetada por
quaisquer eventos externos ou internos (ações). Um bom exemplo de comportamento es-
tático é o do serviço de “raiz quadrada” de Número. Se alguém solicitar o serviço de “raiz
quadrada” do número 4, que é uma instância de Número, o resultado será sempre 2. Nos
dias de hoje é 2. Nenhuma ação externa ou interna poderá fazer com que o método de
Número mude o resultado no cálculo da raiz quadrada. Se, de fato, somente existissem
métodos estáticos, nós teríamos um modelo muito bom, administrando unicamente o tre-
cho estrutural do modelo. Na verdade, escrever o método (código) seria fácil, pois esta-
ríamos utilizando as mesmas técnicas usadas na programação imperativa.

Comportamento Dinâmico
Se todos os comportamentos no mundo fossem estáticos, ele não seria interessante. Feliz-
mente, vivemos em um mundo dinâmico. Por exemplo, veja como um agente de emprés-
timos responderá à sua pergunta, “Qual é a taxa preferencial de juros para empréstimos?”.
A resposta a essa questão pode mudar praticamente de hora em hora. Similarmente, veja
como o funcionário do balcão de reservas de uma companhia aérea responderia a sua
questão, “Qual é a passagem mais barata de Nova Iorque a San Francisco para 15 de ja-
neiro?”. Essa resposta poderá mudar enquanto você estiver tomando uma decisão pelo
telefone. Esses são dois exemplos de comportamento dinâmico. As razões para essas mu-
danças no comportamento podem ser capturadas deixando-se um objeto existir em mui-
tos estados diferentes. Portanto, a resposta de um objeto pode ser baseada em seu estado.
Esse tipo de comportamento não é controlado muito bem utilizando-se a técnica de pro-
gramação imperativa. Esses métodos são melhor capturados utilizando-se um outro me-
canismo, denominado máquina de estado finita.
Para melhor entendermos o conceito de estados, examinemos o processo de reserva
em um sistema de reservas de uma companhia aérea. Quando entra um pedido, uma re-
serva é criada e movida para o estado “solicitado”. Enquanto nesse estado, se existirem
assentos vagos, a reserva poderá ser confirmada; entretanto, se não existirem assentos va-
gos essa reserva será colocada na “lista de espera”. Quando uma reserva confirmada é
cancelada, ela é movida para o estado “cancelado”. Se a pessoa apresenta-se e voa no
tempo determinado, a reserva é movida para o estado “utilizado”, e quando o avião ater-
rissa ela é movida para o estado “arquivado”. Um cenário similar é mantido para a lista
de espera. Dessa maneira, o objeto Reserva poderia ter os seguintes estados: Solicitado,
Lista de Espera, Confirmado, Cancelado, Utilizado e Arquivado. Uma mudança de esta-
do poderia ocorrer quando fosse solicitado um serviço do objeto. Quando um método as-
sociado a um serviço muda o estado do objeto, o estado é registrado no trecho de dados
18 UML E C++ CAP. 2

do objeto. Normalmente, há um conjunto finito de seqüências de mudanças de estado


permitido a um objeto. O conjunto completo de seqüências é chamado de ciclo de vida
do objeto. Como todas as seqüências possíveis de mudanças de estado são normalmente
programadas com um mecanismo de máquina de estado finita, necessitamos de uma ma-
neira de capturar essa informação sob uma forma gráfica. Um mecanismo desse tipo é o
“diagrama de cerca”, que revela o ciclo de vida do objeto Reserva.

FIGURA 2.1 Diagrama de cerca para o objeto Reserva.

Esse diagrama de cerca exibe o estado e as transições (movimento de um estado


para outro) possíveis. Entretanto, ele não mostra o que causará no objeto uma mudança
de comportamento ou de estado, nem que ação (operações realizadas) é tomada quando
a mudança de estado é reconhecida. Um melhor modelo de capturar esses dois novos as-
pectos do comportamento dinâmico é um diagrama de transição de estado. Na Unified
Modeling Language (UML, Linguagem de Modelagem Unificada), um diagrama de esta-
do revela (1) estado, (2) transição, (3) evento, (4) condição e (5) ação. A descrição completa
dos componentes mais importantes de um diagrama de transição de estado é apresentada
a seguir:
Estado: Modo de comportamento do objeto.
Transição: Representa o movimento de um estado para outro. A transição logica-
mente é modelada como não tomadora de tempo (ou seja, o objeto move-se de um
estado para outro instantaneamente). Uma transição tem duas partes: condição de
transição e ação de transição.
Evento: Tecnicamente, um evento é uma ocorrência em um ponto com o tempo.
Cada evento representa um incidente ou uma indicação de que está para ocorrer
uma progressão. Um evento pode incorporar argumentos que mais à frente definem o
evento.
Condição: Quando uma condição de transição é satisfeita, o objeto move-se de um
estado para outro. Uma condição pode ser o recebimento de um sinal externo/in-
terno, o alcance de um valor absoluto ou relativo de uma variável ou o recebimento
de um sinal de tempo. A condição é também conhecida como guarda (guard).
Ação: Operação (algoritmo) executada pelo objeto quando ele se move de um esta-
do para outro. As ações podem incluir enviar um sinal para outro objeto e desenca-
dear uma transformação.
A Figura 2.2 ilustra um modelo para um diagrama de transição de estado. Na Figu-
ra 2.3, mostramos um diagrama de transição de estado para o objeto Reserva.
CAP. 2 ADMINISTRANDO A COMPLEXIDADE: ANÁLISE E DESENHO 19

FIGURA 2.2 Diagrama de transição de estado.

FIGURA 2.3 Diagrama de transição de estado para o objeto Reserva.

A UML tem adicionado um pouco de semântica extra ao modelo dinâmico, que


aborda (1) tratamento de interrupções (estado da história), (2) operações em andamento
dentro de um estado que leva tempo para completar (atividades), (3) restrições de regu-
lagem (marca de regulagem), (4) processos em encadeamento (tarefas) e (5) sincronização.
Esses novos elementos serão discutidos em um capítulo posterior.

REGRAS
Com a adição de relacionamentos (herança, agregação e associação), a tecnologia orien-
tada a objeto acrescentou alguns mecanismos poderosos para especificar a semântica de
20 UML E C++ CAP. 2

dados de qualquer domínio de aplicação. O uso de técnicas clássicas para especificar o


comportamento estático e de máquinas de estado finitas para especificar o comportamen-
to dinâmico correspondem a mecanismos poderosos para a captura da semântica proce-
dural. Entretanto, uma das deficiências dos métodos orientados a objeto atuais é a falta
de mecanismos que suportem a semântica declarativa. Na maioria dos métodos orienta-
dos a objeto, a semântica declarativa (ou seja, regras) é deixada para a inventividade do
analista/desenvolvedor. Supondo-se que o propósito do novo método é auxiliar-nos a
administrar a complexidade proporcionando mecanismos que modelem a semântica
completa do domínio do problema, necessitamos então incorporar mecanismos que li-
dem com regras (ou semântica declarativa) para nosso repertório.
Para os novatos, a semântica declarativa aborda os temas de descrição de controle
global e de regras de negócio. Pelo fato de a maioria das semânticas declarativas ser ex-
plicitamente especificada em regras, nós focaremos o tópico das regras. Os tipos de regras
que estamos interessados em capturar são (1) regras de controle, (2) regras de negócio, (3) re-
gras de tratamento de exceções, (4) regras de contenção e (5) regras desencadeadoras.
Em um capítulo posterior, examinaremos uma nova linguagem, R++, que estende
C++ para fornecer-nos alguns dos recursos requeridos para manusear certos tipos de re-
gras que acabamos de relacionar. Em certo sentido, R++ estende C++ para proporcionar
os mecanismos extras necessários para o suporte da semântica funcional plena (ou seja,
suporte para as semânticas declarativa e de procedimento).

Sistemas Complexos
Acabamos de apresentar os mecanismos mais importantes disponíveis no paradigma
orientado a objeto para administrarmos a complexidade. Existem outros mecanismos que
são mais adequados a um livro orientado a objeto avançado, do qual temos vários exem-
plos encontrados na atualidade no mercado.
Antes de podermos determinar se um paradigma ajuda a administrar a complexi-
dade mais do que os outros paradigmas, devemos primeiramente entender as caracterís-
ticas (atributos) de aplicações ou sistemas complexos. Estudos identificaram cinco
atributos-chave para aplicações ou sistemas complexos. Eles são os seguintes:
■ Os sistemas complexos assumem a forma de uma hierarquia. Um sistema complexo
é composto de subsistemas inter-relacionados que possuem seus próprios subsiste-
mas até o ponto em que algum nível mais baixo de componentes elementares é
atingido.
■ A escolha de quais componentes em um sistema são primitivos é relativamente ar-
bitrária e amplamente dependente da vontade do observador do sistema.
■ Ligações de intracomponentes geralmente são mais fortes do que ligações de inter-
componentes.
■ Os sistemas hierárquicos, em geral, são compostos de somente poucos diferentes ti-
pos de subsistemas em várias combinações e arranjos.
■ Constata-se, invariavelmente, que um sistema complexo que funcione é quase sem-
pre desenvolvido a partir de um sistema simples que obteve bom desempenho.
Um bom exemplo de sistema complexo é o ser humano. Comparemos os mecanis-
mos disponíveis no paradigma orientado a objeto com os mecanismos que um biólogo
utiliza para analisar um ser humano. Na biologia, o bloco de construção (componente pri-
mitivo) é a célula, que é composta de uma membrana, do citoplasma e dos núcleos.
CAP. 2 ADMINISTRANDO A COMPLEXIDADE: ANÁLISE E DESENHO 21

Similarmente, no paradigma orientado a objeto, o componente primitivo é o objeto, que


tem dois subcomponentes: dados e funções. As células são agrupadas para formar ór-
gãos; similarmente, os objetos são reunidos por intermédio de relacionamentos para for-
mar subsistemas. Os órgãos são organizados sob alguma forma hierárquica para formar
sistemas biológicos; os subsistemas são reunidos por meio de vários relacionamentos
para formar sistemas/aplicações.
Em um ser humano, a “célula” e o “órgão” se comportam de uma maneira estática
(os mesmos em todo o tempo) e dinâmica (não necessariamente os mesmos). A tecnologia
orientada a objeto inclui técnicas para capturar o comportamento dinâmico de um objeto
e de um subsistema. Além do mais, um ser humano pode, de fato, mudar seu comporta-
mento por completo realmente provendo diferentes serviços. Similarmente, na tecnologia
orientada a objeto, existem técnicas/mecanismos para migrar um objeto de um tipo para
outro. Além disso, técnicas/mecanismos complementares ainda estão sendo desenvolvi-
dos para suportar outros conceitos necessários à modelagem de nossa percepção da
realidade.

■■ RESUMO
Inicialmente, o desenvolvimento de software era um empreendimento artístico que ba-
seava-se excessivamente nas aptidões e habilidades técnicas do indivíduo. À medida que
o software cresceu em complexidade, esse enfoque livre e improvisado não podia mais
ser aceito. A introdução de linguagens de alto nível ajudou na resolução de alguns dos
problemas associados com a complexidade, mas essas linguagens não eram o suficiente.
O aumento de mais formalidade no desenvolvimento de software era suportado pela in-
trodução de métodos formais:
■ Programação estruturada
■ Programação modular
■ Programação imperativa
■ Programação orientada a objeto
Como resultado dessas principais tendências, os desenvolvedores modernos tinham
vários mecanismos de abstração em suas caixas de ferramentas que poderiam usar quan-
do necessário. Esses mecanismos compreendiam:
■ Funções e procedimentos
■ Módulos
■ Tipos de dados abstratos
■ Classes e objetos
Classes/objetos é simplesmente o mecanismo mais recente e, certamente, não será
o último.
O mecanismo de abstração de classes/objetos tem vários conceitos-chave associados
a ele:
1. Passagem de mensagens. Uma ação é iniciada por uma solicitação de serviço
(mensagem) enviada ao objeto específico.
2. Generalização/especialização. Permite às classes compartilhar o mesmo código.
3. Polimorfismo. Permite que o código compartilhado seja personalizado à classe
específica.
22 UML E C++ CAP. 2

4. Relacionamentos. Mecanismo pelo qual os objetos se conhecem mutuamente.


■ Associações. Um relacionamento entre objetos.
■ Agregações. Um objeto é composto de outros objetos.
5. Comportamento. O serviço que um objeto provê.
■ Comportamento estático. Comportamento independente do estado atual do
objeto.
■ Comportamento dinâmico. Comportamento que difere segundo o estado do
objeto.
6. Regras. Um mecanismo declarativo para tratar dos tópicos da descrição de con-
trole global e das regras de negócio.
Programação Orientada
a Objeto
PROGRAMAÇÃO ORIENTADA A OBJETO
3.Programação Orientada a Objeto

E stamos ouvindo clamores desesperados por uma solução genial — algo


que fizesse os custos de software caírem tão rapidamente quanto os custos
relativos ao hardware de computadores. Não há sequer um desenvolvimento
em particular, em alguma tecnologia ou em técnica de administração, que che-
gue a prometer nem que sejam aperfeiçoamentos de uma ordem de magnitude
em produtividade, confiabilidade e simplicidade.

F. Brooks Jr., The Silver Bullet,


Essence and Accidents of Software Engineering

S oftware confiável, flexível e de fácil manutenção é difícil de ser criado. Os sistemas de


software são complexos e, conforme sugerido por Brooks, a complexidade é uma par-
te da essência do sistema. Nenhum processo de abstração consegue eliminar a complexi-
dade na sua totalidade. Entretanto, acreditamos que podemos criar mecanismos que nos
ajudem a administrar essas complexidades. Além disso, cremos que algumas dificuldades
não são “acidentes”; elas surgem como conseqüência do modo como o software é cons-
truído. Temos convicção de que se mudarmos a forma como construímos o software ire-
mos melhorar essas pretensas dificuldades “acidentais”.

23
24 UML E C++ CAP. 3

O Que É Programação Orientada a Objeto?


Não É a Solução Mágica
Programar um computador é, e sempre será, uma das tarefas mais difíceis jamais em-
preendidas pelos seres humanos. Mesmo com o advento de muitas ferramentas, um pro-
gramador proficiente deve ter inteligência, lógica, a habilidade de encontrar e utilizar
abstrações, experiência e criatividade. A programação orientada a objeto não é a “solução
mágica” ou o “cálice sagrado” que os administradores das organizações de desenvolvi-
mento de software estão procurando. Todavia, a programação orientada a objeto é mais
do que simplesmente uma coleção de novos recursos acrescentados a uma linguagem de
programação já existente; ela é um paradigma avançado.

Paradigma Avançado
A programação orientada a objeto é um paradigma de programação avançado. Paradig-
ma é definido como um “conjunto de teorias, padrões e métodos que, juntos, representam
um modo de organizar conhecimento”. Essa é a definição desenvolvida do termo propos-
to no livro de Thomas Kuhn, The Structure of Scientific Revolution. Desenvolvedores têm
utilizado outros paradigmas tais como a programação imperativa (C, Pascal, Cobol, Ada),
programação lógica (Prolog, C5) e programação funcional (FP, ML).
A linguagem de programação que utilizamos influencia diretamente o modo como
visualizamos (modelamos) a realidade. Nos anos 70, quando estávamos utilizando lin-
guagens de programação como C, Pascal e PL/1, usávamos um paradigma de programa-
ção imperativa para modelar a realidade — o método estruturado. Nos anos 80, quando
estávamos utilizando SQL e 4GL com bancos de dados relacionais, usávamos um para-
digma de modelagem de dados para modelar a realidade — diagramas de entidade e re-
lacionamento. Nos dias atuais, programamos utilizando C++, Java, Smalltalk e Objective
C. Utilizamos o paradigma orientado a objeto para modelar a realidade.
Para um compilador, a diferença entre uma linguagem imperativa como C e uma
linguagem orientada a objeto como C++ é apenas a adição de algumas palavras-chave e
tipos de dados. Entretanto, efetuar uma utilização eficiente dessas facilidades requer que
os desenvolvedores mudem suas percepções para um enfoque totalmente diferente para
fins de modelagem e resolução de problemas. A programação orientada a objeto é uma
nova maneira de pensar sobre o que significa fazer cálculos, como organizamos nossas
informações dentro de um sistema de computador e de que forma descrevemos nossa vi-
são (modelo) da realidade.

Conceitos Básicos da Programação Orientada a Objeto


O conceito/mecanismo mais fundamental da programação orientada a objeto é o de ob-
jeto. Como este livro é destinado a desenvolvedores, definiremos um objeto como uma
unidade de software que consiste de atributos (dados) e de métodos (código) que atuam
sobre aqueles dados. Os dados não são diretamente acessíveis aos usuários do objeto. O
acesso aos dados é conferido somente por meio dos métodos ou código, providos pelo
objeto (ou seja, procedimentos de chamada de seus métodos), conforme mostrado na
Figura 3.1. Isso define o primeiro princípio do paradigma orientado a objeto — encap-
sulamento.
CAP. 3 PROGRAMAÇÃO ORIENTADA A OBJETO 25

FIGURA 3.1 Objeto apresentando dados encapsulados por funções.

Encapsulamento. O objeto contém tanto os dados como os métodos (código) que


manipularão ou transformarão esses dados.
Os serviços de um objeto definem como outros objetos ganham acesso a seus méto-
dos. Cada objeto anuncia os serviços públicos que está disposto a prestar a todos os ob-
jetos. Ele também provê outros serviços (protegidos e privados), restritos unicamente a
outros objetos específicos. Discutiremos esse ponto com maior profundidade mais adian-
te neste livro. A idéia de prover serviços define o segundo princípio do paradigma orien-
tado a objeto — ocultação de informações.
Ocultação de Informações. O objeto que contém os atributos (dados) define quais
serviços (funções) estão disponíveis para os outros objetos. Na realidade, os outros
objetos não têm nem acesso, nem conhecimento dos dados (atributos) ou de que for-
ma o serviço é provido pelo método (código).
Definiremos um agente como um objeto que presta serviços e um cliente como um
objeto que usa um ou mais desses serviços.1 Antes que um cliente possa utilizar os ser-
viços de um agente, uma interface deverá ser definida. Essa definição de interface é cha-
mada de protótipo do serviço. O protótipo é composto de duas partes: (1) o nome do
serviço (chamado seletor por alguns especialistas) e (2) os argumentos do serviço (deno-
minado de assinatura por alguns especialistas). Cada objeto deve definir seu protótipo
para cada serviço que planeje prover. O conjunto de protótipos definidos é o protocolo
do objeto (ou, alternativamente, ele é a interface do objeto). O protocolo define como um
cliente pode invocar (ou solicitar) os serviços desse objeto.
Um bom exemplo de um objeto e sua interface é o sistema de ícones que todos nós
utilizamos em um sistema com janelas. Uma ação muito comum nesse sistema é a de se-
lecionar um ícone e, em seguida, usar um menu descendente para obtermos todos os ser-
viços selecionáveis a partir daquele ícone. Dessa maneira, em um sistema orientado a
objeto, o ícone é realmente um objeto e o menu define a interface (ou protocolo) do
objeto.
Um objeto pode utilizar o serviço público de um outro objeto por meio do mecanis-
mo (paradigma) de passagem de mensagens para enviar uma mensagem que se adapte
ao protótipo do serviço. Se o objeto 1 (o cliente) quiser utilizar um serviço do objeto 2 (o

1. O agente também pode ser chamado de servidor. Um agente poderá receber uma melhor
denominação durante a análise, pois, nesse caso, um servidor possivelmente será reservado para
indicar o objeto real que realiza o trabalho.
26 UML E C++ CAP. 3

agente), o cliente enviará uma mensagem ao agente solicitando o serviço específico deste.
Note que a mensagem deverá ser direcionada a um objeto específico e conter o nome do
serviço solicitado. Além disso, a mensagem talvez contenha informações extras (argu-
mentos) que o agente necessitará para realizar o serviço solicitado.
Por exemplo, no linguajar característico da programação, o objeto 1 executa um pro-
cedimento de chamada ao serviço (função) pertencente ao objeto 2 e passa todos os valo-
res paramétricos apropriados necessários para esse procedimento de chamada. Uma vez
definido que a passagem de mensagens é implementada por um procedimento de cha-
mada em C++, cabe perguntar em que sentido um mecanismo de passagem de mensa-
gens é diferente de um mecanismo de procedimento de chamada. Certamente que, em
ambos os casos, há uma solicitação implícita por “ação” e um conjunto de operações bem-
definidas que serão realizadas para satisfazer o pedido. Todavia, há três distinções im-
portantes.
Primeiro, em um mecanismo de passagem de mensagens, cada mensagem é envia-
da a um receptor designado (agente). No paradigma da programação imperativa, um me-
canismo de procedimento de chamada não tem nenhum receptor designado (agente).
Essa distinção suporta o encapsulamento.
Segundo, a interpretação da mensagem (método ou conjunto de operações/código
utilizado para cumprir o pedido de serviço) depende do receptor e pode variar com re-
ceptores diferentes. Essa distinção é necessária para suportar ocultação de informações e
polimorfismo, que explicaremos mais adiante neste livro. Isso leva ao terceiro princípio
do paradigma orientado a objeto — passagem de mensagens.
Passagem de Mensagens. Um objeto pode comunicar-se com outro objeto unica-
mente via mecanismo de passagem de mensagens.
Cada mensagem deve ser enviada a um receptor designado, e a interpretação da
mensagem depende desse receptor.
Terceiro, no paradigma orientado a objeto, o receptor específico de qualquer dada
mensagem normalmente não é conhecido até o tempo de execução (run-time), de modo
que a determinação de qual método invocar não pode ser feita até então. Dessa maneira,
há uma ligação posterior (late binding) entre a mensagem (pedido de serviço/procedimen-
to de chamada) e o método (trecho de código) que será utilizado para cumprir a solicita-
ção por “ação”. Isso pode ser contrastado com a ligação antecipada (early binding) (tempo
de compilação ou vinculação) entre o procedimento de chamada e o trecho de código no
paradigma da programação imperativa.
O suporte para a ligação posterior define o quarto princípio do paradigma orienta-
do a objeto — ligação posterior.
Ligação Posterior. Suporte para a habilidade de determinar o receptor específico e
seu método correspondente (código) a ser executado por uma mensagem em tempo de
execução.
Da perspectiva do cliente, é o agente que provê o serviço. É possível que o agente
realmente delegue o trabalho a um terceiro objeto. Isso leva ao quinto princípio do para-
digma orientado a objeto — delegação.
Delegação. O trabalho é transmitido, via passagem de mensagens, de um objeto
(cliente) a um outro objeto (agente) pelo fato de que, da perspectiva do cliente, o
agente tem os serviços de que o cliente necessita. O trabalho é continuamente trans-
CAP. 3 PROGRAMAÇÃO ORIENTADA A OBJETO 27

mitido até que ele atinja o objeto que tenha não só os dados como o método (código)
para realizar o serviço.

FIGURA 3.2 Passagem de mensagens entre objetos.

A delegação é às vezes referida como o perfeito princípio burocrático. Considere, por


exemplo, uma organização. O diretor-presidente do corpo diretivo envia uma solicitação de
serviço (mensagem) ao diretor-executivo de operações para construir uma nova fábrica. Da
perspectiva do diretor-presidente do corpo diretivo, é responsabilidade do diretor-executivo
de operações prover esse serviço. Entretanto, todos nós sabemos que o diretor-execu-
tivo não possui as aptidões nem o conhecimento (informação) para, na realidade, cons-
truir uma fábrica. Assim, esse último tem um método que delega o trabalho ao chefe de
projetos. Este tem um método que delega o trabalho ao engenheiro-chefe, o qual dispõe
de um quadro de funcionários para construir essa fábrica. Na verdade, o método do en-
genheiro-chefe delega tarefas específicas aos apropriados especialistas em várias discipli-
nas para a construção dessa fábrica. Estes são os engenheiros que, particularmente,
têm o conhecimento e as ferramentas necessárias para projetar a fábrica que se planeja
construir.
Neste ponto vemos aplicado o quinto princípio do paradigma orientado a objeto. O
trabalho é delegado para o objeto que dispõe da informação (dados) e das habilidades
(método) para a realização da tarefa. A parte burocrática advém do fato de que tanto o
28 UML E C++ CAP. 3

diretor-executivo de operações como o chefe de projetos anunciam o serviço “construir


uma nova fábrica”, muito embora nenhum deles tenha a informação ou as habilidades
para realizar a tarefa. Todavia, eles têm acesso a recursos (objetos) que podem realizar a
tarefa, de forma que podem tomar para si a responsabilidade pela sua realização.
O que fica delegado é a autoridade para realizar o trabalho; a responsabilidade não
pode ser delegada. Da perspectiva do diretor-presidente, é responsabilidade do diretor-
executivo de operações atender ao pedido de construir uma nova fábrica. Similarmente,
da perspectiva do diretor-executivo de operações, é responsabilidade do chefe de projetos
cumprir o pedido de construir uma nova fábrica. Quando o chefe de projetos aceita esta
solicitação, ele aceitou a responsabilidade do diretor-executivo de operações para realizar
o trabalho. Entretanto, tanto o diretor-executivo como o diretor-presidente não sabem
como será realizado o trabalho. Isso está aplicando o princípio da ocultação de informações.
Agora daremos uma olhada em alguns outros conceitos orientados a objeto. O pro-
cesso de categorizar as coisas ajuda-nos a organizar o mundo complexo em que vivemos.
Podemos fazer algumas suposições sobre um objeto que se encontra em uma particular
categoria. Se um objeto é uma instância da categoria (classe), ele se enquadrará no mode-
lo padrão para aquela categoria. Isto nos leva ao sexto princípio do paradigma orientado
a objeto — classe/instância/objeto.
Classe/Instância/Objeto. Todos os objetos são instâncias de uma classe. Instâncias
podem ser criadas (geradas) ou destruídas (eliminadas) em tempo de execução.
A forma como um objeto provê um serviço é determinada pela classe da qual o ob-
jeto é uma instância. Assim, todos os objetos da mesma classe utilizam o mesmo método
(código) em resposta a uma solicitação específica de serviço (procedimento de chamada).
Anteriormente discutimos o protótipo de um serviço e o protocolo na forma como ele se
correlaciona a um objeto. Agora, com o conceito de classe, vemos que os protótipos e o
protocolo são na realidade definidos por uma classe e aplicáveis a todos os objetos que
sejam instâncias dessa classe.
Não só organizamos nossos objetos em categorias (classes) como também dispomos
nossas categorias em uma hierarquia, do geral para o específico. Isso nos leva ao sétimo
princípio do paradigma orientado a objeto — generalização (sem polimorfismo).
Generalização sem Polimorfismo. As classes podem ser organizadas utilizando-se
uma estrutura de herança hierárquica. Nessa estrutura, a subclasse herdará os atri-
butos, os relacionamentos e os métodos da superclasse, que estão em um ponto
mais alto da árvore. Uma superclasse abstrata é uma classe utilizada para criar so-
mente subclasses. Portanto, não há instâncias diretas dessa classe.
Entretanto, para uma regra há sempre exceções. Com o intuito de tratar das exce-
ções à regra dentro de nossa estrutura hierárquica, definimos o oitavo princípio do para-
digma orientado a objeto — generalização com polimorfismo, uma modificação do
sétimo princípio.
Generalização com Polimorfismo. As classes podem ser organizadas utilizando-se
uma estrutura de herança hierárquica. Nessa estrutura, a subclasse herdará os atri-
butos, os relacionamentos e os métodos da superclasse, que se encontram em um
ponto mais alto da árvore. Entretanto, uma subclasse poderá criar seu próprio mé-
todo para substituir um método de qualquer uma de suas superclasses ao prover
CAP. 3 PROGRAMAÇÃO ORIENTADA A OBJETO 29

um serviço que seja disponível no nível da superclasse quando uma instância da-
quela subclasse é o agente.
Para a subclasse, o método dela irá ignorar o método da superclasse para prestar o
mesmo serviço.
Muito embora a generalização seja um conceito poderoso, há relacionamentos entre
objetos que não podem ser capturados utilizando-se esse conceito. Isso nos leva ao nono
princípio do paradigma orientado a objeto — relacionamentos.
Relacionamentos. Colaborações2 entre objetos para prover um serviço a um cliente
são normalmente capturadas por uma relação de associação, que é tecnicamente de-
nominada vínculo.
Mais adiante neste livro, discutiremos um relacionamento de agregação; nesse caso
um vínculo é suficiente, pois uma agregação é realmente um vínculo com propriedades
especiais.

Linguagens de Programação Orientadas a Objeto


Antes de podermos abordar a linguagem C++, deveríamos examinar brevemente os pa-
radigmas de programação e a variedade de linguagens utilizadas na tecnologia orientada
a objeto. Isso nos fornecerá uma melhor perspectiva do motivo pelo qual selecionamos
C++ para este livro. Os paradigmas e linguagens de programação são:

Programação Baseada em Objeto


A programação baseada em objeto é um estilo de programação que utiliza encapsulamen-
to e objetos. Os métodos e atributos são escondidos dentro do objeto, e cada objeto é iden-
tificado de maneira incomparável. Não há suporte para o mecanismo de classe, herança,
relacionamentos, comportamento dinâmico e regras. Ada é um exemplo de linguagem de
programação baseada em objeto.3

Programação Baseada em Classe


A programação baseada em classe inclui todos os mecanismos das linguagens baseadas
em objeto, bem como os mecanismos para classes e instâncias. CLU é um exemplo de lin-
guagem de programação baseada em classe.

Programação Orientada a Objeto


A programação orientada a objeto inclui todos os mecanismos para programação baseada
em classe, bem como os mecanismos para suporte de herança e auto-recursão. Smalltalk
é um exemplo de linguagem de programação orientada a objeto.

2. A definição dos diferentes tipos de relacionamentos (ou seja, associação, agregação, vínculo,
generalização, especialização) será explicada em profundidade em capítulos posteriores.
3. O Visual Basic é igualmente um exemplo de linguagem de programação orientada a objeto.
30 UML E C++ CAP. 3

Programação OO Avançada
A programação OO avançada inclui todos os mecanismos para programação orientada a
objeto e recursos para suporte de herança múltipla, associação, agregação e comporta-
mento dinâmico. C++ e Java são exemplos de linguagens de programação OO avançada.

Programação Orientada a Objeto de Ponta


A programação orientada a objeto de ponta inclui todos os mecanismos de um estilo de
programação OO avançada, bem como os mecanismos para suporte da implementação
de regras. R++ é um exemplo de linguagem de programação de ponta.

Por Que C++?


A linguagem C foi desenvolvida por Dennis Ritchie no início dos anos 70 como uma lin-
guagem de implementação de sistemas e foi utilizada para a construção do UNIX. A fi-
losofia de desenho da linguagem C é descrita a seguir:
■ Torná-la portável.
■ Mantê-la pequena e simples.
■ Torná-la rápida.
■ Confiar no programador.
■ Não impedir que o programador faça o que necessita ser feito.
Com a utilização dos sistemas UNIX e pelo fato de que ela era uma linguagem pe-
quena e concisa, a linguagem C tornou-se amplamente utilizada como uma linguagem de
programação de uso geral.
A linguagem C++ foi criada por Bjarne Stroupstrup no início dos anos 80. Bjarne ti-
nha quatro objetivos principais com o desenho dessa linguagem:
1. Torná-la compatível com C.
2. Estender C com uma construção de classe similar à do Simula 67.
3. Suportar forte digitação.
4. Reter a filosofia de desenho da linguagem C.
Para atingir esses objetivos, C++ reteve muito da linguagem C, incluindo um rico
conjunto de operadores, um desenho praticamente ortogonal, concisão e extensibilidade.
Para nós, a escolha de C++ como nossa linguagem de desenvolvimento foi determi-
nada em grande parte devido aos objetivos citados. Primeiro, desejávamos uma lingua-
gem de programação que preservasse a filosofia de desenho da linguagem C, pois não
acreditávamos que desenvolver software um dia seria tão fácil a ponto de necessitarmos
somente de uma “linguagem orientada a objeto”. Segundo, precisávamos de uma lingua-
gem que facilitasse a interconexão de nossos sistemas a sistemas legados.
As capacidades técnicas de C++ colaboraram para que nossa decisão se tornasse
mais fácil:
1. C++ é uma linguagem altamente portável, pois há agora um padrão ANSI para
C++. Ainda, existem compiladores para esta linguagem em aproximadamente
todos os diferentes computadores e sistemas operacionais.
2. Um programa em C++ pode ser rápido, pois ele não incorre nos gastos em tem-
po de execução do tipo “verificação e coleta de lixo” encontrados na maioria das
linguagens “orientadas a objeto puras”.
CAP. 3 PROGRAMAÇÃO ORIENTADA A OBJETO 31

3. C++ não requer um ambiente gráfico e tem um custo relativamente baixo de


aquisição.
4. C++ é um casamento entre a linguagem Assembly, de baixo nível, e construções
orientadas a objeto de alto nível. O desenvolvedor poderá escrever código no ní-
vel apropriado para modelar a solução particular e, ainda, manter detalhes de
implementação em nível da máquina.
5. C++ é uma linguagem de multiparadigmas que proporciona ao desenvolvedor
uma gama de opções relativas ao desenho e codificação de uma solução. Nós
utilizaremos essas opções para demonstrar técnicas que funcionam em vez de
insistir em dogmas paradigmáticos.
Em resumo, pode-se considerar a linguagem C++ como alternativa de linguagem
orientada a objeto de um desenvolvedor profissional a linguagens “orientadas a objeto
puras”, tais como Smalltalk, Objective C, Eiffel etc. A linguagem confia no programador
e não impede que ele a estenda para suportar mecanismos abstratos proveitosos e tam-
bém utilize técnicas não-orientadas a objeto quando apropriado. Além do mais, ela é uma
extensão de uma linguagem de programação que tem sido utilizada para escrever um
grande número de aplicações em uma ampla faixa de máquinas.
Esse potencial todo é importante sob o ponto de vista prático porque temos conhe-
cimento de bem poucos sistemas comerciais que chegaram à altura dos “conceitos puros
da orientação a objeto”, conforme definido pelos especialistas da tecnologia orientada a
objeto. Para a grande parte dos sistemas que construímos, precisamos ir além dos “con-
ceitos puros” para produzir sistemas que satisfizessem as limitações de prazo, recursos e
verbas e/ou se conectassem pela interface com sistemas legados escritos em linguagens
de programação imperativas como a C. Não há melhor linguagem, a não ser uma lingua-
gem de multiparadigmas, que consiga fornecer aos desenvolvedores de ponta um modo
de usar a criatividade e experiência em um contexto controlado para solucionar proble-
mas reais, atender a limitações de negócios e, também, requerer os benefícios da nova tec-
nologia prometida.
Quando discutirmos regras, examinaremos a linguagem R++, uma linguagem de
programação de ponta, criada pelos laboratórios da AT&T e da Lucent Bell.

Modos de Organizar a Realidade


No passado, os desenvolvedores de software tinham utilizado vários paradigmas (modos
de visualizar ou modelar a realidade) para organizar e administrar software. Entretanto,
cada um desses paradigmas iniciais suportava a modelagem de uma visão específica de
um sistema. As quatro visões essenciais de um sistema são descritas a seguir:
■ Visão de Dados/Entidade
Diagrama de entidade e relacionamento
■ Visão Funcional
Decomposição funcional (técnicas estruturadas)
■ Visão Comportamental
Diagramas de transição de estado (mapas de estado)
■ Visão de Controle
Sistemas baseados em regras
32 UML E C++ CAP. 3

As metodologias de software mais importantes têm sido desenvolvidas utilizando


uma dessas visões como bloco construtivo fundamental.
Por exemplo, o método da análise e desenho estruturados é baseado na utilização
da visão funcional como o bloco de construção essencial de seu paradigma da realidade.
Nesse modelo, o computador é um manipulador de dados. Ele tem certos conjuntos de
instruções que provocarão sua ida para a memória para extrair valores (dados) de vários
slots (endereços de memória), transformá-los de alguma forma e, em seguida, armazená-
los de volta em um outro slot. Embora sendo um modelo razoavelmente acurado sobre o
modo como opera um computador, ele não reflete a forma como a maioria das pessoas
ocupa-se quando da resolução de problemas. Esse tem sido denominado o modelo de
computação de “escaninho” (pigeon-hole).
A metodologia do banco de dados relacional (análise de dados) é baseada na visão
de dados/entidade da realidade. Nesse modelo, o computador é um gerenciador de da-
dos que organiza dados em coleções (denominadas entidades), que são os blocos de cons-
trução do sistema. O computador, então, manipula esses dados dentro das entidades para
obter a informação necessária para efetuar o processamento. Entretanto, essa metodologia
não dizia nada sobre a forma como o computador se encontrava para tirar vantagem da
entidade quando do processamento de dados. Assim, a maioria dos profissionais que uti-
lizou essa metodologia para fins de análise de dados ainda usava o modelo de computa-
ção de “escaninho” para o processamento de dados.
A metodologia de máquina de estado finita (statemate) é baseada na visão compor-
tamental da realidade. Nesse modelo, o computador é um gerenciador de estados. Ele or-
ganiza o processamento baseado no estado do sistema. O estado é o bloco de construção
desse sistema, e os dados que estão sendo manipulados são dependentes do estado. Essa
metodologia não trata da administração de dados. Além do mais, ao desenharem as fun-
ções e ações dessa metodologia, os profissionais continuavam a usar o mesmo modelo de
computação de “escaninho”.
O sistema baseado em regras (parte dos sistemas de inteligência artificial) é baseado
em uma visão de controle da realidade. Nesse modelo, o computador é uma máquina de
inferência que executa um conjunto de regras (declarações if-then). Na teoria, a seqüência
na qual as regras foram executadas não era material. Todavia, na prática, a maioria de
nós não era capaz de encontrar regras que fossem verdadeiramente desacopladas. Pelo
fato de não existir estrutura para a organização das regras, havia também uma coesão
muito fraca. Além disso, os sistemas baseados em regras não nos ajudam a administrar
os dados e não suportam conceitos de procedimento.
Praticamente todas essas metodologias ainda são baseadas no modelo de computa-
ção de “escaninho” e são todas muito fracas na modelagem de visões alternativas do sis-
tema. Em resumo, não existe uma metodologia suficientemente rica para administrar a
informação em um domínio de problema complexo.
Os engenheiros e pesquisadores de software têm feito tentativas durante pelo me-
nos 30 anos no sentido de melhorar as técnicas de construção de aplicações/sistemas de
software. Tentativas de abordar esse assunto partiram de inovações em linguagens de
programação e de vários enfoques estruturados para o desenvolvimento de aplicações.
Ou seja, nós utilizamos métodos estruturados, 4GLs, ferramentas CASE, técnicas de mon-
tagem de protótipos e geradores de código para abordar esse assunto.
O grau de sucesso desses esforços é questionado em muitos relatórios. Estes eviden-
ciam que os métodos estruturados prejudicaram tanto a produtividade dos desenvolve-
dores como a qualidade do produto final. Além disso, em aplicações muito complexas,
CAP. 3 PROGRAMAÇÃO ORIENTADA A OBJETO 33

os 4GLs podem realmente degradar a produtividade e, devido à restrição das linguagens


de alto nível, provocar a mudança para a codificação 3GL.
Era o momento de os profissionais de software entenderem o porquê de estarem
tendo essas dificuldades quando:
■ O software tornou-se a despesa predominante na colocação no mercado e manuten-
ção de um sistema.
■ Nem mesmo os programadores mais capazes conseguiam criar código correto e re-
siliente.
Para conseguirem compreender melhor esse tema, os cientistas da computação co-
meçaram a pesquisar o desenvolvimento de software.
Os pesquisadores de software levaram todo esse tempo para entender o grau de di-
ficuldade envolvido para se construir um “bom” software. Em suas pesquisas, eles cons-
tataram que é difícil construir bom software devido às seguintes razões:
■ A complexidade do domínio do problema.
■ A dificuldade em administrar o processo de desenvolvimento.
■ A flexibilidade possível devida ao software.
■ O problema de caracterização do comportamento de um sistema contínuo.
Neste livro, nós abordamos principalmente a primeira razão. A última razão é dis-
cutida em nosso estudo de casos. Discutimos também o uso apropriado da flexibilidade
das técnicas orientadas a objeto no estudo de casos e a flexibilidade da linguagem C++
nas seções de codificação. A administração do processo de desenvolvimento está fora do
escopo deste livro.

Modelo de Simulação da Computação


A visão quanto à resolução de problemas da programação orientada a objeto é muito di-
ferente do modelo de “escaninho” utilizado na programação imperativa. No paradigma
orientado a objeto, nós nunca utilizamos alguns dos termos convencionais, tais como atri-
buições (assignments), variáveis (variables) ou endereços de memória (memory addresses).
Em vez disso, utilizamos termos como objetos (objects), mensagens (messages) e serviços
(services). Dispomos de um universo de objetos bem-comportados que, de forma cortês,
pedem uns aos outros para realizar serviços para eles próprios. Temos uma comunidade
de ajudantes que nos assiste na resolução de problemas.
A idéia de criar um universo de ajudantes é muito similar a um estilo de simulação
de computador denominado “simulação controlada por evento discreto”. Nesse estilo, o
usuário cria modelos de vários elementos da simulação e descreve como eles interagem
entre si. Então, por meio de algum evento discreto, os elementos são postos em movimento.
Isso é quase idêntico à forma como realizamos modelagem/programação orientada
a objeto. Definimos vários objetos no universo que nos ajudarão a resolver o problema,
bem como de que forma eles interagem entre si, e, em seguida, os colocamos em movi-
mento. Como resultado, consideramos a programação orientada a objeto como a utiliza-
ção de um modelo de simulação em vez de um modelo de “escaninho” da computação.
Este modelo de simulação ainda proporciona ao desenhista/programador uma me-
lhor metáfora para a resolução de problemas. Quando pensamos em termos de serviços
e métodos (como prover o serviço), podemos contribuir com uma grande parcela de ex-
periência, entendimento, idéias e intuição, extraídas de nossa rotina diária. Em contraste,
34 UML E C++ CAP. 3

a maioria de nós tem muito pouca percepção de como estruturar um programa que pensa
sobre resolver problemas em termos de “escaninhos” ou slots contendo valores.

Modo Orientado a Objeto de Organizar a Realidade


Para ilustrar como o paradigma orientado a objeto ajuda-nos a administrar a complexi-
dade, deixe-nos aplicar esta técnica de modelagem em uma situação do mundo real. Pri-
meiramente, descreveremos a situação e, em seguida, discutiremos de que forma
utilizaríamos o paradigma orientado a objeto para capturar essa situação.
Neste exemplo, temos uma família com o pai (John), a mãe (Jane), dois filhos (Peter
e Paul) e duas filhas (Elizabeth e Mary). John é ator e Jane, dentista. Todas as crianças são
estudantes e o cachorro da família chama-se Lassie. A médica da família é Alice. Esta fa-
mília possui uma casa no subúrbio de uma grande cidade. Embora o corte de grama do
terreno da casa seja, normalmente, uma tarefa designada ao pai, essa atividade também
poderia ser uma tarefa paga a qualquer um dos filhos. Contudo, um trabalhador, de
nome Jack, é quem atua profissionalmente como cortador de grama nas vizinhanças
da casa.
Uma manhã, Jane percebe que o gramado precisa ser aparado e cortado e, assim,
ela menciona a John que é hora de fazê-lo. Ele concorda e diz que irá cortar a grama na-
quela tarde. No fim da tarde, John chega em casa e está exausto devido ao longo dia no
estúdio, e decide pagar a um de seus filhos para a realização dessa tarefa. Ele procura por
um de seus filhos; o primeiro que ele vê é Mary. Pergunta se ela pode cortar a grama por
cinco dólares. Ela concorda; entretanto, Mary sabe que Jack, o cortador de grama profis-
sional, está propenso a cortar a grama por quatro dólares. Assim, Mary pede a Jack para
cortar a grama por quatro dólares, e Jack concorda em fazer o trabalho. Jane chega em
casa mais tarde nessa noite e nota o gramado aparado e cortado. Achando que foi John
quem tinha realizado o serviço, ela o cumprimenta pela excelente condição do gramado.
Agora permita-nos fazer uma análise orientada a objeto dessa situação. Existem os
seguintes objetos tangíveis em nosso modelo: John, Jane, Peter, Paul, Elizabeth, Mary,
Lassie, o cortador de grama da família, Jack, e Alice. Jack, o cortador de grama profissio-
nal, faz anúncio de um serviço de aparo e corte de gramados. John, como o pai da casa,
também provê um serviço de corte de grama, porém, limitado ao gramado da família.
Cada um dos filhos também provê o mesmo tipo de serviço, mas por um preço determinado.
O protótipo do serviço de corte de grama não é o mesmo para cada um dos objetos.
Por exemplo, o protótipo para o serviço de corte de grama de Jack pode ter o nome (se-
letor) “cortar a grama” e os argumentos (assinatura) — endereço do gramado, de quem
cobrar, endereço para cobrança. O protótipo dos quatro filhos para o serviço de “cortar
a grama” talvez pareça similar ao de Jack, pois eles estão desejosos de arrumar algum
gramado para obter dinheiro. O protótipo dos filhos pode ter o nome “cortar a grama” e
os argumentos — endereço do gramado, dinheiro (em espécie). Finalmente, John somente
irá aparar e cortar a grama do gramado da família na condição de uma tarefa caseira. O
protótipo de John poderá ter o nome “cortar a grama” e ser isento de argumentos. John
tinha um valor padrão para o gramado a ser feito, a saber, o gramado da família, quando
Jane lhe pediu para que aparasse e cortasse o gramado. Ele além disso sabia que não re-
ceberia pagamento pelo aparo e corte do gramado da família.
Jane, a cliente, solucionou o problema de ter o gramado arrumado encontrando um
agente apropriado, John, a quem ela passa uma mensagem contendo sua solicitação de
serviço por “ação”. Para que John processe a mensagem, a mensagem de Jane deve seguir
CAP. 3 PROGRAMAÇÃO ORIENTADA A OBJETO 35

o protocolo/protótipo que ele definiu para o serviço. Como Jane enviou uma mensagem
que John consegue interpretar (ou seja, a mensagem é consistente com um protocolo den-
tro do protocolo que John anunciou), John deve ter um método (algum algoritmo ou con-
junto de operações) para realizar o serviço solicitado (“cortar a grama”).
Nesse caso, John comporta-se de maneira dinâmica. Se não estiver cansado no final
do dia, ele cortará a grama sozinho; porém, se estiver cansado, ele pedirá a um de seus
filhos para cortar a grama. De forma alternativa, se John sempre corta a grama sozinho,
como Jane aparentemente supôs, então o comportamento de John é considerado estático.
Em qualquer cenário (quer John utilizasse um método dinâmico ou estático para seu
comportamento), quando aceitou a mensagem, ele acabou aceitando a responsabilidade
de satisfazer o pedido de Jane. Similarmente, quando Mary aceitou o pedido de John para
“cortar a grama” por cinco dólares, ela também aceitou a responsabilidade de satisfazer
o pedido de John.
Jane desconhecia, e provavelmente nem queria saber, o método particular que John
utilizou para prover o serviço “cortar a grama”. Ela estava muito contente ao ver que o
gramado tinha sido cortado quando chegou em casa. Se ela tivesse investigado, entretan-
to, teria descoberto que Jack cortou a grama por quatro dólares. Similarmente, John des-
conhecia o método particular que Mary utilizou para cortar a grama. Essa é a visão do
cliente do princípio da ocultação de informações.
No paradigma orientado a objeto, o serviço do agente é definido para o cliente em
termos de responsabilidades. A solicitação de Jane por “ação” (ou seja, serviço de John)
deve indicar unicamente o resultado desejado (corte da grama). John é livre para perse-
guir qualquer técnica que atinja os resultados desejados e não é tolhido pela interferência
de Jane. É responsabilidade dele definir como irá prover o serviço solicitado, definindo o
método para a prestação do serviço.
O método de John era um algoritmo baseado no fato de estar ou não cansado no
final do dia. Conforme sabemos, neste dia em particular ele delegou o serviço de cortar
a grama para sua filha, Mary. Além disso, Mary subseqüentemente delegou o trabalho
para Jack.
Deste exemplo, nós verificamos um outro princípio, também totalmente humano,
na passagem de mensagens. O primeiro pensamento de todos os clientes e agentes é en-
contrar uma outra pessoa que faça o trabalho. Essa é a aplicação do princípio da delega-
ção. Naturalmente, os objetos nem sempre podem responder a uma mensagem pedindo
a outro objeto que realize uma ação. Se assim fosse permitido, haveria um círculo infinito
de solicitações, semelhante a uma burocracia de “empurradores” de papel, cada um pas-
sando papéis a algum outro membro da organização. Em certo ponto, no mínimo alguns
objetos precisariam realizar algum serviço em lugar de passar a solicitação a um terceiro
objeto. Essas idéias são utilizadas em nosso exemplo do corte da grama. Da perspectiva
de Jane, seu marido John prestou o serviço “cortar a grama”, razão pela qual ela o cum-
primenta pela excelente condição do gramado. Da perspectiva de John, foi Mary quem
cortou a grama. John, então, possivelmente também agradecerá à Mary pelo excelente tra-
balho que ela realizou. Mas, na verdade, se os comportamentos ou métodos (a forma
como prestamos os serviços) não estivessem escondidos (ocultação de informações) de
Jane, ela teria sabido que foi Jack quem cortou a grama.
Para entendermos melhor o mecanismo de passagem de mensagens, daremos uma
olhada em um cenário diferente. John poderia ter enviado sua mensagem “cortar a gra-
ma” a um de seus filhos. Suponhamos que ele a enviasse para Peter, e não para Mary. O
comportamento, ou método, de Peter é diferente do de Mary. Mais especificamente, ele
36 UML E C++ CAP. 3

precisa de cinco dólares para comprar gasolina para ir a um jogo de bola no dia seguinte.
Dessa maneira, ele mesmo cortará a grama. John e Jane tinham a opção de escolher a pes-
soa a quem solicitariam o serviço (ou enviariam a mensagem). Entretanto, o cliente deve
enviar a mensagem a um receptor designado, e esse deverá prestar o serviço. Se Jane ti-
vesse pedido a Alice, a médica da família, para cortar a grama, ela estaria incorrendo em
erro porque Alice não presta serviços de corte de grama. Se Alice entendesse a mensa-
gem, provavelmente retornaria com uma mensagem de erro “médicos não cortam gra-
ma”. Muito provavelmente, isso resultaria em uma mensagem inválida para o recipiente.
Agora vejamos alguns outros conceitos orientados a objeto. Embora Mary talvez ja-
mais tenha negociado com Jack, ela tem algumas idéias sobre o comportamento que pode
esperar quando solicita seus serviços. Isso se deve ao fato de que Jack é um cortador de
grama profissional, e ela tem informações sobre esse tipo de profissional. Como Jack é
uma instância da categoria (classe) CortadorDeGrama, ele se enquadra no padrão geral
dos cortadores de grama. Podemos utilizar a palavra CortadorDeGrama para representar
a categoria de todos os cortadores de grama. Essa é uma aplicação do princípio da clas-
se/instância/objeto.
Mary tem informações complementares genéricas sobre Jack que vão além de ele ser
uma instância da classe CortadorDeGrama. Ela também as tem porque Jack se encontra,
igualmente, na classe ProfissionalDeServiçosDomésticosGerais. Ela sabe que Jack irá a
sua casa para cortar a grama, exatamente da mesma maneira que outros profissionais de
serviços gerais, como é caso do limpador de carpete e do jardineiro. Essa classe é diferen-
te da classe ProfissionalDeSaúde, que normalmente não realiza serviços em residências.
Além disso, Mary sabe que Jack também está na classe DonoDePequenaEmpresa e, como
tal, irá pedir uma quantia em dinheiro como parte do serviço e fornecerá um recibo, exa-
tamente da mesma forma que qualquer outro proprietário de pequena empresa. Mary or-
ganizou seu conhecimento sobre Jack em termos de uma hierarquia de classes.
A árvore de generalização com a organização de Mary é apresentada na Figura 3.3.
Utilizamos a notação da UML nessa ilustração. A generalização/especialização (herança)
é mostrada como uma linha sólida, da subclasse à superclasse, com uma cabeça triangu-
lar na extremidade da superclasse. A geração de uma classe é mostrada com uma linha
pontilhada, da instância à classe, com uma cabeça triangular na extremidade da classe.
As classes e instâncias são mostradas como retângulos. As instâncias são distintas das
classes pois têm os nomes sublinhados.
Jack é uma instância da classe CortadorDeGrama, mas esta é uma forma especiali-
zada (subclasse) da classe ProfissionalDeServiçosDomésticosGerais. Além do mais, Pro-
fissionalDeServiçosDomésticosGerais é uma subclasse da forma generalizada
(superclasse) de DonoDePequenaEmpresa, e Mary, seguindo a hierarquia, foi até Maté-
riaOrgânica, via Humano e Mamífero. Portanto, há uma grande quantidade de informa-
ção genérica que ela tem sobre Jack que não está diretamente correlacionada ao fato de
ele pertencer à classe CortadorDeGrama. Isso se deve ao fato de que Mary assume que
o conhecimento de uma classe mais geral aplica-se a uma especialização dessa classe. A
classificação de Jack feita por Mary é a aplicação do princípio da generalização com po-
limorfismo.
CAP. 3 PROGRAMAÇÃO ORIENTADA A OBJETO 37

FIGURA 3.3 Generalização/Especialização — hierarquia para o exemplo do corte de grama.

A generalização (herança) trabalha de forma que satisfaça aos pais. As crianças


(subclasses) herdam todos os atributos (conhecimento) e métodos (comportamento) dos
pais (superclasse). E mais, as crianças podem ter outros atributos (mais inteligentes do
que os pais) e métodos (podem desempenhar mais do que os pais). Isso é o que chama-
mos de forma de especialização de “um bom filho”. Infelizmente, nem todos os filhos são
“bons filhos”.4 Necessitamos de um modo de modelar o “mau filho”. Trataremos disso
em seguida.
Sabemos que os mamíferos se reproduzem dando à luz filhotes; certamente, os ho-
mens fazem da mesma forma e também o cachorro Lassie. Entretanto, Phyllis, o ornitor-
rinco fêmea, se reproduz pondo ovos em vez de dar à luz. Portanto, a subclasse
Ornitorrinco provê o serviço de reprodução de maneira diferente do restante dos
mamíferos.
Se quisermos capturar esse fato e ainda utilizar nossa hierarquia de generalização,
deveremos ter um modo de lidar com as exceções da regra geral. Fazemos isso propor-
cionando um modo para a subclasse prover um método diferente para um serviço defi-
nido na superclasse (classe dos pais). Em nosso exemplo, a superclasse Mamífero definiu
um serviço denominado “reproduzir”. O método para o serviço “reproduzir” é dar à luz
crianças vivas. Ornitorrinco é uma subclasse de Mamífero. Na forma de especialização
de “bom filhote”, Phyllis reproduziria dando à luz filhotes vivos. Entretanto, Phyllis é um
“mau filho”; ela optou por reproduzir pondo ovos. Nessa forma de especialização, deno-
minada generalização com polimorfismo, Phyllis provê seu próprio método (ou seja, pon-
do ovos) para o serviço “reproduzir”. Portanto, Phyllis utilizará um método diferente do

4. Este comentário não é um julgamento moral sobre o bem e o mal. Na verdade, apreciamos o fato de
que este paradigma suporte crianças más.
38 UML E C++ CAP. 3

de Lassie para prover o serviço “reproduzir”. Phyllis é um exemplo da necessidade de


modificar o princípio da generalização sem polimorfismo.
Em nosso exemplo, precisamos utilizar generalização com polimorfismo. Mary e to-
dos os outros estudantes responderão à mensagem “ cortar a grama” delegando qualquer
solicitação que pague a Jack acima de cinco dólares, visto que ele cortará a grama por
quatro dólares. Entretanto, Peter é uma exceção, posto que ele sempre responderá ao pe-
dido de “cortar a grama”; fazendo ele mesmo esse serviço. Portanto, temos de utilizar ge-
neralização com polimorfismo para capturar o comportamento de Peter (veja a Figura 3.3).
Examinaremos mais minuciosamente o método que Mary utiliza para “cortar a gra-
ma”. Para que Mary peça a Jack que ele corte a grama, ela precisará ter acesso a ele. Nor-
malmente, isso é feito quando Jack envia folhetos à vizinhança anunciando seus serviços.
Mary vê o folheto e anota o nome e o número de telefone dele em sua agenda. Muito em-
bora essa ação possa parecer inócua, Mary estabeleceu um relacionamento com Jack. No
instante em que anotou o nome Jack em sua agenda, ela decidiu considerar a utilização
de seus serviços. Na realidade, a razão pela qual Jack envia os folhetos é a de estabelecer
relações que irão agregar a sua base de clientes. Cada pessoa que ler o folheto e arquivar
o nome, número de telefone e tipo de serviço efetuado por ele, estabelecerá uma relação
com Jack. Nós podemos nomear esse relacionamento a partir de duas perspectivas: (1) da
perspectiva de Jack, todas as pessoas que mantêm as informações encontradas no folheto
são clientes e (2) da perspectiva de Mary e dessas pessoas, Jack é um cortador de grama
profissional.
Essa espécie de relacionamento em que um objeto sabe sobre outro objeto devido a
serviços específicos é denominada vínculo. Suponhamos que o bairro seja chamado de
FunTown e que tenhamos uma categoria (classe) de pessoas denominada FunTowner.
Pelo fato de Jack ser uma instância de CortadorDeGrama, e como cada um de seus clien-
tes é uma instância de FunTowner, nós podemos capturar este vínculo em um conceito
de nível mais alto denominado associação. Uma associação descreve um grupo de vínculos
com estrutura e semântica comuns. Todos os vínculos em uma associação devem conec-
tar objetos de uma mesma classe a objetos de uma mesma segunda classe. Portanto, se
tivermos uma segunda pessoa, Joan, que também seja uma instância de CortadorDeGra-
ma, e cada um de seus clientes seja uma instância de FunTowner, teremos um segundo
vínculo pertencente à mesma associação.
As associações são bidirecionais. É comum dar um nome a uma associação em cada
direção. Em nosso exemplo, para a direção de CortadorDeGrama o nome é cliente, e para
a direção de FunTowner o nome é cortador de grama.5 Sem a associação, não haveria veí-
culos para Mary acessar os serviços de Jack.
A Tabela 3.1 resume os conceitos-chave orientados a objeto utilizados no exemplo
do “corte da grama”.

5. Na realidade, um vínculo é implementado por meio de indicadores de um objeto para outro(s). Um


indicador (pointer) é uma referência explícita a um objeto; por conseguinte, uma associação é
implementada em uma classe como atributo de indicador de classe para a outra classe. Por exemplo,
na porção de dados de uma classe FunTowner, poderá haver um membro de dado cortador de grama
que aponta para um objeto CortadorDeGrama. De modo inverso, a porção de dado da classe
Cortador de Grama poderá conter um membro de dado cliente que aponta para um conjunto de
objetos FunTowner, que são os clientes.
CAP. 3 PROGRAMAÇÃO ORIENTADA A OBJETO 39

TABELA 3.1 Conceitos OO com Exemplos


Conceito OO Explicação/Exemplo

Cliente Solicitador do serviço (ou seja, remetente da


mensagem — Jane)
Agente (servidor) Agente a quem a mensagem é enviada (ou seja, John)
Objeto Jane, John, Mary, Lassie
Envio de mensagem Pedido de Mary para John para “cortar a grama”
Assinatura Informações (argumentos) extras necessárias para
pôr em prática a solicitação (isto é, quantia a ser
paga)
Responsabilidade Responsabilidade de John em satisfazer o pedido de
Jane
Atributos Nome, idade etc.
Serviços John e Jack prestam o serviço “cortar a grama”
Método Esta(s) é(são) a(s) operação(ões) para a execução do
serviço, escondida(s) do cliente
Colaboração Objeto que ajuda um outro objeto na realização do
método (ou seja, Jack coopera com Mary no
cumprimento do serviço “cortar a grama”)
Classe Mamífero, DonoDeEmpresa
Instância O mesmo que um objeto
Herança CortadorDeGrama é uma forma especializada de
ProfissionalDeServiçosDomésticosGerais. Esse, por
sua vez, é um Homem, que é uma forma
especializada de Mamífero
Polimorfismo Phyllis pondo ovos
Subclasse Subclasse herdará atributos e métodos de sua(s)
superclasse(s) (ou seja, Homem herdará todos os
atributos e métodos de Mamífero)
Superclasse Dentista é uma subclasse da superclasse
ProfissionalDeSaúde
Superclasse abstrata Classe utilizada somente para criar subclasses (ou
seja, Mamífero)

■■ RESUMO
Os princípios do paradigma orientado a objeto são os seguintes:
1. Encapsulamento. Um objeto contém os dados e os métodos (código) que mani-
pularão ou mudarão os dados.
2. Ocultação de informações. Os serviços de um objeto definem de que forma ou-
tros objetos têm acesso a seus métodos e, portanto, a seus dados. Cada objeto
anuncia serviços públicos que está disposto a prover a outros objetos.
3. Passagem de mensagens. Um objeto (cliente) pode se comunicar com um outro
objeto (agente) unicamente via mecanismo de passagem de mensagens. Um
cliente solicita um serviço de um agente enviando uma mensagem que confere
com um protocolo predefinido que o agente define para aquele serviço.
40 UML E C++ CAP. 3

4. Ligação Posterior (Late binding). O receptor específico de qualquer dada men-


sagem não será conhecido até o tempo de execução (run-time), de modo que a
determinação de qual método invocar não poderá ser feita até então. Lembre-se
da herança com polimorfismo.
5. Delegação. O trabalho é transmitido, via mecanismo de passagem de mensa-
gens, de um objeto (cliente) a um outro objeto (agente), pelo fato de que, do pon-
to de vista do cliente, o agente tem os serviços de que o cliente necessita. O
trabalho é continuamente transmitido até que ele atinja o objeto que tem tanto
os dados como os métodos (código) para sua realização. A delegação é algumas
vezes denominada princípio burocrático perfeito.
6. Classes e objetos. Todos os objetos são instâncias de uma classe. A forma como
um objeto provê um serviço é determinada pela classe da qual o objeto é uma
instância. Portanto, todos os objetos da mesma classe utilizam o mesmo método
(código) em resposta a uma específica solicitação de serviço.
7. Herança e polimorfismo. As classes podem ser organizadas utilizando-se uma
estrutura de herança hierárquica. Nessa estrutura, a subclasse herdará os atribu-
tos e os métodos da(s) superclasse(s) que se encontra(m) em posição(ões) mais
alta(s) na árvore. Todavia, uma subclasse pode criar seu próprio método para
substituir o método de qualquer uma de suas superclasses na execução de um
serviço disponível no nível da superclasse. Quando a subclasse é o agente para
aquele específico serviço, o método da subclasse omitirá o método da(s) super-
classe(s) para prover o mesmo serviço.
8. Relacionamentos. A associação e a agregação são utilizadas para capturar a co-
laboração entre objetos necessária para prestar um serviço a um cliente.
Delimitando o Domínio
DELIMITANDO O DOMÍNIO
4.Delimitando O Domínio

A fronteira perdeu a linha invisível que separa a imagem da realidade.

William Wordsworth

O capítulo anterior introduziu o modo orientado a objeto de organizar a realidade. A


organização de toda a realidade para fins de modelagem não é uma atividade sim-
ples, mesmo sendo realizada pelo modo orientado a objeto. Praticamente falando, não se
deve tentar organizar toda a realidade. Normalmente, lida-se com a modelagem de um
domínio de aplicação específico, ou seja, seleciona-se um domínio tratável dentro do qual
será desenvolvido o modelo. Este capítulo identifica dois métodos para a captura dessa
fronteira de domínio: casos de uso e contratos.
Casos de uso capturam os requisitos funcionais e as proposições de valor1 de um
sistema proposto com seus processos associados de alto nível (isto é, aqueles processos
que estão externos à fronteira do sistema) necessários para atingir estas específicas pro-
posições de valor.2 Contratos articulam os serviços (operações) providos pelo sistema
(software de aplicação) para atingir essas proposições de valor. Um dos valores-chave em
se produzir casos de uso e contratos é que eles facilitam as discussões entre investidores
e analistas/desenvolvedores. Os dois são normalmente escritos utilizando-se termos de
negócios, que são naturais para a maioria dos investidores.

1. As proposições de valor são as razões pelas quais o sistema está sendo construído. Elas identificam
o valor que vários recursos proporcionam ao negócio.
2. Os autores também as utilizaram para capturar as características de desempenho/requisitos do
sistema sob uma perspectiva de emprego. A captura desses parâmetros é útil para os testes de
performance.

41
42 UML E C++ CAP. 4

Introdução aos Casos de Uso


Ivar Jacobson, em Object-oriented Software Engineering: A Use-Case Driven Approach, definiu uma
abordagem baseada em cenários para estabelecer uma fronteira no domínio denominada mé-
todo Objectory. Esse método focava a identificação dos elementos importantes de um domínio
em termos de como eles contribuíam ou se comportavam enquanto proviam um serviço. Ele
chamou cada cenário de caso de uso porque descreviam um uso do sistema3.
Vários autores definem casos de uso de maneira diferente:
■ Um caso de uso especifica uma seqüência de ações, inclusive variantes, que um sis-
tema realiza e que produz um observável resultado de valor para um particular ator
(Jacobson, Booch, Rumbaugh 1999).
■ Um caso de uso é uma descrição de todas as possíveis seqüências de interações en-
tre o sistema e um ou mais atores em resposta a algum estímulo inicial devido a um
dos atores (Rumbaugh 1994).
■ Um caso de uso é uma coleção de possíveis seqüências de interações entre o sistema
sob discussão e seus atores externos, relacionada com um objetivo em particular
(Cockburn 2000).
Os pontos em comum em todas essas definições são os atores e as seqüências de in-
terações. Neste enfoque, diversos conceitos são importantes: o objetivo, o sistema, o ator,
o caso de uso e o grupo de casos de uso.
O objetivo (goal) é o valor de negócio para o(s) “usuário(s)” do sistema que, geral-
mente, inicia(m) a interação com o sistema.
O sistema (system) é a aplicação, com todo seu hardware associado, que será utili-
zada pelos “usuários”.
Um ator (actor) é uma entidade externa que interage com um sistema.
Um caso de uso (use case) é a descrição de uma interação que consegue um objetivo
proveitoso para um ator.
Um grupo de casos de uso (use case bundle) é uma coleção de casos de uso estreita-
mente correlacionada com alguma atividade ou elemento organizador de negócio.
Um grupo de casos de uso nos propicia um modo de organizar nossos casos de uso
em coleções que cooperarão conosco para entendermos melhor a funcionalidade do
sistema que estamos desenvolvendo.

Sistema
Historicamente, os sistemas têm sido entendidos com a utilização de três modelos bási-
cos: caixa preta, caixa branca e caixa transparente. Cada modelo sucessivo, conforme ilus-
trado na Figura 4.1, proporciona detalhes cada vez melhores das partes internas e da
implementação do sistema. O modelo de caixa preta, mostrado como a fronteira sólida,
enfatiza o valor que o sistema provê e onde ele se enquadra no restante do ambiente de
computação empresarial. Ele introduz o sistema sem se interessar sobre como proverá o
valor de negócio. Aqui, os conceitos-chave são os usuários do sistema (incluindo outros
sistemas) e o valor provido pelo sistema para cada usuário.

3. Supõe-se que o uso do sistema consiga algum objetivo proveitoso para o usuário do “sistema”. Dessa
forma, casos de uso tentam capturar a proposição de valor a partir de uma visão externa. A saber,
o objetivo é capturar a proposição de valor sob a perspectiva do usuário.
CAP. 4 DELIMITANDO O DOMÍNIO 43

FIGURA 4.1 Visibilidade das partes internas de um sistema de acordo com os modelos de
caixa preta, caixa branca e caixa transparente.

O modelo de caixa branca, mostrado como o oval, apresenta o sistema em termos


de quais funções específicas de negócio o sistema provê. Esse modelo enfatiza a forma
como os objetos no sistema orientam os processos de negócios. Nós podemos utilizar um
modelo de caixa branca para capturar os casos de uso essenciais, detalhados, que são am-
bos primários e secundários. Um modelo de caixa branca não se envolve com a arquite-
tura do hardware ou do software.
O modelo de caixa transparente, mostrado como os círculos bem pequenos, apre-
senta as partes internas do sistema e a forma como elas contribuem para a funcionalidade
nos negócios. Cada círculo representa um elemento interno, quer ele seja um código-fonte
ou dispositivo. Nesse modelo, são incluídos as entidades tecnológicas empregadas dentro
do sistema, bem como os detalhes sobre como elas trabalham em conjunto para prover a
funcionalidade nos negócios.
No desenho de casos de uso, devemos visualizar o sistema como uma caixa preta.
Mas a experiência tem nos revelado que, às vezes, é necessário que adotemos pelo menos
uma visão com a caixa branca para determinarmos a seqüência de interações entre o ator
e o sistema. Isso nos ajuda a definir o caso de uso. Além do mais, quando estivermos tra-
balhando com casos de uso concretos, de baixo nível, provavelmente teremos de adotar
uma visão transparente do sistema.

Atores
O modelo de caso de uso divide o mundo em duas partes: o sistema e os usuários (as
entidades externas que o utilizam). Os atores são um mecanismo para categorizar os
usuários (normalmente uma entidade física) do sistema que compartilham um conjunto
de interações comuns para atingirem um objetivo ou conjunto de objetivos. Um ator pode
ser um usuário, um sistema externo ou um dispositivo. Um ator pode fazer uma solici-
tação de serviço ao sistema, ser solicitado para prover um serviço e interagir com o sis-
tema por meio de um diálogo complexo de requisições de serviço entre o ator e o sistema.
É o emprego de um conjunto de interações comuns em uma maneira similar para atingir
um objetivo que constitui a chave da categorização. Dessa maneira, um ator é uma repre-
sentação4 de qualquer entidade que possa iniciar uma ação por parte do sistema ou rece-
ber uma solicitação para atuar do sistema. Um ator pode ser caracterizado por

4. Em um caso de uso padrão, a instância de um ator é o iniciador. O iniciador é a entidade que inicia
uma série de interações com o sistema. Além do iniciador, um caso de uso pode ter atores
participantes, que são entidades que não iniciaram a interação.
44 UML E C++ CAP. 4

solicitações que ele inicia para o sistema ou por solicitações para as quais ele pode res-
ponder. Em um sentido real, o conjunto completo de solicitações/respostas para todos os
atores estabelece uma fronteira no domínio do qual o sistema possa estar ciente. Ou me-
lhor, um sistema nunca poderá responder a aspectos de um domínio para o qual ele não
foi designado (ou seja, ele não processa solicitações [inputs] para as quais não tenha sido de-
signado).
Ocasionalmente, certos atores poderão compartilhar solicitações comuns que todos
eles invocam do sistema. Preferivelmente a associar explicitamente o mesmo conjunto de
solicitações com cada ator, podemos introduzir um ator generalizado que monopolize
aquelas solicitações comuns. Os outros atores são tratados como especializações e herdam
a habilidade de atender àquelas solicitações a partir da generalização.

Casos de Uso
Um cenário é uma pequena história que delineia uma seqüência esperada de solicitações
e respostas entre um usuário (usuários) e o sistema. Ele é utilizado para comunicar como
um usuário específico5 emprega o sistema para atingir um objetivo proveitoso. Escrever
um cenário é um simples jogo de “o que acontece a seguir”. A maioria dos cenários é sim-
ples; há apenas uma seqüência lógica de operações a partir do estado inicial. Outros ce-
nários são mais complicados, com múltiplos casos de exceção (coisas indo mal) ou
diferentes caminhos de interações (opções).
Um caso de uso é estreitamente relacionado com um cenário.6 A Figura 4.2 ilustra
um diagrama simples de caso de uso para um sistema de inscrição em cursos.7

FIGURA 4.2 Diagrama simples de caso de uso.

5. Um usuário específico é uma instância de um ator. Por exemplo, Joe é um usuário. Joe é uma
instância do ator: usuário.
6. O caso de uso é a forma generalizada de uma família de cenários. Portanto, um cenário é uma
instância específica de um caso de uso.
7. Uma discussão detalhada sobre diagramas de casos de uso é apresentada no final deste capítulo.
CAP. 4 DELIMITANDO O DOMÍNIO 45

Ele descreve um sistema em termos das seqüências de interações entre vários atores e
o sistema (ou seja, um específico caso de uso captura todos os cenários que começam com a
mesma solicitação para atingir o mesmo objetivo do usuário). Ainda, ele é um modo formal
de descrever todas as interações entre os atores e o sistema para atingir os objetivos de ne-
gócio do sistema. O ator que inicia o caso de uso é chamado de ator de iniciação (iniciador).
Na maioria das interações, o diálogo pode resultar na interação do sistema com outros atores;
esses atores são chamados de atores participantes. A interação assume que o sistema é uma
caixa preta e utiliza elementos do domínio como atores, que interagem com o sistema sob
uma forma de solicitação/resposta. Um caso de uso identifica as pré-condições que devem
existir para ele ser válido, as pós-condições que definem o estado do sistema após o caso de
uso estar concluído, negócio detalhado (não dependente da tecnologia) que é realizado, ex-
ceções que possam surgir nos negócios e restrições nos negócios que se aplicam ao sistema
quando da resposta à solicitação específica de um ator.
Ao capturar os aspectos funcionais do sistema, uma das dificuldades em se gerar
uma discussão útil de um sistema é manter a descrição em um nível consistente de abs-
tração. Para que os casos de uso sejam desenvolvidos com sucesso, é necessário conhecer
a dimensão da descrição funcional que se está tentando capturar. Então, pode-se deter-
minar o nível de detalhe na informação que deverá ser capturado.
Em uma dimensão, nós podemos distinguir entre descrições funcionais de alto e
baixo níveis de um sistema. Uma descrição de alto nível provê descrições breves e gerais
da essência dos valores de negócio providos. Ela não se interessa com a forma como os
valores de negócio são atingidos. Uma descrição de baixo nível provê detalhes de negó-
cios mostrando a ordem exata das atividades, tarefas ou alternativas.8
Em uma segunda dimensão, podemos distinguir entre funções primárias e secun-
dárias do sistema. Funções primárias são as funcionalidades essenciais nos negócios do
sistema — as funções providas aos usuários que constituem a razão pela qual o sistema
existe. Os processos secundários tratam dos casos excepcionais e raros.9 Essas são as fun-
ções necessárias que proporcionam um sistema robusto.
Finalmente, em uma terceira dimensão, podemos distinguir entre as funções essen-
ciais e concretas do sistema. Os casos de uso essenciais são soluções de negócio indepen-
dentes da implementação (hardware e software), enquanto que os casos de uso concretos
são dependentes do desenho. A distinção entre essencial e concreto é a existente entre os
modelos de caixa preta e caixa transparente.

Relacionamentos entre Casos de Uso. Um caso de uso típico esboça uma seqüência de
interações entre o iniciador de uma solicitação de serviço ao sistema e o sistema. Acontece
com freqüência o fato de algumas seqüências de interações serem comuns através de
múltiplos casos de uso. Nessa situação, podemos extrair essas seqüências comuns como

8. Uma descrição de alto nível de Romeu e Julieta de Shakespeare é que ela é uma história de amor.
Uma descrição de baixo nível inclui os tipos de detalhes sobre a história, conforme apresentado em
compêndios para estudo. Nenhuma das descrições de nível é, na verdade, a história.
9. Pode-se argumentar que processos secundários não são raros ou ocasionais se alguém incluir dentro
desta categoria processos, tais como copiar dados para fins de segurança ou transferir dados
necessários a outros sistemas. Esses processos possivelmente não são a razão primária para a
construção do sistema, mas são processos essenciais quase sempre requeridos. Consideramos
aqueles casos como casos primários.
46 UML E C++ CAP. 4

um caso de uso. Então, incluímos o caso de uso10 assim formado dentro dos casos de uso
dos quais eles foram extraídos. Quando essa situação existe, dizemos que cada um dos
“múltiplos” casos de uso incorpora o caso de uso comum (ou seja, podemos ter um rela-
cionamento “incluir” entre os casos de uso). O relacionamento “incluir” permite que pos-
samos localizar em um caso de uso uma seqüência comum de atividades entre os vários
casos de uso. Isso tem a vantagem de que, quando ocorrem mudanças nessa seqüência
comum, ele somente precisa ser mudado em um local.
Além disso, podemos ter a situação em que vários casos de uso são idênticos, com
a exceção de uma ou duas subseqüências específicas de interações. Assim, podemos ex-
trair o núcleo comum (caso de uso de base) e tratar os casos de uso que diferem como
extensões11 do caso de uso de base. Portanto, existe um relacionamento “estender” entre
uma extensão e o núcleo. Isto nos possibilita capturar de forma cômoda aquelas situações
em que as seqüências capturadas em diversos casos de uso podem diferir como resultado
de uma condicional simples no final da seqüência.
Finalmente, no desenvolvimento de um caso de uso de alto nível é freqüente o caso
em que ele pode encerrar diversos casos de uso detalhados e estendidos. O relacionamen-
to que existe entre o caso de uso de alto nível e o caso de uso detalhado e estendido é
uma relação do tipo generalização/especialização.

Objeto da Descrição. Conforme estabelecido anteriormente, um caso de uso pode ser


classificado em três dimensões diferentes:
1. Primário versus secundário.
2. Essencial versus concreto.
3. Alto nível versus baixo nível.
A descrição do caso de uso de um sistema enfatizará um de cada par de fatores.
Normalmente são utilizados vários agrupamentos para verificar o nível de detalhe cap-
turado nos casos de uso:
■ Primário, essencial, alto nível
■ Primário e secundário, essencial, baixo nível
■ Primário, concreto, baixo nível
■ Primário e secundário, concreto, baixo nível
Gerar uma descrição funcional detalhada e completa de um sistema envolverá a
produção sucessiva de vários tipos de casos de uso, iniciando do topo e operando des-
cendentemente. Seguem descrições dos tipos comuns de casos de uso.
No nível mais abstrato estão os casos de uso primários, essenciais e de alto nível.
Eles, basicamente, definem como o sistema suporta os processos de negócios envolvidos
na obtenção das proposições de valor de negócio. Esses casos de uso, em geral, somente
definem as funções que serão executadas pelo sistema; eles não definem claramente os
inputs e outputs (ou seja, as interfaces ou os serviços associados com o sistema ou atores)
dos detentores do processo ou “processos”. Eles também não definem como o processo
irá desempenhar seu trabalho. Eles são úteis em conseguir um entendimento de alto nível
do fluxo de trabalho. Além disso, nos fornecem uma ordem de magnitude sobre o núme-

10. Isto é semelhante a uma sub-rotina comum utilizada por muitas rotinas.
11. Isto é como a formação de subclasses, em que apenas é permitida a adição de mais código à rotina
parente.
CAP. 4 DELIMITANDO O DOMÍNIO 47

ro de casos de uso detalhados (primários e secundários, essenciais e de baixo nível) que


serão produzidos, e uma estrutura para identificar os fatores críticos de sucesso para o
projeto.12 Por exemplo, podemos utilizar esses casos de uso para priorizar as caracterís-
ticas de acordo com o valor de negócio e o emprego estimado com base na proposição
de valor. Se confiabilidade e casos de uso incomuns e raros forem críticos ao desenho de
nosso sistema, deverão ser também desenvolvidos casos de uso secundários para auxiliar
no dimensionamento do problema.13
Para entendermos um sistema complexo e muito extenso, talvez queiramos o desen-
volvimento de casos de uso essenciais e de baixo nível (ambos primários e secundários).
Quando assim procedemos, podem ser requeridos vários casos de uso para prover os de-
talhes que estão resumidos em um caso de uso primário, essencial e de alto nível. Utili-
zamos esses casos de uso para capturar as interfaces necessárias ao suporte dos objetivos
de negócio. Elas podem ser utilizadas para capturar a seqüência de interações (solicitação
de serviços/eventos) entre o sistema e todos os atores; entretanto, todas as interfaces são
descritas sob uma forma independente da tecnologia. Os casos de uso escritos neste nível
são o mínimo nível necessário para se escrever um contrato de interface para o sistema.
Entretanto, para alguns contratos, são necessários detalhes mais específicos (ou seja,
limitações dependentes da tecnologia). Assim, são desenvolvidos casos de uso primários,
concretos e de baixo nível. Nos casos de uso primários, concretos e de baixo nível, não
somente é capturada a seqüência de interações como também são incluídas as decisões
de desenho baseadas em tecnologia.14 Finalmente, quando nosso sistema também tiver
funções incomuns, raras e críticas, ou tiver robustez como requisito-chave, desenvolveremos
casos de uso primários e secundários, concretos e de baixo nível. Quando levarmos os ca-
sos de uso a esse grau de detalhe, lidaremos com a inicialização, recuperação e com casos
de uso secundários, que são necessários para um sistema robusto.

Informação Capturada. Uma das razões para o emprego de um enfoque de caso de uso
para a delimitação de nosso sistema é a facilidade com a qual a informação pode ser iden-
tificada. Casos de uso podem capturar o seguinte conjunto de informações:15
■ Atores
Um caso de uso identificará todos os atores que dele participam. Em algumas situa-
ções, não haverá um ator explicitamente identificado (uma situação comum no su-
porte de casos de uso), e isto deverá ser notado.16
■ Relacionamentos com outros casos de uso

12. O fator crítico de sucesso em muitos esforços de processos de reengenharia talvez não seja o sistema;
é a implementação bem-sucedida dos novos processos de negócios aos usuários!
13. Estes casos de uso geralmente não são suficientes para a escrita de um contrato de interface para o
sistema.
14. Isto é uma transgressão da arquitetura e/ou desenho. Sim, a teoria é perfeita, mas a prática é mais
vantajosa.
15. Os autores também estenderam isso para incluir emprego de dados, que os auxiliarão a fazer ajustes
no desempenho em sistemas maiores.
16. O propósito dos casos de uso é capturar a interação entre atores e o sistema. Na situação ora
colocada, o ator que está participando é implicitamente identificado pelo caso de uso que o inclui.
Casos de uso incluídos podem ser incorporados em vários casos de uso, cada um deles lidando com
diferentes atores. É por essa razão que declaramos que um caso de uso não poderia, talvez,
identificar explicitamente um ator.
48 UML E C++ CAP. 4

Uma identificação de caso de uso identificará os relacionamentos (generalização/es-


pecialização, relacionamentos “incluir” e “estender”) que os casos de uso têm uns
com os outros.
■ Pré-condições
Um caso de uso poderá requerer a retenção de condições específicas a fim de ser in-
vocado com sucesso. Todas essas condições devem ser identificadas. Em alguns ca-
sos, são planejados sistemas para exibir modalidade no comportamento. Ou seja,
espera-se que eles operem em diferentes modos e exibam diferentes comportamen-
tos para diferentes modos. Uma pré-condição identificará o modo requerido e
quaisquer outras condições que deverão reter para o caso de uso ser válido. Isso in-
clui informação de como a ação é solicitada, confirmação da identidade do usuário,
valores que precisa reter, e qualquer outro fator que afete a conclusão bem-sucedida
do caso de uso.
■ Detalhes17
Um caso de uso descreve os detalhes sobre a forma como um sistema provê algum
serviço. Os detalhes de um caso de uso identificam os detalhes das seqüências de
interações. Os detalhes são capturados como interações gradativas entre objetos do
domínio. Cada etapa provê detalhes suficientes para identificar que entidades estão
envolvidas, o que cada entidade faz e o resultado da etapa. Isto pode ser consumado
pela utilização de diagramas de seqüência ou texto.
■ Pós-condições
A execução de um caso de uso pretende gerar algum cálculo desejado ou estado. As
pós-condições identificam exatamente que resultados são esperados da execução do
caso de uso. Isso inclui quaisquer efeitos colaterais produzidos — tais como quais-
quer objetos criados e todos os objetos destruídos. Recomendamos que sejam espe-
cificadas as seguintes pós-condições:
■ Criação ou destruição de instâncias
■ Relacionamentos (associação e agregação) formados ou rompidos
■ Mudanças de valores em variáveis
■ Mudanças de estado (inclusive o estado final)
■ Exceções
Qualquer ação executada em um caso de uso é suscetível a erro. Dados desejados
poderiam não ser localizados, cálculos poderiam ser abortados e poderia ser perdi-
da a conectividade. É preciso identificar todos os possíveis erros que podem ocorrer
no caso de uso. Além do mais, é útil identificar as ações específicas que devem ser
tomadas para a recuperação. Daí, desejamos saber, para cada exceção, a circunstân-
cia na qual ela pode ocorrer e a providência a ser tomada.
■ Restrições
É necessário também identificar todas as restrições que poderiam se aplicar ao caso
de uso. Essas restrições podem atuar sobre os valores manipulados, recursos a ele
alocados e alocações de recursos para as várias etapas. Existem normalmente as con-
dições invariantes (ou seja, as condições que devem ser sempre verdadeiras). As

17. A seção de detalhes não é desenvolvida para casos de uso de alto nível.
CAP. 4 DELIMITANDO O DOMÍNIO 49

condições invariantes devem ser suportadas no início (pré-condição) do serviço


(operação) e no seu final (operação). A violação dessas restrições pode também ori-
ginar erros, e estes devem ser identificados como uma exceção.
■ Variantes/Alternativas
É necessário também identificar todas as variações que poderiam se aplicar ao caso
de uso. Estas são normalmente as variações não cobertas por casos de uso indepen-
dentes. Normalmente, as variações ou são facilmente tratadas ou consideradas
como parte de outro caso de uso.
Naturalmente que a informação real capturada em um específico caso de uso varia-
rá de acordo com diversos fatores. Em particular, ela variará caso ele seja (1) de alto ou
baixo nível e (2) essencial ou concreto. A Tabela 4.1 resume a informação tipicamente de-
senvolvida para tipos comuns de casos de uso que são desenvolvidos.
A Tabela 4.1 correlaciona informação com os vários tipos de casos de uso. Ela pode
ser utilizada para guiar o desenvolvimento de um caso de uso. Por exemplo, o desenvol-
vimento de casos de uso essenciais, primários e de alto nível requer que sejam identificadas:
■ A informação essencial de negócio (ou seja, estabelecer a proposição do valor de ne-
gócio).
■ As pré-condições que devem ser aplicadas para o caso de uso se completar.
■ As pós-condições que são prometidas.
■ Quaisquer restrições ou variações que possam existir.
Segundo a Tabela 4.1, não estarão sendo identificados relacionamentos entre casos
de uso, detalhes ou exceções, pois estas informações não são apropriadas para este tipo
de caso de uso.

TABELA 4.1 Informações Associadas aos Diferentes Tipos de Casos de Usoa


Alto Nível Baixo Nível Baixo Nível Baixo Nível
Primário Primário e Primário Primário e
Essencialb Secundário Concreto Secundário
Essencial Concreto

Atores B B B,T B,T


Relacionamentos R R
Pré-condições E E E E
Detalhes H H D D
Pós-condições E E E E
Exceções A A
Restrições A A A A
Variantes A A A A
a
Chave para os dados da tabela: B — informação de negócio essencial; T — informação tecnológica
essencial descrevendo relacionamentos entre casos de uso primários e secundários, estendidos e
incluídos; E — informação essencial; H — informação de alto nível sobre a interação entre sistema e
atores; D — informação concreta detalhada que se aplica a casos de uso que não são generalizações;
A — informação incluída como apropriada para o caso de uso específico (note que nem todos os
casos de uso terão exceções, restrições ou variações).
b
Casos de uso estendidos ou incluídos seguem as diretrizes para o tipo de caso de uso com os quais
eles estão relacionados.
50 UML E C++ CAP. 4

Grupos de Casos de Uso


Grupos de casos de uso são coleções de casos de uso que estão estreitamente correlacio-
nadas com alguma atividade ou elemento organizador de negócio. Eles fazem sentido so-
mente quando o valor de negócio provido por um sistema necessita de significativa
funcionalidade, tal como poderia ser encontrado em um sistema de aeronave por controle
eletrônico, um programa de CAD — Computer Aided Design (projeto assistido por compu-
tador) — ou um software para uma chave de telecomunicações.18 Para facilitar o enten-
dimento, um domínio pode ser particionado em unidades menores de forma que
adquirimos uma maior percepção sobre o domínio pelo estudo das partes. Alguns crité-
rios básicos para o agrupamento de casos de uso em um pacote são descritos na relação
seguinte. É altamente provável que os termos da simples descrição dos casos de uso in-
dicará claramente quais são os melhores critérios para o estabelecimento de grupos de ca-
sos de uso.
■ Mesmo Ator — Mesmo Estado
Um pacote extremamente simples é atingido agrupando-se todos os casos de uso
iniciados pelo mesmo ator no mesmo estado do sistema. Um sistema grande como
esse pode ter vários estados diferentes a partir dos quais diferentes atores fazem so-
licitações. Esse esquema de empacotamento com freqüência resultará em um con-
junto balanceado de grupos que, individualmente, encerram um número razoável
de casos de uso.
■ Entidades Comuns
Muitas vezes faz mais sentido agrupar de acordo com os casos de uso que lidam
com as mesmas entidades. Nesse caso, múltiplos atores podem ser envolvidos no
gerenciamento de diferentes aspectos da mesma entidade. Por exemplo, uma loja
eletrônica baseada na Web poderá ter um componente do software que trata dos pe-
didos dos clientes. Assim, poderemos ter atores que geram esses pedidos (clientes),
atores que efetuam a cobrança dos pedidos (sistema de transação via cartão de cré-
dito), atores que remetem os pedidos (funcionários do departamento de entregas) e
atores que desejam personalizar o inventário para dar um melhor suporte aos clien-
tes (administradores). O único ponto em comum que reúne todos esses atores é “pe-
didos” e “gerenciamento de pedidos”. Agrupar todos esses casos de uso faz sentido,
particularmente quando houver uma outra entidade (tal como “produto” e “geren-
ciamento do produto”) que é o foco de muitos atores também. Este agrupamento
foca elementos comuns.
■ Fluxo de Trabalho Específico
De forma alternativa, um sistema que suporta atores na realização de seus serviços
pode ser agrupado usando o fluxo de trabalho. Nessa situação, um grupo descreve
um conjunto de atividades realizadas por um único ator no curso da realização de

18. Sistemas que possuem interfaces de usuário extensivas ocasionalmente se enquadram nesta
categoria. Editoração visual e programas multimídia, em que se pode posicionar vários objetos de
negócio, estabelecer relações entre eles, editar o modelo, validá-lo e distribuí-lo entre diversos
indivíduos, são complexos o bastante para que haja uma possível ocorrência de muitas seqüências
de eventos.
CAP. 4 DELIMITANDO O DOMÍNIO 51

algum aspecto de seu trabalho. Os casos de uso descrevem diferentes caminhos pelo
fluxo de trabalho. Esta abordagem de agrupamento de casos de uso é similar ao en-
foque dos pontos de vista.19

Documentando Casos de Uso


Um aspecto importante dos casos de uso é documentá-los de um modo que proporcione
uma maior compreensão do domínio. Como uma imagem vale por um milhão de pala-
vras, um modo altamente informativo e proveitoso de documentar casos de uso é pela
utilização da notação da UML.

Diagrama de Caso de Uso


Um diagrama de caso de uso mostra de que forma os casos de uso estão correlacionados
uns com os outros e com os atores. Um exemplo de diagrama de caso de uso é apresen-
tado na Figura 4.3. Um ator é ilustrado como uma figura de stick man (pessoa), mesmo
em situações de sistemas externos. Casos de uso individuais são representados como fi-
guras ovais identificadas pelo nome do caso do uso. Linhas conectam atores ao caso de
uso por eles iniciado ou conectam os casos de uso aos atores participantes a partir dos
quais o sistema faz solicitações. Se a interação é unidirecional, a linha é terminada por
uma seta. O sentido da seta é apontado do solicitador ao provedor de um serviço. No
exemplo, o gerente de banco faz uma solicitação ao sistema, que, por sua vez, faz uma
solicitação ao banco de dados de contas.

FIGURA 4.3 Um exemplo de diagrama de caso de uso ilustrando generalização/especializa-


ção de atores, generalização/especialização de casos de uso e interação bidire-
cional entre um caso de uso e um ator.

19. A abordagem dos pontos de vista é baseada na visualização de um sistema desde uma perspectiva
de estudos etnográficos. Ela enfatiza a forma como um indivíduo trabalha para resolver um
problema.
52 UML E C++ CAP. 4

Em algumas situações, a interação é bidirecional e o vínculo é ilustrado como uma


linha com setas nas duas extremidades. Nessa situação, o ator pode fazer um pedido ao
sistema ou este pode solicitar ao ator para efetuar alguma ação. Esse seria o caso em que
o gerente necessita monitorar contas específicas para atividades particulares, e o sistema
gera alertas solicitando a atenção do gerente quando ocorrerem eventos específicos. O sis-
tema poderá fazer perguntas ao banco de dados para identificar quando ocorrerá o evento.
As generalizações/especializações de caso de uso e de ator que foram previamente
identificadas também são documentadas em diagramas de casos de uso. O relacionamen-
to é ilustrado com um triângulo na extremidade de uma linha. As linhas originam-se nas
especializações e apontam na direção da generalização. No exemplo, o ator funcionário
de banco é uma generalização da especialização contendo os atores caixa de banco e ge-
rente de banco. A idéia nesse ponto é que alguns casos de uso (à procura de balanço de
contas, mudança de endereços de clientes etc.) podem ser executados quer por um geren-
te de banco, quer por um caixa de banco.
A UML tem um mecanismo para a ilustração dos casos de uso compatíveis (de su-
porte); ou seja, uma notação para os relacionamentos do tipo “estender” e “incluir”. Uma
seta tracejada é desenhada entre o caso de uso de suporte e o caso de uso que ele suporta,
com a seta identificada pelo tipo de relacionamento representado. O sentido da seta in-
dica a direção do relacionamento. Para casos de uso que estendem outros casos de uso,
a seta aponta a partir do caso de uso “que estende”. A seta aponta na direção do caso de
uso “incluído”. Isso é ilustrado na Figura 4.4. O caso de uso Efetuar Depósito inclui o caso
de uso Obter Informação do Cliente. O caso de uso Fazer Depósito Eletrônico estende o
caso de uso Efetuar Depósito pela adição de funcionalidade.

FIGURA 4.4 Representando as relações <<estender>> e <<incluir>> entre casos de uso na


UML.

Diagrama de Seqüência: Documentando os Detalhes


Os detalhes de um caso de uso podem ser documentados utilizando-se diagramas de se-
qüência20. Um diagrama de seqüência mostra a ordem na qual mensagens são trocadas
entre o(s) ator(es) e o sistema. O diagrama de seqüência tem os participantes representa-
dos por caixas retangulares. Partindo dessas caixas retangulares, há uma linha estendida
tracejada e vertical. As trocas de mensagens entre participantes são ilustradas como setas
dirigidas e são identificadas pela mensagem que está sendo comunicada. A seqüência de
mensagens é lida de cima para baixo. Assim, o tempo transcorre de cima para baixo.

20. Os diagramas de seqüência são discutidos mais adiante no Capítulo 7 deste livro.
CAP. 4 DELIMITANDO O DOMÍNIO 53

Quando a linha tracejada é substituída por um retângulo, isso indica que o objeto está ati-
vo e utilizando recursos durante aquele período de tempo.
Um exemplo de um diagrama de seqüência capturando o caso de uso de “abrir con-
ta” é ilustrado na Figura 4.5. Este exemplo mostra como um gerente de banco solicita ao
sistema para criar uma nova conta. O sistema pede informações sobre o cliente, que são
providas pelo gerente de banco. A seguir, o sistema solicita ao gerente de banco para que
este identifique o tipo de conta, que novamente é provido pelo gerente de banco. O sis-
tema solicita a informação sobre o saldo inicial, também provida pelo gerente de banco.
Uma vez que todas essas informações são obtidas, o sistema solicita ao banco de dados
a geração de uma nova conta. O gerente de banco é, então, informado pelo sistema que
foi criada uma conta com sucesso.

FIGURA 4.5 Diagrama de seqüência ilustrando os detalhes da interação entre o sistema e os


atores para o caso de uso Abrir Conta.

Descrição Textual
Além das representações gráficas dos casos de uso, é prática comum utilizar descrições
textuais de casos de uso individuais. Uma descrição textual contém as informações iden-
tificadas na seção anterior. Um modelo típico é ilustrado na Figura 4.6. O principal valor
das descrições textuais é que são capturadas mais informações do que nas descrições grá-
ficas. Em conseqüência, a maioria dos profissionais utiliza uma combinação de diagramas
de casos de uso para prover um panorama geral do sistema: diagramas de seqüência para
captura das interações e descrições textuais para capturar as pré-condições, pós-condi-
ções, exceções, condições invariantes e variações.
54 UML E C++ CAP. 4

Termos em Casos de Uso


Descrição: Uma descrição, com uma ou duas sentenças, do caso de uso.
Atores: Identifica os atores participantes no caso de uso.
Inclusões: Identifica os casos de uso nele incluídos.
Extensões: Identifica o caso de uso que ele poderá estender.
Pré-condições: Identifica as condições que devem ser atendidas para invocar este caso de uso.
Detalhes: Identifica os detalhes do caso de uso.
Pós-condições: Identifica as condições que lhe são asseguradas reter na conclusão do caso de uso.
Exceções: Identifica quaisquer exceções que possam surgir na execução deste caso de uso.
Restrições: Identifica quaisquer restrições que porventura se apliquem.
Variantes: Identifica quaisquer variações que poderiam suportar o caso de uso.
Comentários: Provê novas informações provavelmente importantes para este caso de uso.

FIGURA 4.6 Modelo para a documentação de casos de uso.

Diretrizes para o Desenvolvimento de Casos de Uso


Esta seção apresenta um conjunto de práticas que os autores constataram serem úteis
para o desenvolvimento de descrições de casos de uso. As diretrizes são aqui apresenta-
das para ajudar a evitar o fenômeno da paralisia de análise (analysis paralysis). Esse tipo
de paralisia ocorre quando o analista é incapaz de escrever quaisquer cenários ou criar
cenários muito detalhados. O propósito da modelagem de casos de uso é entender o com-
portamento externo do sistema. Os estágios finais ficarão muito mais fáceis se os requisi-
tos de up-front (iniciais) forem claros e definidos.
As seções seguintes provêem diretrizes para áreas problemáticas com as quais mui-
tas pessoas se deparam quando tentam desenvolver descrições de casos de uso. As áreas
básicas são:
■ Evitar Paralisia de Análise
■ Identificar Atores
■ Identificar Casos de Uso Essenciais e de Alto Nível
■ Estabelecer Grupos de Casos de Uso
■ Desenvolver Detalhes de Casos de Uso
■ Identificar Casos de Uso Compatíveis (de Suporte)
■ Desenvolver Casos de Uso-Limite

Evitar Paralisia de Análise


As diretrizes a seguir são especialmente orientadas para que se evite o fenômeno da pa-
ralisia de análise:
1. Escreva, primeiramente, duas ou três das transações simples mais comuns.
2. Tente criar cenários mais “abstratos” quando dois ou três deles parecerem muito
similares.
3. Seja precavido em criar mais de 30 casos de uso para cobrir as ações fundamen-
tais do sistema.
4. Casos de uso complementares para eventos incomuns deverão ser selecionados
com cuidado e mantidos em um número administrável.
CAP. 4 DELIMITANDO O DOMÍNIO 55

5. Faça a análise sob uma forma incremental. Primeiro, desenvolva um modelo de


caso de uso essencial, primário e de alto nível. Depois, utilize esse modelo para
desenvolver um modelo de caso de uso essencial, primário e secundário e de
baixo nível. Em seguida, utilize o modelo resultante para guiar o desenvolvi-
mento de casos de uso concretos, primários e de baixo nível.
6. Dentro de um modelo, desenvolva-o interativamente. Inicialmente faça descri-
ções mais breves e, em seguida, as refine.

Identificar Atores
É crítico identificar todas as entidades externas que interagirão com o sistema. Cada en-
tidade externa é um ator. Quando os atores são identificados, eles precisam ser documen-
tados. Em particular, devemos identificar todas as responsabilidades que cada um tem de
cumprir utilizando o sistema ou provendo-o. Isso nos ajuda a identificar o conjunto de
ações que o ator executará em relação ao sistema. Temos as seguintes diretrizes:
1. Identifique primeiramente os atores e, em seguida, documente suas responsabi-
lidades. Isto impede que nos envolvamos demais documentando um ator e es-
queçamos outros.
2. Foque esforços sobre os atores que iniciam ações no sistema. Esses são os atores
mais fáceis de identificar, e, caso não sejam identificados nesta etapa, provavel-
mente permanecerão omitidos durante um longo tempo. Outros atores, a partir
dos quais são feitas solicitações, aparecerão mais tarde no processo se forem
ocultados nesta fase.
3. Identifique os diferentes papéis que um indivíduo em particular poderia assu-
mir com relação ao sistema e introduza atores para cada papel. Isto nos ajuda a
identificar diferentes modos de operação que o sistema, talvez, tenha de exibir.
Muitas vezes é difícil identificar todos os atores que interagem com um sistema.
Nesta seção, quatro categorias de alto nível de usuários são discutidas para ajudar no
provimento de um modo sistemático de identificação de atores:
■ Usuários
Embora possamos pensar em um específico indivíduo como um usuário, queremos
considerar estes indivíduos em relação ao papel que eles desempenham frente ao
sistema. Portanto, uma pessoa que acrescente dados, utiliza-os e gere relatórios está
atuando em três papéis diferentes que serão refletidos como três diferentes atores.
É fácil omitir indivíduos que empregam o sistema. Dada a grande variedade de pa-
péis para os possíveis usuários, somente podemos fornecer uma breve lista de sub-
categorias de uso que talvez seja útil:
■ Usuários finais-alvo
■ Administradores
■ Gerentes
■ Clientes21

21. Os autores trabalharam em sistemas nos quais o cliente exigia (demandava) uma interface que lhes
permitisse obter uma melhor visão interna do sistema e da forma como os usuários o empregavam.
A despeito do custo e do esforço em criar essas interfaces, a maioria delas nunca chega a ser
utilizada; a não ser por testadores de sistemas.
56 UML E C++ CAP. 4

Cada uma dessas subcategorias pode descrever diferentes indivíduos e compreen-


der múltiplos atores para capturar os vários papéis de cada indivíduo.
■ Aplicações
Todas as aplicações externas (processos individuais e sistemas de software) que in-
teragem com o sistema são atores.22 Por enquanto, devemos ignorar fronteiras de
máquinas porque elas não foram estabelecidas. Isso significa que aplicações que,
eventualmente, executarão na mesma plataforma de nosso sistema serão tratadas
como aplicações externas sob a perspectiva das fronteiras de nosso sistema.
Deve ser tomado cuidado quanto a estabelecer se diferentes processos de um dado
sistema externo estão diretamente envolvidos, pois cada um deles será um ator em
separado. Não inclua processos dentro do sistema hipotético que está sendo descrito
(mesmo se você achar que eles façam parte do sistema) porque o propósito neste
caso é estabelecer o domínio da aplicação. Ou seja, queremos desenhar uma frontei-
ra entre a aplicação e o mundo exterior.
■ Dispositivos
Identifique todos os dispositivos que interagem com o sistema. Normalmente, isto
não compreende itens como monitores, teclados, mouses e outros tipos padrão de
dispositivos que servem de interfaces ao usuário.23 Em vez disso, estamos falando
de atuadores e sensores.
■ Eventos Externos (por exemplo, Cron)
À medida que formos construindo mais sistemas interativos assíncronos e de tempo
real, o reconhecimento de eventos externos como potenciais atores estará se tornan-
do mais importante. Por exemplo, é situação comum que a atividade de um sistema
seja inicializada pela passagem do tempo. Como resultado, o tempo pode ser trata-
do como se fosse um ator. Os autores denominam esse ator de Cron24 depois do
surgimento do mecanismo UNIX para invocar funcionalidade em determinados
momentos. Enquanto o Cron pode ser implementado como um controlador de tem-
po dentro do sistema como um todo, o controlador ainda é acionado por um relógio
externo ao programa. O Cron pode ser utilizado para capturar atividades periódicas
e tempos de espera. Se mais do que um elemento desencadeador periódico for ne-
cessário, então vários atores Cron poderão ser introduzidos no sistema.
Essas categorias devem ser utilizadas para auxiliar na identificação de atores em um
modo de sistema. Elas são bastante comuns nos sistemas, que, utilizando-as como um ele-
mento desencadeador, possibilitarão a descoberta de atores.

22. Isto não inclui os serviços do sistema operacional ou da estrutura. Aqui, estamos falando sobre
aplicações de negócios com as quais o sistema talvez necessite interagir para atingir seus objetivos
de negócio.
23. Dispositivos de interface de usuários são importantes se alguém estiver desenvolvendo estruturas
para interface de usuários. De modo geral, pode-se supor que um conjunto apropriado de classes de
estrutura controlará a interação com esses dispositivos. Reconhece-se que algumas pessoas tratam
dispositivos como interfaces para os atores. Essa prática possibilita a um único sensor representar
diversos papéis diferentes em relação ao sistema.
24. O Cron recebeu este nome devido ao titã Cronus, filho de Urano e Gaea. Ele era o deus grego do
destino.
CAP. 4 DELIMITANDO O DOMÍNIO 57

Identificar Casos de Uso Essenciais e de Alto Nível


Partindo-se de uma perspectiva de modelagem, um caso de uso deve capturar a série de
interações entre um ator e o sistema que atinja algum objetivo de negócio útil para quem
inicia a interação. A identificação de responsabilidades dos atores é uma boa base a partir
da qual podem-se encontrar razões para o ator interagir com o sistema. Para todos os ato-
res, faça as seguintes perguntas:
■ Quais são os processos em que eles participaram nos quais atingiram algum objeti-
vo de negócio?
■ Como, no processo, eles utilizam os serviços do sistema para concluir suas tarefas
para o alcance do objetivo?
■ Quais são os processos fundamentalmente diferentes nos quais eles participaram?
■ Qual é o evento inicial que dá partida ao processo?
■ Qual é o evento que inicia a série de interações com o sistema?
Cada processo que atinja um objetivo de negócio útil é, definitivamente, um caso de
uso. Os outros processos fundamentalmente diferentes nos quais eles participam geral-
mente resultam em outros casos de uso. Embora a prática padrão de documentação de
casos de uso deva começar do evento externo ao sistema, em certas circunstâncias pode-
se querer documentar o caso de uso do evento externo ao ator.
Para documentar os casos de uso identificados, pode-se simplesmente construir
uma tabela25 com (1) o nome do caso de uso, (2) o ator iniciador, (3) a solicitação de ser-
viço ao evento (ação) que um ator inicia, (4) uma breve descrição e (5) o objetivo de ne-
gócio daquela solicitação de serviço inicial ou evento. Essa tabela é utilizada para
desenvolver os casos de uso. Existirá pelo menos um caso de uso para cada evento; mas,
em certas circunstâncias, poderemos precisar de mais de um caso de uso para capturar
as informações necessárias.
Muito embora tenhamos um propósito de identificar todos os casos de uso que cada
ator é capaz de iniciar, com freqüência vemos situações em que alguns usos do sistema
são omitidos. Como conseqüência, essa tabela será atualizada com novos dados à medida
que o modelo de caso de uso for sendo desenvolvido em etapas posteriores.
Seguem algumas diretrizes para a consecução desta etapa:
1. Ao identificar os casos de uso, dê um nome descritivo e faça uma descrição, com
uma ou duas sentenças, de cada um deles. Nomeie os casos de uso com base no
objetivo que o ator está tentando conseguir e, caso necessário, para distinguir va-
riações na circunstância em que ele é invocado. Utilize um verbo para iniciar o
nome do caso de uso. A descrição com uma ou duas sentenças serve para iden-
tificar a interação aproximada que deverá ser capturada no caso de uso. Todas
essas informações deverão aparecer em colunas separadas na tabela.
2. Não pule etapas, nem utilize o modelo descrito em uma etapa posterior desse
enfoque como seu mecanismo solo para a documentação destes casos de uso. É
preciso obter um bom panorama geral dos casos de uso (tal como o que se con-
segue em uma tabela) para realizar as etapas seguintes.

25. Os autores agregam ainda valor econômico ao cliente, freqüência de uso e distribuição de emprego
a essa tabela para ajudar a priorizar os casos de uso (correspondentes a propriedades) e dimensionar
os requisitos de performance.
58 UML E C++ CAP. 4

3. Não introduza detalhes em demasia nas descrições básicas. É normal que uma
descrição pareça trivial até a hora em que se complete a documentação de um
caso de uso. O mérito de mantê-la simples é para dar-nos uma “empurrada
mental” quando estivermos, mais tarde, “atolados” em detalhes.
4. É importante distinguir a solicitação de serviço ou notificação de evento que o
ator está iniciando da maneira (ação) com que o ator invoca a solicitação ou no-
tificação de evento. Em muitos casos, a mesma solicitação de serviço pode ser
invocada de diversas maneiras: por toques no teclado, itens de menu ou botões.
Entretanto, as atividades resultantes do sistema são idênticas. É o último com-
ponente que estamos tentando capturar em casos de uso.
5. Evite casos de uso dependentes da tecnologia26 (fazer download, salvar, inicia-
lizar e interromper) neste momento. Ainda estamos abordando casos de uso vol-
tados a negócios. Não há informações disponíveis a ponto de identificarmos
eficazmente um comportamento apropriado. Esses casos de uso serão os últimos
a serem desenvolvidos porque devemos esperar até que sejam conhecidos deta-
lhes suficientes para identificar que informações devem ser iniciadas durante a
inicialização (startup) e preservadas durante a interrupção de atividades (shut-
down).
6. Identifique primeiramente as atividades gerais. Essas atividades constituem ca-
sos de uso de alto nível, que são, de fato, definidos por um conjunto de casos de
uso de baixo nível.27 Os casos de uso de baixo nível estão onde se encontram
identificadas as atividades especiais. (Exemplo, o gerenciamento de contas é um
caso de uso de alto nível, enquanto que acrescentar, atualizar e eliminar uma
conta são casos de uso de baixo nível, que estabelecem atividades detalhadas
para o gerenciamento de uma conta.)
7. Documente imediatamente atores identificados como resultado da descrição de
um caso de uso. Esses atores precisarão ser documentados em termos de quais
ações eles são requeridos para prover.
8. Se múltiplos atores podem iniciar o mesmo conjunto de ações, introduza um
ator abstrato a partir do qual todos os outros são especializações. Isso simplifica
o desenvolvimento dos casos de uso no futuro e, igualmente, proporciona refle-
xão sobre os vários graus de acesso à funcionalidade do sistema de que diversos
indivíduos são providos.

Estabelecer Grupos de Casos de Uso


Esta atividade é opcional e somente deverá ser executada se estivermos lidando com um
sistema em escala muito grande, que envolva centenas de casos de uso.28 Conseguir o

26. Segundo alguns especialistas, há vários tipos de casos de uso. Para os especialistas em casos de uso,
estamos trabalhando em casos de uso essenciais, e não em casos de uso reais.
27. Há uma enorme controvérsia sobre quais casos de uso necessitam ser documentados. Sugerimos que
se use o bom senso em vez de seguir algumas regras insanas com um milhão de exceções. O caso
de uso é para colaborar conosco na limitação do domínio do negócio e para facilitar a etapa de
desenho. Procure documentar os casos de uso consistentes com estes objetivos.
28. Até mesmo com 30 casos de uso, não é incomum para o diagrama de contexto encerrar múltiplas
páginas. O posicionamento de casos de uso em várias páginas pode seguir as mesmas diretrizes ora
apresentadas.
CAP. 4 DELIMITANDO O DOMÍNIO 59

controle deles pode ser uma tarefa desencorajadora. A meta nesse caso é particionar os
casos de uso em pacotes significativos compreensíveis. Cada pacote constitui um contexto.
Nesta atividade, nós devemos adotar um conjunto de critérios que produza uma or-
ganização eficaz dos casos de uso. Os critérios identificados na seção onde foi introduzi-
do contexto deverão ser considerados. Veja a seguir algumas coisas para se ter em mente
quando da seleção de critérios por meio dos quais se desenvolvem os grupos:
■ Nenhum grupo de caso de uso deverá conter um número excessivo de casos de uso
(tal como poderia resultar da descrição de todo o sistema). Isso supera o propósito
de particionar o domínio em um primeiro exame.
■ Não é particularmente útil ter grupos que contém apenas um ou dois casos de uso.
Novamente, isso supera o propósito de particionar os casos de uso. Nós não sere-
mos capazes de ver a floresta se quisermos ver todas as árvores.
■ Os grupos precisam fazer sentido da perspectiva do domínio. Não ajuda termos ca-
sos de uso desiguais agrupados sem algum aspecto subjacente do domínio unindo-
os todos.

Desenvolver Detalhes de Casos de Uso


Uma vez que identificamos os casos de uso e os organizamos em grupos significativos,
podemos começar a desenvolver descrições detalhadas de cada caso de uso. Trabalhe
com um grupo de cada vez, conforme documentado em seu diagrama de contexto. Mui-
tas vezes, se esses casos forem bem-desenvolvidos, alguns membros de uma equipe de
desenvolvimento poderão prosseguir com outras atividades de análise enquanto os gru-
pos remanescentes estiverem sendo documentados.
Sugerimos a utilização do modelo ilustrado na Figura 4.6. Ele captura todas as in-
formações associadas com casos de uso, organiza-as de uma maneira consistente da for-
ma como os detalhes são identificados e provê uma seção de comentários em que pode
ser registrada informação complementar. O emprego deste modelo ajuda a assegurar que
todos os casos de uso sejam documentados exatamente da mesma maneira. A seguir, te-
mos algumas diretrizes básicas:
1. Ao preencher o modelo não deixe trechos do documento em branco que você
determinou não serem aplicáveis. Uma notação apropriada deverá aparecer na-
queles trechos — isto permite que outros saibam o que tem sido considerado na
verdade.
2. Inicie cada caso de uso em uma nova página. Na prática real, os casos de uso
são reescritos muitas vezes antes de se tornarem estáveis. O processo de iniciar
um novo caso de uso em uma nova página possibilita-nos imprimir somente o
caso modificado sem deixar os próximos casos de uso desorganizados ou sem
eliminar uma árvore extra pela impressão de todos os casos de uso.
3. Pense na descrição do caso de uso sob a perspectiva do ator.
4. Enfatize interações (solicitações de serviço) e eventos entre o sistema e o ator
sem decompor o processamento interno do sistema.
5. Discussões do processamento interno ou decisões de desenho podem ser neces-
sárias e é permissível (pelo emprego prático desta técnica) o descobrimento de
atores participantes, mas isso deverá ser feito com o entendimento das conse-
qüências (ou seja, compromisso inicial do desenho) e, obrigatoriamente, será do-
cumentado na seção de detalhes.
60 UML E C++ CAP. 4

6. Variações do caso de uso, que não serão cobertas com casos de uso independen-
tes, também deverão ser documentadas na seção de variantes.
Detalhes Complementares para Casos de Uso Primários e Secundários, Essenciais
e de Alto Nível. Conforme definido anteriormente, os casos de uso são mas fáceis
de ser desenvolvidos em um modo interativo. Em cada interação, o objetivo é pro-
ver um maior número de detalhes sobre o caso de uso. Se estivermos utilizando o
modelo que acabamos de apresentar, os primeiros poucos passos se concentrarão na
metade superior desse modelo. Em particular, focaremos as seguintes seções do mo-
delo: Atores, Inclusões, Extensões, Pré-condições e Pós-condições. Isso possibilita-
nos desenvolver detalhes e colocá-los em um estado estável antes de abordarmos a
metade inferior do modelo, que é estreitamente correlacionada com a metade supe-
rior. Algumas diretrizes que devem ser seguidas durante as primeiras interações são
apresentadas a seguir:
1. Comece de maneira simples e, lentamente, introduza complexidade. Foque pri-
meiramente o caso simples, em que tudo é perfeito e não existem problemas.
Não é uma má idéia fornecer um conjunto muito breve de detalhes inicialmente
para cada caso de uso, enfocando somente as características em andamento. Isso
permite que se possam identificar casos de uso compatíveis (de suporte), que
simplificam os processos ao extraírem detalhes comuns de outros casos de uso.
2. A preocupação acerca de telas pode ocasionar dificuldades na escrita de casos
de uso. Muitas vezes, chega-se na metade de um caso de uso e, então, começa-se
descrevendo com o que uma tela em particular se parece. A descrição pode pros-
seguir por várias páginas se o layout dessa tela for complexo. Em vez disso, se
deveria somente identificar os objetos presentes na tela. Mesmo assim, somente
focar aqueles objetos que se aplicam ao caso de uso. Não se preocupe sobre o
layout dos botões e dos campos na tela. É a interação com a tela que é impor-
tante no caso de uso. Isso pode ser feito com uma figura na seção de comentários
da descrição do caso de uso ou em uma descrição separada do caso de uso.
3. Incluir grandes quantidades de ifs e jumps nos detalhes interfere com o entendi-
mento do domínio. Praticamente todas as pessoas já ouviram falar do código
“spaghetti”, mas utilizar ifs e jumps em casos de uso provoca o surgimento do
texto “spaghetti”. Os problemas são os mesmos entre o código “spaghetti” e
o texto “spaghetti” — ninguém consegue compreender o que se pretende
(nem mesmo o autor).
4. É importante identificar cada etapa que apareça dentro da seção de detalhes do
modelo com um número. Isso nos possibilita fazer referências cruzadas àquela
etapa em outras seções do caso de uso (e pelos casos de uso) e é extremamente
importante no tocante à identificação de exceções e restrições.
Detalhes Complementares para Casos de Uso Primários e Secundários, Concretos
e de Baixo Nível. Ao lidarmos com casos de uso primários e secundários, concretos
e de baixo nível, estamos introduzindo detalhes que chegam às raias do desenho.
Nesse ponto, estamos perscrutando a estrutura do sistema em vez do sistema em si.
Por exemplo, algumas mensagens poderiam ser traduzidas em solicitações de ser-
viço sobre objetos do domínio que são capturados dentro do sistema. Se tivermos
um objeto “conta”, então, poderemos incluir dentro dos detalhes do caso de uso
CAP. 4 DELIMITANDO O DOMÍNIO 61

uma declaração no sentido de que para o objeto “conta” é enviada uma mensagem
atualizada. Temos a seguir algumas diretrizes que se aplicam nestes casos de uso:
1. O desenvolvimento destes casos de uso somente deveria ser tentado por indiví-
duos com boas aptidões de desenho. Um problema encontrado extremamente
comum é que se corre o risco de especificar um desenho desprovido de recursos
para estes casos de uso.
2. O uso freqüente da palavra sistema em cada descrição detalhada indica outro
problema. Normalmente, há um elemento que falta no desenho. Esse elemento
extraviado é, com freqüência, um objeto de controle que administra uma intera-
ção entre muitos diferentes objetos. Se esses objetos de controle não forem intro-
duzidos no modelo, o resultado é um objeto de sistema “inchado”, que tem de
controlar interações entre milhares de objetos.
3. Para cada etapa na seção de detalhes, identificar quais erros ou alternativas po-
dem ocorrer. Cada erro é examinado em termos de quais ações deveriam ser
executadas para manter o modelo consistente. As informações necessárias para
identificar que ações deveriam ser realizadas é, de modo geral, clara desde o
contexto no qual ocorre o erro.
4. Capturar exceções em uma tabela que compreende três colunas: a etapa na qual
o erro ocorre, uma identificação para o erro e as ações que deverão ser realiza-
das. De forma semelhante ao caso da seção de detalhes, é útil numerar cada eta-
pa nas ações a serem realizadas (iniciando com 1, para cada exceção).
5. Na seção de detalhes de um caso de uso inclua outro caso de uso, identificando
todas as exceções que o caso incluído possa lançar para aquela etapa. Isso pos-
sibilita ao caso incluído identificar a condição de erro para a qual ele precisa rea-
gir. Naturalmente, essas exceções serão identificadas na seção de exceções da
descrição para o caso de uso incluído.

Identificar Casos de Uso Compatíveis (de Suporte)


Casos de uso compatíveis (de suporte) são aqueles incluídos em outros casos de uso ou
que estendem um outro caso de uso. Estamos envolvidos com três tipos diferentes de ca-
sos de uso: os incluídos, os que estendem e a generalização. Seguem algumas diretrizes
para a identificação e utilização de casos de uso compatíveis (de suporte):
1. Visualize os casos de uso incluídos como uma relação que identifica um caso de
uso que atua como uma sub-rotina em relação a outros casos de uso. Normal-
mente, casos de uso incluídos não terão atores que os iniciem. Podemos consi-
derar esses casos de uso como atores de herança.
2. A seção de pré-condições da descrição de um caso de uso deveria identificar que
informações são requeridas para que o caso de uso execute normalmente. Se es-
tivermos escrevendo um caso de uso “salvar”, uma informação que poderia ser
requerida é o nome de arquivo.
3. Em alguns casos, diversos casos de uso compartilharão uma estrutura comum
com a exceção de algumas etapas extras de menor importância. Esses casos po-
dem ser simplificados como uma extensão de um caso de uso comum de núcleo.
Sendo assim, o caso de uso explora os detalhes de outro caso de uso e identifica
onde os detalhes extras são incorporados.
62 UML E C++ CAP. 4

4. A seção de pré-condição de um caso de uso que estende um outro identifica a


condição que determina se a extensão deveria ser invocada.
5. Em alguns casos, a mesma atividade geral poderá ser realizada em vários casos
de uso, mas existem detalhes significativamente diferentes dependentes das en-
tidades que neles participam. Mesmo que as generalizações tenham sido identi-
ficadas em um estágio anterior a este ponto, ainda será uma boa idéia examinar os
casos de uso para determinar se podem ser acrescentadas novas generalizações.

Desenvolver Casos de Uso-Limite29


A situação mais comum encontrada entre pessoas que escrevem casos de uso pela pri-
meira vez é que elas imediatamente começam escrevendo casos de uso para iniciar e pa-
rar o sistema. O principal problema é que elas nem mesmo sabem o que o sistema deve
fazer, ainda que estejam preocupadas sobre quais atividades de inicialização precisam
ocorrer. A seguir, temos algumas diretrizes para estabelecer quando os casos de uso-limi-
te deverão ser desenvolvidos:
1. As atividades de inicialização são altamente dependentes do desenho. Se estiver-
mos desenvolvendo casos de uso essenciais, então não haverá informações a
ponto de identificar que ações deverão ser executadas durante as fases de inicia-
lização (startup) e interrupção de atividades (shutdown). Esses eventos não deve-
rão ser desenvolvidos para casos de uso essenciais.
2. Se estivermos desenvolvendo casos de uso concretos, os casos de uso-limite so-
mente deverão ser tratados depois que todos os casos de uso essenciais e secun-
dários tiverem sido desenvolvidos. Nesse ponto, é preciso ter detalhes
suficientes para identificar se deverão ou não ser criadas conexões a atores ex-
ternos durante a inicialização, se deverão ser construídos detalhes estruturais es-
pecíficos e assim por diante.

Contratos
Os contratos correspondem a um mecanismo introduzido por Bertrand Meyers no desen-
volvimento de Eiffel para desenvolver módulos de software confiáveis, extensíveis e reu-
tilizáveis. A idéia tem sido adotada por outros pesquisadores no desenvolvimento do
método BON para análise e desenho orientados a objeto.
A idéia básica é que um contrato deve ser um acordo formal que expresse os direi-
tos e obrigações entre um cliente e um prestador de serviço. O contrato estabelece as con-
dições sob as quais é provido um serviço. Ele identifica que condições (pré-condições) o
cliente deve satisfazer para a solicitação ser atendida com sucesso e o que o provedor de
serviço garante quanto a um resultado (pós-condição) se for satisfeita a pré-condição. O
contrato pode ser resumido como se segue:
Se uma rotina de chamada promete requisitar nosso serviço com a pré-condição sa-
tisfeita, então o serviço (método) promete retornar com um estado final no qual a
pós-condição é satisfeita.

29. Os autores utilizaram o termo limite para indicar uma atividade que ocorre no início e no fim das
atividades de um sistema.
CAP. 4 DELIMITANDO O DOMÍNIO 63

O conceito de confiabilidade de contratos é baseado nos seguintes princípios:


■ Um sistema ou elemento de software não é correto ou incorreto per si.
Um sistema ou elemento de software somente pode ser consistente ou inconsistente
com sua especificação.
A precisão deveria, na realidade, ser aplicada ao software e à sua especificação.
■ Para um programa ser correto, qualquer execução dele que inicie no estado em que
a pré-condição seja verdadeira resultará em um estado em que a pós-condição tam-
bém será verdadeira.
■ A pré-condição estabelece as propriedades que deve suportar antes da requisição do
serviço. Satisfazer a pós-condição é obrigação de “quem solicita”.
■ A pós-condição estabelece as propriedades que deve suportar após o serviço ser
executado. Esta é a obrigação do método que está sendo executado.
■ Sob nenhuma circunstância o corpo do serviço (método) chegará algum dia a testar
algo quanto às pré-condições. Isso evita uma desnecessária programação defensiva.
■ Asserções não são um mecanismo de checagem de contribuições (inputs). Uma pré-
condição é uma rotina de software destinada a especificações de rotinas de softwa-
re. Ela não tratará de erros humanos.
■ Asserções não são estruturas de controle. Asserções não são utilizadas para controle
de fluxo. Uma violação de asserção em tempo de inserção é um bug no programa.
■ Toda característica que aparece em uma pré-condição deve estar disponível a todos
os clientes; isto é, o cliente deve ser capaz de validar todas as características de uma
pré-condição.
■ Condições invariantes devem ser satisfeitas pelos construtores e por todos os métodos.
O uso de contratos altera o desenvolvimento de uma atividade de programação de-
fensiva para uma atividade de programação segura.
Um exemplo simples de cálculo da raiz quadrada de um número ilustra o uso de
contratos. A abordagem convencional adotada para definir uma sub-rotina para o cálculo
da raiz quadrada captura duas tarefas separadas: (1) encontrar e retornar a raiz quadrada
quando da passagem de um número não negativo e (2) retornar algo razoável quando da
passagem de um número negativo. A primeira tarefa é fácil de ser entendida. A segunda
tarefa, no entanto, é diferente. O problema principal é que o fornecedor desconhece o que
constitui um resultado coerente sob a perspectiva do “solicitador”.
Em um mundo perfeito, as pré-condições e pós-condições seriam tudo o que é ne-
cessário para estabelecer a garantia de serviços. Entretanto, o mundo real é uma outra
questão. Por exemplo, as conexões entre duas máquinas são perdidas, os recursos são in-
disponíveis ou um sinal é recebido do hardware. As situações nas quais um prestador de
serviço é incapaz de cumprir suas obrigações são denominadas falha.
O enfoque contratual para lidar com falhas é dirigido ao prestador de serviço para
que levante uma exceção que identifique a falha. Isso provê um enfoque consistente de
tratamento de erros da perspectiva do provedor de serviço. A exceção é, então, tratada
pelo cliente de maneira apropriada para o determinado cliente. Como resultado, vários
clientes podem lidar com a exceção em seus próprios termos.
O desenvolvimento de casos de uso identificou pré-condições e pós-condições. Estas
são as mesmas que serão utilizadas na especificação de um contrato. As pré-condições es-
tabelecem as condições que devem ser satisfeitas pelo cliente anteriormente à solicitação
do serviço. As pós-condições estabelecem o resultado da operação provido pelo prestador
de serviço, supondo-se que a pré-condição tenha sido satisfeita. A operação identifica que
64 UML E C++ CAP. 4

transformação, função ou serviço são providos. A seção de exceções da descrição de casos


de uso identifica as falhas que o prestador de serviço admite que possam ocorrer durante
o provimento do serviço.
Da perspectiva do entendimento do sistema, o uso de contratos é particularmente
útil. Os contratos identificam os serviços específicos que o sistema proverá, o que o soli-
citador de um serviço deve assegurar para obter o serviço e que expectativas serão levan-
tadas em caso de falha no sistema. Em resumo, eles definem a interface do sistema.

Abordagem Recomendada
Este capítulo apresentou casos de uso e contratos como meios de delimitar o domínio de
negócio. A abordagem recomendada é:
1. Desenvolver um modelo de caso de uso primário, essencial e de alto nível.
2. Se o domínio de negócio não é bem-entendido, utilizar o modelo da etapa 1 para
desenvolver um modelo de caso de uso primário e secundário, essencial e de
baixo nível.
3. Se a tecnologia não é bem-entendida, utilizar o modelo da etapa 2 para desen-
volver um modelo de caso de uso primário, concreto e de baixo nível.
4. Se a confiabilidade é a questão, utilizar os modelos das etapas 2 e 3 para ajudar
a desenvolver um modelo de caso de uso secundário, concreto e de baixo nível.
No desenvolvimento de qualquer um desses modelos de casos de uso, são recomen-
dadas as seguintes diretrizes:
1. Escreva, primeiramente, duas ou três das transações simples mais comuns.
2. Tente criar cenários mais “abstratos” quando dois ou três deles parecerem muito
similares.
3. Seja precavido e crie mais de 30 casos de uso para abranger as ações fundamen-
tais do sistema.
4. Casos de uso complementares para eventos incomuns deverão ser selecionados
com cuidado e mantidos em um número administrável.
5. Dentro de um modelo, desenvolva-o interativamente. Inicialmente, faça descri-
ções mais breves e, em seguida, as refine.
Uma vez identificados os casos de uso, utilize-os para estabelecer contratos de sis-
tema. Nos contratos, identifique (1) as pré-condições, (2) as pós-condições, (3) a operação
e (4) as exceções.

Exemplo
O exemplo do corte de grama do capítulo anterior foi apresentado para demonstrar como
uma situação do mundo real poderia ser modelada utilizando-se um modo orientado a
objeto de organizar a realidade. Conhecer ou possuir a habilidade de modelar uma situa-
ção como essa não constitui razão suficiente para construir-se um sistema. É necessário
estabelecer um valor de negócio que justifique o desenvolvimento inicial desse modelo e
sua transformação em um sistema. Assim, que tipo de valor de negócio pode ser estabe-
lecido para justificar um sistema que venha a descrever como uma família lida com a si-
tuação de aparar e cortar o gramado de sua casa?
CAP. 4 DELIMITANDO O DOMÍNIO 65

Neste capítulo, generalizaremos essa situação em um sistema de serviços domésti-


cos gerais. O objetivo desse sistema é o de conseguir uma melhor visão sobre de que for-
ma e por que uma família gasta seu dinheiro.30 O sistema talvez inclua outras atividades
relativas a serviços domésticos gerais, como pintura de paredes, conserto de tubulações,
reparo de instalações elétricas, substituição de aparelhos domésticos e outras atividades
comuns. Em termos de nosso valor de negócio especificado para o sistema, isto possibilita
capturar de que forma é feita a manutenção de uma casa. Um subsistema prático talvez
seja rastrear as situações de danos causados por quebras, que custam dinheiro para serem
reparados.
No desenho e implementação deste modelo, utilizaremos uma abordagem de simu-
lação para o desenho do sistema.31 Para uma simulação padrão, há um ator-chave: um
relógio de simulação. Esse relógio ativa o sistema a partir de um aspecto dependente do
tempo. Entretanto, nós queremos que nosso sistema também reaja a eventos assíncronos.
Para lidar com esses tipos de eventos, adicionaremos um outro daemon, denominado dae-
mon de eventos assíncronos.
Esta é a forma como os dois atores (ou geradores de eventos) trabalham:
■ O relógio de simulação é um marcador periódico de tempo que, regularmente, en-
via uma mensagem ao sistema indicando o incremento de simulação atual. O siste-
ma responde a esse evento informando a todos os objetos ativos na simulação que
executem as ações apropriadas para o tempo corrente na simulação.
■ O daemon de eventos assíncronos envia uma notificação de evento ao sistema quan-
do da ocorrência de um evento. Qualquer coisa pode registrar um evento no daemon
de eventos assíncronos. O sistema responde tendo todas as partes do sistema exa-
minando o ambiente para determinar se alguma ação precisa ser executada.
Por exemplo, se a roupa suja da casa é lavada todos os dias às 9 horas da noite, o
relógio de simulação pode ser utilizado para iniciar o comportamento correto. Similar-
mente, se a máquina de lavar roupas apresenta alguma avaria, o daemon de eventos as-
síncronos pode ser utilizado para notificar o sistema. (Note que este sistema é muito
ineficiente visto que isso ainda precisa ser observado.)
No capítulo anterior, foi dito que a mãe, Jane, notou que o gramado precisava ser
aparado e cortado. Isso faz de Jane um ator em relação ao nosso sistema? Na verdade,
Jane é uma parte de nosso sistema no sentido de que ela é um dos objetos que participam
dele. Isso faz do corte de grama um de nossos atores visto que poderíamos dizer que o
gramado revelou a ela que necessitava de aparo e corte? Nós poderíamos adotar tal pers-

30. Por esse ser um exemplo que não precisamos vender à gerência ou implementá-lo de fato, somos
livres para estabelecer o domínio de negócio da maneira que quisermos. Nenhum dos autores
realmente gostaria de ir até a gerência e explicar que pretendíamos implementar uma simulação de
corte de grama.
31. O relógio de simulação e o daemon de exame de arredores são exemplos de atores (Cron) que
correspondem a eventos externos; a saber, o transcorrer do tempo. Isso possibilita que foquemos o
que ocorre como resultado da passagem de tempo sem nos preocuparmos em como isso é
implementado dentro do sistema. Nós ainda não teremos eventos espontâneos gerados pelo sistema
sem que uma fonte externa forme as condições para a criação do evento. Eventos espontâneos são
muito difíceis de explicar e criar.
66 UML E C++ CAP. 4

pectiva, mas essa decisão poderia conduzir a uma situação na qual todas as coisas em
volta da casa seriam atores, com a possível exceção dos membros da família. Decidimos
pelo caso em que o gramado envia um evento ao daemon de eventos assíncronos. Assim,
faremos valer que foi o recebimento de um evento (ou seja, uma mensagem) do daemon
de eventos assíncronos que fez com que Jane olhasse para a grama, observasse que ela
estava muita alta e, portanto, decidisse que a grama precisava de aparo e corte. Isso, na
verdade, supõe que Jane estava na parte externa da casa na hora em que recebeu a men-
sagem para examinar os arredores da casa.32
Esses dois atores juntos podem ativar a simulação de maneira não-repetitiva. Ou
seja, os efeitos desses dois atores podem ser combinados. Por exemplo, a simulação por
relógio pode programar John para ativar seu serviço condicionado de corte de grama, que
é dependente do estado todos os sábados às 14h35. Mas, igualmente, podemos utilizar o
controlador (handler) de eventos assíncronos para mudar o estado de John, enviando um
evento em um sábado chuvoso que irá impedi-lo de cortar a grama naquele dia.
Para tornar o modelo efetivo para casos de uso, acrescentaremos mais dois atores:33
■ Observador
■ Diretor
O observador faz a assinatura e obtém atualizações sobre o status da simulação. O
diretor envia comandos ao sistema para controlar a evolução da simulação.
Baseando-nos na descrição precedente, temos quatro atores: (1) relógio de simula-
ção, (2) daemon de eventos assíncronos, (3) observador e (4) diretor. Identificamos também
as respostas básicas funcionais do sistema, para as mensagens provenientes desses atores.
Agora, definiremos essas respostas com mais detalhes.
As respostas funcionais do sistema, devido às mensagens oriundas do relógio de si-
mulação, são:
■ Tique (Tick)
■ O sistema responde deslocando a simulação para a frente um incremento do pe-
ríodo de simulação.
■ O sistema remete a informação de simulação ao observador solicitada por ele.

As respostas funcionais do sistema, devido às mensagens oriundas do daemon de


eventos assíncronos, são:
■ Procurar (Look)
■ O sistema responde com o auxílio de todos os objetos autodirigidos, que exami-
nam os arredores e respondem a qualquer situação que possam identificar atri-
buindo e programando tarefas.

32. Se Jane estivesse no interior da casa naquela hora, talvez tivesse notado que havia necessidade de
realizar uma aspiração do carpete para retirada de pó.
33. Podemos argumentar igualmente que uma boa razão para separar os dois papéis é que poderia ser
possível, por meio de um programa externo (gerador de cenários), substituir a pessoa que está
cumprindo o papel de diretor. Da mesma forma, o observador poderia ser substituído por um
sistema que coleta dados da simulação para uso em estudos estatísticos de resultados de simulações.
CAP. 4 DELIMITANDO O DOMÍNIO 67

As respostas funcionais do sistema, devido às mensagens oriundas do observador,


são:34
■ Fazer a Assinatura (Subscribe)
■ O sistema armazena a assinatura de forma que possa iniciar a remessa de infor-
mações ao observador sobre as mudanças de estado.
As respostas funcionais do sistema, devido às mensagens oriundas do diretor, são:
■ Definir (Set)
■ O sistema define a variável de simulação apropriada.
Essas mensagens são utilizadas para disparar os casos de uso primários, essenciais
e de alto nível.
O diagrama de caso de uso para nossos casos de uso é mostrado na Figura 4.7. Con-
forme pode ser visto, mantivemos o número de casos de uso em um mínimo, embora
conseguíssemos capturar os detalhes mais importantes do sistema.

FIGURA 4.7 Diagrama de caso de uso para os casos de uso primários, essenciais e de alto
nível para o exemplo do corte de grama.

34. Poderíamos acrescentar mensagens de “inicie” e “pare” a ser enviadas do observador ao sistema
para início e parada da simulação. Estas teriam o efeito de enviar mensagens semelhantes ao relógio
de simulação e ao daemon de observação de arredores.
68 UML E C++ CAP. 4

Procurar por Condições de Trabalho


Descrição: Este caso de uso estabelece que trabalho deverá ser realizado dentro da simulação como
resultado da situação na qual pessoas simuladas observam os arredores — também simulados.
Atores: Daemon de Eventos Assíncronos
Pré-condições: Nenhuma
Detalhes:

1. O Daemon de Eventos Assíncronos envia uma mensagem Procurar ao sistema.


2. Cada pessoa da casa procura, nas cercanias mais próximas, por algo que não esteja em um estado
apropriado (Exame do ambiente).
3. Se um item de trabalho for identificado, este será programado para ser realizado (Programação de
trabalho).

Pós-condições: Quaisquer itens de trabalho dentro da localidade de uma pessoa são programados para
serem tratados.
Comentários: Se permitirmos que o observador inicie e pare a simulação, então teremos a pré-condição de
que a simulação se encontra em andamento.

FIGURA 4.8 Descrição textual do caso de uso Procurar por Condições de Trabalho.

Deslocar Simulação para a Frente Uma Unidade de Tempo


Descrição: Este caso de uso detalha como a simulação é mantida atualizada com o tempo de simulação.
Atores: Relógio de Simulação, Observador
Pré-condições: Tempo real suficiente que passou desde a última atualização
Detalhes:

1. O Relógio de Simulação envia uma mensagem Tique ao sistema.


2. Cada objeto avalia seu estado atual considerando a mudança no tempo de simulação.
3. O sistema remete informações sobre as mudanças de estado ao Observador.

Pós-condições: O modelo tem sido atualizado para refletir o novo tempo de simulação.
Comentários: Se permitirmos que o observador inicie e pare a simulação, então teremos a pré-condição de
que a simulação se encontra em andamento.

FIGURA 4.9 Descrição textual do caso de uso Deslocar Simulação para a Frente Uma Unida-
de de Tempo.

Os casos de uso são também documentados textualmente. Empregamos a Tabela 4.1


para identificar a informação capturada no texto. As descrições de casos de uso são mos-
tradas nas Figura 4.8 a 4.11. A descrição identifica o caso; provê uma descrição de altís-
simo nível dele; identifica os atores envolvidos, as pré-condições, os detalhes, as
pós-condições e quaisquer comentários. Aquelas áreas que não se aplicam (pré-condi-
ções) não são deixadas em branco, mas preenchidas com anotações apropriadas de forma
que possamos saber que este aspecto foi considerado. Sendo assim, não temos pré-condi-
ções que precisem dar suporte para que a mensagem receba tratamento.
CAP. 4 DELIMITANDO O DOMÍNIO 69

Definir Parâmetro de Simulação


Descrição: Este caso de uso possibilita que a simulação seja feita sob medida, baseada em parâmetros
definidos pelos usuários.
Atores: Diretor
Pré-condições: Nenhuma
Detalhes:

1. O Diretor envia uma mensagem ao sistema identificando um parâmetro de simulação e um valor.


2. O Sistema define o parâmetro de simulação com o novo valor.

Pós-condições: O valor de um parâmetro de simulação é modificado, e a simulação continua considerando


o novo valor do parâmetro.
Comentários: Exemplos de parâmetros poderiam incluir o tempo do relógio real entre os tiques do relógio
de simulação (por exemplo, devem decorrer três segundos entre os tiques); o incremento simulado de
tempo para o relógio de simulação (ou seja, cada tique corresponde a l hora); ou a personalização do que
está prestes a ocorrer em uma simulação (por exemplo, acredita-se que irá chover no sábado).

FIGURA 4.10 Descrição textual do caso de uso Fixar Parâmetro de Simulação.

Fazer a Assinatura da Informação de Simulação


Descrição: Este caso de uso descreve a forma como o sistema é informado sobre qual informação sobre
simulação o Observador quer da simulação.
Atores: Observador
Pré-condições: Nenhuma
Detalhes:

1. O Observador envia uma mensagem Fazer a Assinatura ao sistema.


2. O Sistema se autoconfigura de forma que todas as informações do tipo solicitado são passadas ao
Observador.

Pós-condições: O sistema é configurado para remeter informação específica ao Observador.


Comentários: Nenhum

FIGURA 4.11 Descrição textual do caso de uso Fazer a Assinatura da Informação de Simu-
lação.

Agora que temos as simulações dos casos de uso Deslocar para a Frente Uma Uni-
dade de Tempo e Procurar por Condições de Trabalho, de que forma desenvolveremos o
caso de uso associado com o corte de grama? Os casos de uso que tratam especificamente
de corte de grama são do tipo essenciais e de baixo nível que, mais à frente, definem
como é atingida a funcionalidade de alto nível. Nós podemos modelar o cenário de corte
de grama descrito no capítulo anterior com três casos de uso essenciais de baixo nível: (1)
Verificar a Grama, (2) Planejar Cortar a Grama e (3) Cortar a Grama.
Se considerarmos a etapa Exame do Ambiente, deveremos reconhecer que há muito
poucos casos de uso especializados para este caso de uso de alto nível. Em particular, há
diferentes “ambientes” que podem ser verificados, e esse exame requer que uma pessoa
esteja no local apropriado observando a necessidade. Assim, por exemplo, quando esta-
mos no lado externo da casa, possivelmente queremos “verificar a grama”; entretanto,
quando estamos no interior dela, talvez queiramos “verificar o carpete”. Por conseguinte,
introduzimos um caso de uso de generalização, Exame do Ambiente, do qual Verificar a
70 UML E C++ CAP. 4

Grama é uma especialização juntamente com outros casos de uso (por exemplo, Verificar
o Carpete).
O caso de uso Verificar a Grama captura a situação em que Jane nota que a grama
precisa ser aparada e cortada. Esta é, no momento, uma versão especializada35 de Exame
do Ambiente. Além do mais, o caso de uso Procurar por Condições de Trabalho englo-
bará o caso de uso Exame do Ambiente.
No modelo, Planejar o Trabalho também é um caso de uso que tem uma versão es-
pecializada para o gramado, Planejar Cortar a Grama. Isso é feito por Jane, que solicita a
John para que ele corte a grama; com o que ele concorda — planejando o corte da grama
para o final daquela tarde.
O terceiro caso de uso, Cortar a Grama, captura a forma como o gramado é aparado e
cortado. Ele abrange os detalhes sobre como o pai, John, envolve os filhos e Jack, o cortador
de grama profissional. Essa é uma especialização do caso de uso Executar Ação Corretiva.
(Há vários outros casos de uso de natureza similar; por exemplo, Aspirar o Pó do Chão).
Certamente, a pré-condição para o caso de uso Executar Ação Corretiva é que um
item de trabalho tenha sido identificado. Essa generalização é incluída no caso de uso
Procurar por Condições de Trabalho para prover a funcionalidade nos casos em que uma
tarefa é programada para ser realizada uma vez que tenha sido identificada.
Uma porção do diagrama de caso de uso para nosso sistema está ilustrada na Figura
4.12. Ela mostra como o Daemon de Eventos Assíncronos faz disparar o caso de uso Pro-
curar por Condições de Trabalho. Esse caso de uso inclui o caso de uso Exame do Am-
biente, que é uma generalização para os casos de uso Verificar a Grama e Verificar o
Carpete. O caso de uso Procurar por Condições de Trabalho também inclui o caso de uso
Executar Ação Corretiva, que é uma generalização para os casos de uso Cortar a Grama
e Aspirar o Pó do Carpete.

FIGURA 4.12 Porção do diagrama de caso de uso para os casos de uso primários, essenciais
e de baixo nível, detalhando os casos de uso de baixo nível associados ao caso
de uso de alto nível Procurar por Condições de Trabalho.

35. Deve ser notado que este caso de uso é um dos vários casos de uso especializados que,
potencialmente, podem ocorrer quando examinamos nossos arredores no caso de uso Procurar por
Condições de Trabalho.
CAP. 4 DELIMITANDO O DOMÍNIO 71

Verificar a Grama
Descrição: Este caso de uso lida com a situação na qual uma pessoa observa o gramado para determinar
se ele precisa ser aparado e cortado.
Atores: Sistema
Particularização: Exame do Ambiente
Inclusões: Nenhuma
Extensões: Nenhuma
Pré-condições: A pessoa deve estar nas redondezas do gramado.
Detalhes:

1. O sistema envia uma mensagem à pessoa para verificar a grama.


2. A pessoa examina a altura da grama.
3. Se a altura for maior do que um certo valor, o gramado precisará ser aparado e cortado; do
contrário, ele não precisará.
4. A pessoa envia uma mensagem identificando a necessidade de cortar a grama.

Pós-condições: A necessidade de cortar a grama é estabelecida (quer ela seja ou não cortada).
Exceções: Nenhuma
Restrições: Nenhuma
Variantes: Nenhuma
Comentários: Deveria haver um patamar para que a altura da grama fosse particular à pessoa.

FIGURA 4.13 Descrição textual do caso de uso Verificar a Grama — Primário, Essencial e de
Baixo Nível.

Esses casos de uso devem ser documentados de forma que nosso entendimento do
que o sistema está fazendo seja capturado. Novamente, podemos retornar à Tabela 4.1
para identificar as informações que deveriam ser capturadas nos casos de uso primários,
essenciais e de baixo nível. As informações que precisamos capturar incluem o nome do
caso de uso, uma descrição de alto nível, quaisquer relacionamentos entre casos de uso,
as pré-condições, os detalhes, as pós-condições, restrições e variantes. Mais uma vez, o
modelo é empregado para uma descrição textual. Um exemplo de descrição textual para
o caso de uso Verificar a Grama é fornecido pela Figura 4.13. Os outros casos de uso dis-
cutidos anteriormente são deixados como exercício para o leitor.
O caso de uso Verificar a Grama demonstra um ponto intrincado sobre casos de
uso. Em particular, esse é um tipo de caso de uso que é uma especialização de um caso
de uso incluído dentro de um outro caso de uso. Surge uma questão relativa ao ator que
o desencadeia. Especificamente, quem é o autor? Fundamentalmente, nesse exemplo es-
pecífico, esse caso de uso é desencadeado como resultado de uma mensagem recebida
pelo Daemon de Eventos Assíncronos.

■■ RESUMO
Este capítulo apresentou os casos de uso como um meio de delimitar um domínio. Os
conceitos fundamentais associados aos casos de uso têm sido definidos, a saber, o objeti-
vo (goal), sistema (system), os atores (actors) e os grupos de casos de uso (use case bundles).
Eles podem ser resumidos como se segue.
O objetivo é o valor de negócio para o(s) “usuário(s)” do sistema que, geralmente,
inicia(m) a interação com ele.
72 UML E C++ CAP. 4

O sistema é a aplicação, com todo o seu hardware associado, que será utilizada
pelo(s) “usuário(s)”. Existem três visões padrão do sistema: caixa preta, caixa branca e
caixa transparente. Casos de uso utilizam a visão da caixa preta do sistema sob análise.
Um ator é uma entidade que interage com o sistema. Um ator pode ser um usuário,
um sistema externo ou um dispositivo. Um ator pode fazer uma solicitação de serviço ao
sistema, ser solicitado para prover um serviço e pode interagir com o sistema através de
um diálogo complexo de solicitações de serviço entre ele e o sistema.
Um caso de uso é uma descrição de uma interação que atinge um objetivo proveitoso
para um ator. O ator que inicia o caso de uso é denominado de ator de iniciação (inicia-
dor). Na maioria das interações, o diálogo poderá resultar na interação do sistema com
outros atores; esses atores são chamados atores participantes. A interação supõe que o sis-
tema seja uma “caixa preta” e utilize elementos de domínio como atores que interagem
com o sistema em uma maneira do tipo solicitação/resposta. Um caso de uso identifica
as pré-condições que devem existir para que ele seja válido, as pós-condições que defi-
nem o estado do sistema após o caso de uso ter sido concluído, negócios detalhados (não-
dependentes da tecnologia) que são realizados, exceções de negócio que possam surgir e
restrições de negócio que se aplicam ao sistema em reação à solicitação específica de um ator.
Um grupo de casos de uso é uma coleção de casos de uso que está estreitamente cor-
relacionada com alguma atividade ou elemento organizador de negócio. Um grupo de ca-
sos de uso fornece-nos um modo de organizar nossos casos de uso em coleções, que nos
auxiliarão a compreender melhor a funcionalidade do sistema que estamos desenvolvendo.
Os vários relacionamentos que um caso de uso poderá suportar com outros casos
de uso foram introduzidos. Conforme será mostrado no próximo capítulo, os casos de
uso e contratos cooperam para que nós identifiquemos objetos que serão parte de nosso
modelo.
Descobrindo os Objetos
DESCOBRINDO OS OBJETOS
5.Descobrindo os Objetos

E u tenho um gato chamado Trash... se fosse tentar vendê-lo (no mínimo a


um cientista da computação) não iria enfatizar que ele é gentil com as
pessoas e auto-suficiente, vivendo a maior parte do tempo à caça de ratos. Pre-
ferivelmente, eu argumentaria que ele é orientado a objeto.

Roger King, My Cat Is Object-Oriented

O capítulo anterior introduziu os casos de uso como um meio de delimitar um domí-


nio. O material daquele capítulo não estava diretamente associado com a orientação
a objeto a não ser pelo fato de que a UML provê uma representação gráfica para casos
de uso. Os casos de uso aplicam-se a vários paradigmas diferentes.
Este capítulo descreve a primeira etapa real de nosso método para a construção de
um modelo OO. Começamos em um ponto de início lógico — encontrar os objetos1. Neste
capítulo, abordamos muitas técnicas diferentes para identificar objetos. O objetivo deste
livro não é conhecê-las em profundidade, mas sim prover um sólido contexto por meio
do qual os desenvolvedores possam dirigir seus esforços. Recomendamos uma técnica,
reconhecendo que ela não é a abordagem mais abrangente, mais nova ou que está mais
em evidência. Contrariamente, nos concentramos em apresentar uma técnica que seja de
fácil aplicação por alguém iniciante na esfera do paradigma orientado a objeto.

1. Nós não estamos de fato encontrando objetos; estamos na realidade encontrando objetos e tipos
(conceitos de análise) que serão implementados com o uso de classes e classes abstratas puras.

73
74 UML E C++ CAP. 5

Análise Orientada a Objeto: Modelo de um Domínio de


Aplicação
Quando analisamos sistemas, criamos modelos de domínios de aplicação de interesse
para nosso negócio. O modelo pode ser muito específico e altamente especializado (isto
é, um sistema de livro-razão para vendedores) ou pode cobrir toda uma empresa. Qual-
quer que seja o caso, o modelo representa um aspecto da realidade e é construído de ma-
neira que nos ajude a administrar a complexidade e a compreender a realidade do
negócio. O modelo sempre é muito mais simples que a realidade, exatamente da mesma
forma que qualquer modelo de brinquedo é mais simples que o item real. Por exemplo,
um carrinho de bombeiro de brinquedo é muito mais simples do que um carro de bom-
beiro real; os modelos de aeroplanos sofisticados são mais simples do que os aviões reais.
Em nosso caso, entretanto, se o modelo for suficientemente rico, poderemos manipulá-lo
para auxiliar-nos a inventar ou redesenhar nossos negócios.
Com os métodos de análise tradicionais, modelamos o mundo utilizando funções
ou comportamentos como nossos blocos de construção. Temos visto algumas das defi-
ciências desse paradigma de modelagem. Com a análise orientada a objeto, modelamos
a realidade tendo objetos como nossos blocos de construção na esperança de eliminar as
falhas de um paradigma de modelagem baseado em funções. No paradigma orientado a
objeto, descrevemos nosso mundo utilizando as categorias de objetos (classes) ou os tipos
de objetos (classe abstrata pura ou interface Java). Designamos atributos (variáveis de da-
dos) e serviços (operações, funções, métodos) para essas categorias de objetos (classes).
Nós também definimos relacionamentos (herança, associação, e agregação) entre as clas-
ses. Em seguida, modelamos o comportamento do mundo como uma seqüência de men-
sagens enviadas entre vários objetos que são instâncias das várias categorias de objetos
ou dos tipos de objetos. Utilizando-se essa forma de análise, podemos mais facilmente de-
senhar e programar o software em uma maneira orientada a objeto para conseguir os be-
nefícios de um software flexível e de fácil manutenção.

Construindo o Modelo OO
Nos livros didáticos tradicionais, a construção do modelo OO é considerada a fase de re-
quisitos do projeto. O que torna esta fase muito confusa para os desenvolvedores é que
duas atividades muito distintas, mas relacionadas, estão ocorrendo simultaneamente:
análise de problemas (análise do domínio) e descrição da solução de negócio (descrição
do produto).
Há pelo menos duas razões para mantermos essas atividades não-seqüenciais e não-
mutuamente exclusivas. Primeiro, um grande número de desenvolvimentos de produtos
requer pouca ou nenhuma análise de problemas. As empresas, de modo geral, não des-
perdiçam tempo ou recursos em um problema que já está bem-compreendido. Segundo,
ao efetuar análise de problemas, a maioria das equipes de trabalho inicia este tipo de aná-
lise. Quando algum trecho do problema ou do domínio do problema estiver bem-enten-
dido, as equipes começarão a trabalhar na descrição da solução de negócio para resolver
aquela parte do problema. Esse processo de avançar e recuar propicia à equipe um modo
eficaz de monitorar o próprio progresso.
O objetivo da análise de problemas é um entendimento relativamente completo dos
problemas e das restrições em soluções potenciais de negócio. Os objetivos de uma des-
crição de solução de negócio traduzem-se no fato de termos uma descrição adequada,
CAP. 5 DESCOBRINDO OS OBJETOS 75

completa, inequívoca, verificável, consistente, modificável, rastreável, organizada e con-


cisa da solução de negócio que seja compreensível para o cliente/usuário/comprador.
Isso não quer dizer que os objetivos de uma descrição de solução de negócio sejam sem-
pre cumpridos, mas deveríamos chegar bem perto disso.
Na análise de problemas, os desenvolvedores/analistas/engenheiros de sistemas
adquirem conhecimento do problema (e do domínio do problema) imediatamente e iden-
tificam todas as possíveis restrições na solução do problema. Uma boa parte desse tempo
é gasta em entrevistas com usuários e especialistas em negócios/domínios e em debates
livres dando ou sugerindo idéias. Durante esse período, há considerável expansão de in-
formações e conhecimento sobre o problema a ser resolvido. As questões que precisam
ser abordadas durante essa atividade são (1) descobrir um modo de permutar restrições e (2)
encontrar maneiras de organizar a superabundância de informação e conhecimento adquiridos.
Em uma descrição de solução de negócio, os desenvolvedores, analistas e engenhei-
ros de sistemas transformam algumas das difíceis decisões em trocas de restrições e des-
crevem o comportamento externo do produto que será criado. Nessa atividade, (1) ideais
são organizados, (2) pontos de vista conflitantes são resolvidos e (3) inconsistências e am-
bigüidades são eliminadas.
O resultado final da análise de problemas e da descrição da solução de negócio de-
verá ser um modelo que:
■ Organize os dados em objetos e classes e forneça-lhes uma estrutura por meio de
relacionamentos de herança, agregação e associação.
■ Especifique comportamentos funcionais locais e defina suas interfaces externas
(protótipos de serviço).
■ Capture comportamento de controle ou global (diagramas de acompanhamento de
eventos).
■ Capture restrições (limites e regras).
Em nossa versão da análise orientada a objeto, não fazemos distinção entre análise
de problemas e descrição de solução de negócio; construímos um modelo da realidade
que deverá capturar ambas. Isso é consistente com a idéia de que um bom e eficiente mé-
todo deveria ter etapas inteiriças e, de preferência, nenhuma transformação de uma etapa
para outra. A primeira etapa nesse método é a de encontrar os objetos.

Identificação de Objetos
Identificar objetos (e classes)2 é a etapa mais importante e difícil em qualquer método
orientado a objeto. Identificá-los é importante porque a especificação de requisitos, o de-
senho e o código os utilizarão como os blocos de construção; erros em identificá-los ade-
quadamente terão um impacto na extensibilidade e na manteneabilidade do software.
Identificá-los é difícil (pelo menos para a maioria dos iniciantes), pois isso requer um co-
nhecimento profundo e habilidade no paradigma orientado a objeto para empregá-lo nas
aplicações. Normalmente, leva-se de três meses a um ano de prática para que as necessá-
rias habilidades amadureçam, mas pode-se levar até mais tempo; dependendo da postura

2. Tecnicamente, a classe também é um objeto. Nos próximos capítulos, trataremos uma classe como
um objeto. Para o leitor experiente, estamos realmente interessados em encontrar classes e não
instâncias de classe neste capítulo.
76 UML E C++ CAP. 5

da pessoa (para com a orientação a objeto), do talento e do nível de treinamento/expe-


riência.
Na teoria, identificar objetos e classes não deveria ser difícil. Lidamos com objetos
e classes todos os dias de nossas vidas. Por exemplo, o brinquedo de uma criança (um
carrinho de bombeiro ou uma boneca) é meramente um modelo de uma entidade do
mundo real (um carro ou uma pessoa). O carrinho de bombeiro de brinquedo tem atri-
butos constantes (por exemplo, altura, largura, cor) ou variáveis (por exemplo, nível de
bateria e estado relativo de conservação ou de mau estado), bem como serviços (por
exemplo, mover para a frente, mover para trás, ligar sirene etc). Ele também tem condi-
ções de exceção como bateria vazando, roda quebrada etc.
Construir modelos mentais e utilizar abstrações são enfoques humanos padrão para
tratar da complexidade da rotina do dia-a-dia. Portanto, se os profissionais de software
entenderem seus domínios de aplicação, a identificação de objetos e classes requererá me-
ramente a captura dos modelos e abstrações relevantes do domínio de aplicação necessá-
rios para resolver o problema. Mesmo assim, por que a identificação ainda é tão difícil?
O problema mais grave é que foi ensinado aos desenvolvedores, e eles têm pratica-
do, uma abordagem de desenvolvimento de software muito menos natural e menos ex-
pressiva — o enfoque funcional (análise estruturada e decomposição funcional). Como
mencionado anteriormente, devido às limitações dos computadores e linguagens de pro-
gramação, os desenvolvedores necessitavam utilizar um paradigma que imitasse um
computador. Isso resultou em um enfoque funcional para a modelagem da realidade que
focava as operações de procedimentos do tipo passo-a-passo.
Devido à inércia humana, agora é muito difícil que desenvolvedores experimenta-
dos rompam esse hábito, particularmente porque as abordagens funcionais são antinatu-
rais (até que a habilidade seja desenvolvida), e eles têm passado anos “afiando” essa
habilidade. Além do mais, pensar em termos de objetos no dia-a-dia pode parecer fácil e
natural, mas pensar sobre analogias de desenvolvimento de software em termos de obje-
tos demandará um certo tempo. Quando os desenvolvedores aplicam pela primeira vez
um método orientado a objeto, eles geralmente estão inseguros do que constitui um ob-
jeto ou uma classe de software. Mesmo sabendo a definição, eles nem sempre estão segu-
ros de que algo possa corresponder à definição de um objeto ou classe.
Essa dificuldade provocou um debate filosófico e muita confusão. Existem, basica-
mente, duas visões completamente diferentes sobre como os objetos e as classes vieram
à tona: empirista e fenomenalista. A visão empirista declara que eles estão por toda a par-
te à espera de serem notados. De acordo com Betrand Meyers, “os objetos estão aí fora
para serem pegos”. Portanto, os desenvolvedores devem estar ofuscados com a presença
deles porque estão muito acostumados à decomposição funcional e, também, estão ten-
tando utilizar funções como seus blocos construtivos.
A visão fenomenalista declara que os objetos originam-se tanto do mundo como de
nossa consciência por meio de um processo dialético. Outros fenomenalistas sugerem que
os objetos do mundo real são reflexo das relações sociais e dos processos de pensamento
humano.
Nós somos adeptos da escola fenomenalista. Não apenas acreditamos fielmente que
os objetos são oriundos de nossa consciência, mas também que agora devemos “objeti-
var” (materializar) o mundo para obtermos o máximo benefício desta nova tecnologia. É
essa “materialização” que torna a identificação de objetos difícil, mesmo para analistas
orientados a objeto experimentados.
CAP. 5 DESCOBRINDO OS OBJETOS 77

Durante os últimos dez anos, os profissionais e pesquisadores do método orientado


a objeto desenvolveram muitas técnicas (em geral indiretas) para combater essa questão.
Estas técnicas foram baseadas na suposição de que há um mapeamento (com sorte, de um
para um) entre coisas que podem ser identificadas por neófitos, bem como objetos e/ou
classes que eles têm dificuldade em reconhecer. Isto possibilita aos neófitos orientados a
objeto realizarem um trabalho útil enquanto adquirem as aptidões e experiência para fa-
zer com que o paradigma como um todo se volte para o enfoque orientado a objeto.
Diferentes metodologistas têm seus próprios enfoques favoritos, e muitas dessas
técnicas estão estreitamente acopladas a alguns enfoques. A maioria dos livros e artigos
discute somente um pequeno número dessas técnicas de identificação, e alguns enfoques
são raramente ensinados. Algumas das técnicas são bem-apropriadas para desenho3
orientado a objeto, mas são menos úteis durante a análise de domínio orientada a objeto
(ou seja, na análise de requisitos). Algumas técnicas são seguras no sentido de que elas
identificam objetos minimamente falsos, e são de fácil utilização, mas identificarão so-
mente os objetos e classes óbvios.
Pelo fato de que não há necessariamente um mapeamento de um-para-um entre ob-
jetos e classes com “outras coisas”, todas essas técnicas têm falhas; a saber, que a utiliza-
ção dessas técnicas poderá produzir identificação falsa positiva e que nenhuma dessas
técnicas fornece uma lista completa de objetos.4 Acreditamos que nenhuma dessas técni-
cas nos auxiliará a encontrar todos os objetos, e somente todos os objetos. Isso é consis-
tente com nossa visão fenomenalista; acreditamos que amadureceremos nossa percepção
da realidade por meio de um processo dialético porque o mundo real dos objetos é um
reflexo das relações sociais e de nossos processos humanos de pensamento. Semelhante-
mente à maioria das coisas, é responsabilidade de sua gerência e de você mesmo contro-
lar este risco.
Essas técnicas, portanto, fornecem-nos uma lista de objetos potenciais, se bem que
uma lista incompleta, e podem ser divididas em duas categorias: atuais e tradicionais. As
técnicas atuais são as mais eficazes e representam o “supra-sumo” das técnicas, muito
embora requeiram significativos treinamento e experiência para serem utilizadas de ma-
neira eficiente. As técnicas tradicionais em geral são altamente indiretas e fáceis de utili-
zar e de utilizar erroneamente, em particular para os principiantes. As técnicas mistas são
limitadas no escopo, mas proveitosas dada a correta situação.

Técnicas Atuais
As técnicas atuais baseiam-se na experiência e no conhecimento do domínio que deve ser
capturado no modelo de objeto. Elas abrangem a faixa que vai desde a utilização de itens
a serem modelados e se estende até os modelos existentes no domínio.

Utilizar os Itens a Serem Modelados


Este é o método preferido dos engenheiros de software orientados a objeto experientes.
Ele reconhece que as entidades do domínio de aplicação precisam ser identificadas antes
de se efetuar a identificação dos correspondentes objetos e classes. Esta técnica é apoiada
por autores famosos, tais como Coad e Yourdon (1991), Shlaer e Mellor (1992) e outros.

3. Em nossa definição de desenho, estamos querendo dizer modelagem do domínio da tecnologia.


4. Por exemplo, todas as abstrações de conceitos ou idéias que necessitam ser modeladas como objetos.
78 UML E C++ CAP. 5

Esta técnica é extremamente eficiente porque é natural, direta e segura. Infelizmen-


te, ela tende a colaborar somente para encontramos os terminadores (terminators) e outros
objetos tangíveis, que são as entidades mais fáceis de identificar. Classes abstratas não são
prontamente identificadas pela utilização deste método. Além do mais, esta técnica re-
quer que o usuário altere o paradigma para uma postura orientada a objeto. Muito em-
bora essa mudança de paradigma deva ser o objetivo fundamental, o treinamento no
serviço poderá ser muito dispendioso.
As etapas desta técnica são:
1. Identificar itens individuais ou próprios do grupo, tais como pessoas, papéis, or-
ganizações, localidades, relatórios, formulários etc. no domínio de aplicação que
será modelado.
2. Identificar os objetos e classes correspondentes.

Utilizar as Definições de Objetos e Classes


Esta técnica assume que o enfoque mais eficaz é o direto e que o engenheiro de software
tem experiência em identificar objetos e classes. A técnica é muito simples; o desenvolve-
dor utiliza abstração de objetos, conhecimento do domínio de aplicação e definição de
classe para intuitivamente identificá-los. Esta é a mesma maneira que desenvolvedores
experimentados reconheceriam abstrações funcionais e de processo.
Este é um enfoque direto e eficaz que provê o melhor particionamento dos requisi-
tos em classes. Quando usada apropriadamente, esta técnica resulta no menor número de
identificações falsas positivas. Esta técnica não tem limitações, mas requer uma mudança
de paradigma significativa para o desenvolvedor.
A mudança de paradigma requer significativos treinamento, prática, intuição e ex-
periência; de modo geral, com pelo menos seis meses de treinamento. Além do mais, não
existem segredos ou ferramentas que ajudem nesta técnica; as ferramentas são projetadas
unicamente para documentar os resultados.

Utilizar Decomposição de Objetos


Esta técnica assume que vários objetos e classes são agregados de objetos e classes com-
ponentes. Além disso, ela presume que a decomposição é um bom modo de identificar
os objetos e classes componentes e que você tem alguns desses objetos ou classes agrega-
dos já identificados.
As etapas desta técnica são:
1. Encontrar os objetos ou classes agregados.
2. Utilizar decomposição de objetos para identificar seus objetos ou classes compo-
nentes.
Esta técnica é um modo natural de lidar com agregados; infelizmente, nem todos os
objetos e/ou classes são agregados ou componentes de um agregado. Além disso, os
agregados do mundo real de modo geral contêm fisicamente seus componentes, e, por
causa desta propriedade, os novatos muitas vezes aninham os componentes da imple-
mentação dentro deste agregado quando um relacionamento de associação talvez fosse
um melhor modelo para os componentes da implementação. Isso pode levar a questões
sutis de modelagem e certas questões técnicas, tais como recompilação, reutilização e ma-
nutenção.
CAP. 5 DESCOBRINDO OS OBJETOS 79

Utilizar Generalização
A generalização supõe que os objetos sejam identificados antes de suas classes, que todo
objeto seja uma instância de alguma classe e que as associações (commonalities) entre ob-
jetos possam ser usadas para generalizar classes.
As etapas desta técnica são:
1. Identificar todos os objetos.
2. Procurar dois ou mais objetos que compartilhem os mesmos atributos e serviços.
3. Generalizar esses aspectos comuns para formar uma classe.
4. Continuar encontrando classes (veja a seção “Utilizar Subclasses”).
O principal benefício deste enfoque é que ele promove a reutilização e suporta o de-
senvolvimento de uma ou mais hierarquias de classificação.

Utilizar Subclasses
Quando utilizamos subclasses, pulamos a etapa de encontrar objetos e diretamente come-
çamos a identificar classes. Esta técnica assume que classes separadas geralmente contêm
recursos (ou seja, atributos, serviços, métodos etc.) e que elas podem ser transformadas
em subclasses que herdam os recursos comuns de uma superclasse comum.
As etapas desta técnica são:
1. Identificar classes que compartilham recursos comuns (ou seja, atributos, méto-
dos, nome de serviço, relacionamento de agregação, relacionamento de associa-
ção etc.).
2. Decompor os recursos comuns para formar uma superclasse (parente) e, em se-
guida, utilizar herança para todas as classes que compartilham estes recursos
para formar subclasses mais simples.
O principal benefício desta técnica é o da reutilização, mas ela apresenta alguns in-
convenientes muito sérios. Quando mal-utilizada, ela origina classes opacas, difíceis de
serem mantidas, que reutilizam a esmo recursos não-aparentados que logicamente não
pertencem a subclasses da mesma superclasse. Ela também poderá produzir acoplamento
de herança excessivo ou inapropriado.

Utilizar Análise de Domínio Orientada a Objeto


Esta técnica assume que uma análise de domínio orientada a objeto (OODA — object-
oriented domain analysis) de uma aplicação no mesmo domínio de problema tenha sido fei-
ta anteriormente. Dada a OODA, as etapas desta técnica são:
1. Analisar os resultados da OODA obtida (no mesmo domínio).
2. Reutilizar (com ou sem modificação) objetos ou classes oriundos da OODA.
Esta técnica suporta reutilização e tende a maximizar a coesão em classes e minimi-
zar mensagens e acoplamento de herança. Se for assumido que a OODA anterior é sólida,
esta técnica naturalmente também fornecerá um “exame da realidade” sobre o atual pro-
jeto, porque os objetos e classes devem ser similares àqueles da OODA. Portanto, poderão
ser economizados tempo e esforço consideráveis se a OODA original for relevante e com-
pleta. Infelizmente, esta técnica tem limitações.
Hoje em dia, encontrar uma OODA adequada e relevante não é tarefa fácil. A maio-
ria dos sistemas apresenta OODAs incompletas ou nem chega a apresentar algum mode-
80 UML E C++ CAP. 5

lo de OODA. Para a reutilização ser eficiente, o domínio de problema deve ser bem-do-
cumentado e entendido pelos desenvolvedores. Personalizar para fins de desempenho e
outras restrições de negócio em um projeto específico poderá diminuir a reutilização. Fi-
nalmente, muito embora seja mais fácil reutilizar do que reinventar, a síndrome do não-
inventado-aqui (NIH — not-invented-here) de muitos desenvolvedores precisa ser
dominada com sucesso.

Reutilizar uma Estrutura de Aplicação


Esta técnica supõe que pelo menos tenha sido feita uma OODA para criar uma estrutura
de aplicação de classes reutilizáveis. Uma estrutura de aplicação é um modelo de domí-
nio específico reutilizável de classes e/ou subconjuntos e todas suas classes associadas
que implementam algumas habilidades comuns. Pelo fato de ela ser de domínio especí-
fico, geralmente é utilizada em aplicações que estão no mesmo domínio.
As etapas desta técnica são:
1. Identificar uma ou mais estruturas de aplicação relevantes no mesmo domínio
de aplicação.
2. Reutilizar objetos (instâncias de classes) e classes de estruturas anteriormente de-
senvolvidas. Note que algumas das classes talvez necessitem de modificação
para serem reutilizadas em sua aplicação específica.
Esta técnica pode ser considerada um aperfeiçoamento da técnica OODA que acaba-
mos de descrever; dessa maneira, ela apresenta todas as vantagens e limitações daquela téc-
nica. Além disso, ela tem limitações complementares. Seus desenvolvedores atuais devem ser
capazes de identificar uma ou mais estruturas de aplicação relevantes que tenham sido an-
teriormente desenvolvidas e armazenadas em um repositório. Muito provavelmente, nem to-
das as classes necessárias estarão na(s) estrutura(s) de aplicação examinada(s).
Uma preocupação relativa às estruturas de aplicação é com a síndrome do não-in-
ventado-aqui (NIH). Essa síndrome é traduzida em uma crença geral de que se a estru-
tura de aplicação não foi desenvolvida localmente, então ela não pode levar em conta
todos os interesses da equipe local. Tal preocupação não é totalmente infundada. Em par-
ticular, estruturas de aplicação geralmente contêm classes de desenho e de análise. Infe-
lizmente, não é fácil distinguir entre as duas. Um resultado disso é que a análise pode ser
inutilmente limitada.

Reutilizar Hierarquias de Classe


Esta técnica supõe que tenha sido desenvolvido um repositório de reutilização com rele-
vantes hierarquias de classe reutilizáveis. Para utilizar esta técnica, as etapas são:
1. Procurar por classes no repositório de reutilização que possam ser reutilizadas,
quer com ou sem modificação.
2. Após identificar as classes, tentar reutilizar a hierarquia de classe associada.
3. Após modificar as classes, tentar criar novas classes abstratas pelo agrupamento
de atributos e métodos comuns.
4. Se as classes forem parametrizadas, fornecer os parâmetros formais genéricos.
Esta técnica apresenta as mesmas vantagens do que se estivéssemos utilizando a
OODA. Além disso, ela maximiza o uso de herança e corresponde a uma adequação na-
tural para o SmallTalk (uma linguagem OO).
CAP. 5 DESCOBRINDO OS OBJETOS 81

Similarmente a todas as demais técnicas, ela tem limitações complementares além


daquelas existentes na OODA. As hierarquias de classificação existentes talvez não sejam
relevantes à aplicação em curso. Classes existentes podem precisar de uma parametriza-
ção ou talvez novas subclasses necessitem ser derivadas.

Reutilizar Objetos e Classes Individuais


Se um repositório de reutilização com objetos e classes relevantes tiver sido desenvolvido,
poderemos reutilizar objetos e classes específicos.
As etapas desta técnica são:
1. Procurar objetos e classes relevantes no repositório de reutilização que possam
ser aplicados à aplicação.
2. Se necessário, modificar os objetos e/ou classes.
3. Prover parâmetros formais, genéricos, para parametrizar classes quando neces-
sário.
Esta técnica apresenta algumas deficiências muito sérias que serão discutidas mais
adiante neste livro.

Utilizar Subconjuntos
Esta técnica presume que os desenvolvedores estejam cada vez mais construindo subcon-
juntos pela utilização de um processo recursivo. Esta técnica é similar à decomposição de
funções; em vez de uma função, apanhe um objeto e o decomponha em outros objetos
(subconjuntos). Continue a decomposição até existirem somente objetos terminais (ou
seja, objetos que não precisam enviar mensagens a outros objetos em um nível inferior)
nos “ramos” da decomposição.
As etapas desta técnica são:
1. Identificar todos os objetos (classes) do nível atual que devem permanecer tem-
porariamente incompletos por dependerem, por enquanto, de um ou mais obje-
tos não identificados.
2. Desenvolver uma especificação/desenho de esquema para os métodos do objeto
temporariamente incompleto utilizando: (1) ou inglês narrativo, (2) ou uma lin-
guagem de especificação orientada a objeto, como é o caso da OOSDL, (3) ou
uma linguagem de desenho de programas (PDL — program design language).
3. Criar os apropriados subconjuntos descendentes (objetos) no próximo nível mais
baixo para tratar as mensagens para os objetos incompletos no nível mais alto.
4. Efetuar a etapa 1 no nível atual que tenha os novos subconjuntos.
Esta técnica apresenta diversas vantagens: suporta identificação incremental de ob-
jetos/classes e identifica todos os subconjuntos em um domínio de aplicação. Ela é muito
similar à decomposição funcional, e portanto há menos choque cultural para os desen-
volvedores treinados na metodologia estruturada. Entretanto, esta técnica tem limitações;
ela identifica somente objetos agrupados em conjuntos. Portanto, deve-se ter alguma ou-
tra técnica que identifique componentes fundamentais dos subconjuntos.

Utilizar Experiência Pessoal


Esta técnica, eventualmente, irá se tornar mais popular e poderá ser a mais viável de to-
das as técnicas fornecidas à medida que os desenvolvedores forem ganhando mais expe-
82 UML E C++ CAP. 5

riência quanto à utilização da metodologia orientada a objeto. Ela pressupõe que os de-
senvolvedores tenham anteriormente desenhado uma ou mais classes relevantes para o
domínio de aplicação. Para construir os novos modelos, os desenvolvedores reutilizam
alguns dos objetos e classes desenvolvidos nos projetos anteriores.
As etapas desta técnica são:
1. Encontrar objetos e classes que correspondam àqueles descobertos em modelos
anteriores que estejam no mesmo domínio de aplicação.
2. Modificar as classes quando necessário para suportar o projeto atual.
Pelo fato de construir com base na experiência de alguma pessoa, esta técnica provê
um razoável “exame da realidade” no projeto atual. Assim, a qualidade das classes e ob-
jetos pode ser melhorada substancialmente, pois eles são baseados em classes e objetos
que já foram construídos e testados. É também muito natural que se queira alavancar a
experiência do desenvolvedor em aplicações.
Contudo, há alguns inconvenientes. Esta técnica dá como certa uma experiência an-
terior relevante, que nem sempre está presente. Isso é particularmente perigoso quando
essa experiência prévia é baseada em projetos de decomposição funcional; os desenvol-
vedores têm uma tendência de identificar classes não-ideais. Nessa situação, a experiência
do passado pode ter um valor limitado e poderá até mesmo ser enganosa. Além do mais,
esta técnica é muito informal, e diferentes desenvolvedores talvez identifiquem objetos e
classes diferentes com base nas mesmas informações de partida; portanto, é uma técnica
extremamente subjetiva. E mais, ela talvez não minimize a passagem de mensagens e o
acoplamento de herança.

Técnicas Tradicionais
As técnicas tradicionais concentram-se mais em descobrir um modelo de domínio do que
na utilização de modelos de domínio existentes. Percebemos que constitui uma boa prá-
tica para um indivíduo aprender sobre técnicas de modelagem de objetos para compreen-
dê-las e aplicá-las anteriormente à utilização das técnicas atuais.

Utilizar Nomes
Introduzida por Russell J. Abbott e popularizada por Grady Booch, esta técnica foi am-
plamente utilizada entre 1983 e 1986 e incluída em muitos métodos de desenvolvimento
orientados a objeto. Esta técnica, quando associada com casos de uso, é particularmente
simples. Em muitas situações, os objetos são identificados assim que os casos de uso es-
tiverem sendo escritos.
As etapas desta técnica são as seguintes:
1. Obter, por exemplo, de um documento de requisitos ou explicitamente de um
autor um texto descritivo em inglês que represente uma descrição informal do
problema a ser resolvido. Se forem utilizados casos de uso para delimitar o do-
mínio, então eles poderão servir como texto narrativo.
O texto deverá utilizar as palavras do domínio de aplicação (ou seja, utilizar os
termos dos especialistas em domínios).
2. Utilizar os nomes, pronomes e frases substantivadas para identificar objetos e
classes (encontrar os objetos e classes do “mundo real”). Nomes próprios no sin-
gular (Jim, ele, ela, funcionário número 5, minha estação de trabalho, meu lar) e
CAP. 5 DESCOBRINDO OS OBJETOS 83

nomes de referência direta (o sexto jogador, a milionésima compra) são utiliza-


dos para identificar objetos. Nomes no plural (pessoas, consumidores, vendedores,
usuários, funcionários) e nomes comuns (todos, um jogador, um consumidor, um
funcionário, uma estação de trabalho) são utilizados para identificar classes.
3. Verbos (pagar, coletar, ler, solicitar) e frases predicativas (todos são remunera-
dos, mudaram simultaneamente) são utilizados para identificar os serviços.
Esta técnica tem muitas vantagens. Idiomas narrativos (inglês, chinês, francês, ale-
mão, japonês etc.) são muito bem-entendidos por todos em um projeto e podem ser um
meio de comunicação efetivo, tanto para o corpo de funcionários técnicos do projeto
como para os não-técnicos. Além do mais, há geralmente um mapeamento de um-para-
um entre nomes e objetos ou classes.
A utilização de nomes não demanda curva de aprendizado; a técnica é objetiva e
bem-definida, e não requer uma mudança completa de paradigma para quem é novato.
De mais a mais, esta técnica não requer uma OODA prévia; é possível aplicá-la a uma
especificação existente de requisitos escrita para análise estrutural e/ou para qualquer
outra metodologia.
Entretanto, esta técnica tem alguns inconvenientes. De um lado, ela é uma aborda-
gem indireta para encontrar objetos e classes. Nomes nem sempre são classes ou objetos
no domínio do problema. Muitas sentenças em uma especificação funcional estão na for-
ma errada para facilitar a identificação de objetos e classes, por exemplo, “baixar a tran-
sação” ou “o software calculará o salário médio”. Em muitos casos, os nomes,
especialmente sujeitos de sentenças, referem-se a (1) um conjunto inteiro ou uma confi-
guração de software de computador (por exemplo, CICS), (2) um subconjunto ou um
componente de software, (3) um atributo ou (4) um serviço. Mais adiante, abordaremos
essas falhas e a forma como podemos tratá-las.

Utilizar Diagramas Tradicionais de Fluxo de Dados


Esta técnica é o resultado oferecido por muitos desenvolvedores e gerentes de software
que tinham investido uma grande soma de dinheiro em dispendiosas ferramentas CASE
que suportam diagramas de fluxo de dados (DFDs — data flow diagrams). Na necessidade
de efetuar uma transição entre métodos de análise de requisitos de decomposição funcio-
nal (ou seja, análise estruturada) e desenho orientado a objeto, eles ansiavam por um
“Santo Graal” que protegesse seus investimentos e fizesse uma transição mais simples.
Esta técnica foi primeiramente publicada por Ed Seidewitz e Mike Stark, membros do
Goddard Space Flight Center, da Nasa.
Esta técnica é compreendida pelo seguinte:
■ Terminadores em diagramas de contexto (CDs — context diagrams)
■ Data stores em DFDs
■ Fluxos de dados complexos em DFDs
Antes da utilização desta técnica, uma análise estrutural deverá ser finalizada e to-
dos os CDs e DFDs deverão ser escritos.
Para utilizar esta técnica, faça o seguinte:
1. Mapeie cada terminador dos CDs em um objeto para encapsular a interface.
2. Identifique uma classe para encapsular a interface de cada conjunto de objetos
“terminadores” idênticos ou similares.
3. Mapeie cada data store dos DFDs em um objeto.
84 UML E C++ CAP. 5

4. Mapeie data stores que contenham mais do que um campo de dados em um ob-
jeto agregado
5. Mapeie toda ou uma parte da transformação de dados associada com o data store
em um serviço do objeto e, por conseguinte, da classe.
6. Mapeie fluxos de dados complexos (ou seja, registre com numerosos campos)
em um objeto.
7. Identifique subtransformações associadas com as partes do fluxo de dados e, em
seguida, mapeie estas subtransformações em serviços do objeto.
A principal vantagem desta técnica é que ela não requer que os analistas e desen-
volvedores tenham de mudar de paradigma. Se os DFDs originais forem bem-construí-
dos, a ocorrência de identificação falsa positiva de classes e objetos será rara. Finalmente,
existem muitos projetos que já dispõem de CDs e DFDs.
Infelizmente, a deficiência desta técnica também está diretamente relacionada ao
fato de não precisar fazer a mudança de paradigma. Praticamente todos os DFDs foram
originalmente escritos para decomposição funcional, e eles têm a inclinação de criar uma
arquitetura de classes mal-equilibrada.
Com a decomposição funcional, há uma tendência de assumir que a “haste princi-
pal” (stem) seja um conjunto de subconjuntos no nível apropriado. Além do mais, há uma
tendência de atribuir serviços ao nível correspondente no qual foi encontrado o subcon-
junto. Isto poderá originar a identificação de objetos no subconjunto errado. Embora a
identificação falsa positiva de objetos e classes seja rara, nem todos os objetos ou classes
são identificados. A raridade da identificação falsa positiva é totalmente dependente da
qualidade dos DFDs originais. Isso ainda é um método indireto de encontrar objetos e
classes; ele é baseado em abstração de dados, e não em abstração de objetos.
Em muitos casos, um objeto ou classe contém mais do que um data store. Dessa for-
ma, é possível que seus atributos sejam mapeados em objetos e classes muito embora seus
objetos e classes associados permaneçam não identificados.
Pelo fato de os DFDs representarem uma decomposição funcional, trechos de um
objeto podem ser dispersados ao longo de diversos DFDs designados a diferentes indiví-
duos. Dessa maneira, variantes diferentes do mesmo objeto podem ser identificadas re-
dundante e independentemente.
Finalmente, não são requeridas transformações para serem o serviço de um objeto.
Portanto, as transformações são geralmente operações compostas que precisam ser atri-
buídas a múltiplos objetos. Se os objetos não forem apropriadamente identificados, isto
resultará em objetos e classes fragmentados.

Utilizar Cartões de Colaboração e Responsabilidade de Classe (CRC


— Class-Responsibility-Collaboration)
Esta técnica foi desenvolvida por Rebecca Wirfs-Brock e outros. Esses desenvolvedores
observaram que a identificação de objetos e classes é uma tarefa humana que pode ser
estimulada pelo uso de pequenos pedaços de papel (ou seja, cartões CRC ou Post-it) para
a representação de objetos/classes. Foi constatado que, ao permitir que os desenvolvedo-
res manuseassem os cartões CRC, aqueles com experiência, criatividade e intuição conse-
guiam com freqüência identificar novos objetos/classes ao notarem “buracos” no atual
conjunto de cartões. Portanto, os cartões serviam tanto como um veículo para identificar
os objetos e classes anteriormente identificados como para estimular os desenvolvedores
CAP. 5 DESCOBRINDO OS OBJETOS 85

a descobrir, de maneira incremental e interativa, novos objetos e classes não correntemen-


te documentados.
Para a versão clássica desta técnica, proceda da seguinte forma:
1. Em um cartão CRC, documente o nome da classe e liste as responsabilidades
(isto é, que serviços ela provê) e os colaboradores (ou seja, objetos/classes de
que ela necessita para cumprir suas responsabilidades).
2. Identifique objetos e/ou classes ausentes que deverão ser acrescentados ao con-
junto existente de cartões CRC. Especificamente, procure responsabilidades que
não podem ser alocadas às classes já existentes e colaboradores que ainda não
foram designados para uma classe.
Para a versão moderna desta técnica, proceda da seguinte forma:
1. Em um Post-it, documente o nome da classe e liste as suas responsabilidades.
2. Posicione todas as classes em um quadro branco e desenhe arcos de associação
entre elas para representar os colaboradores.
3. Identifique objetos e/ou classes ausentes que deverão ser acrescentados ao con-
junto existente de classes. Procure atributos e serviços que não possam ser alo-
cados às classes já existentes.
Para uma versão de caso de uso desta técnica, realize o seguinte:
1. Comece o trabalho com uma sessão de debates dirigida ao estudo de classes.
■ Identifique algumas classes candidatas, relacionando por escrito alguns dos
“nomes” a partir do domínio do problema.
■ Selecione os nomes que têm algumas responsabilidades e anote-os nos cartões
(Post-it).
■ Faça uma breve descrição da classe para fins de orientação.
2. Utilize os casos de uso para gerar cenários ao sistema.
■ Atribua uma classe a um participante.
■ Represente o cenário e descubra as reais responsabilidades de cada uma das
classes.
■ Procure atributos e serviços que não possam ser alocados às classes atuais.
■ Acrescente classes ausentes para administrar estas situações da mesma forma
como você percorreu os cenários.
■ Atualize os Post-it a respeito do nome da classe, responsabilidades e colabo-
radores à medida que você for percorrendo os cenários.
3. Posicione todas as classes em um quadro branco e desenhe arcos de associação
entre elas para representar a colaboração.
Seguem algumas diretrizes para a utilização desta técnica:
■ Limite o tamanho do grupo a três ou seis participantes.
■ Cenários devem ser concretos e específicos (ou seja, os casos de uso devem
ser expandidos e reais).
■ Crie um pilha de descarte (não a “lata de lixo”; talvez você queira trazer à me-
mória uma classe descartada).
■ Registre cada cenário (nós preferimos utilizar diagramas de seqüência).
■ Utilize uma caneta simples para efetuar mudanças (isto garante que todos
concordem antes de serem feitas as mudanças).
86 UML E C++ CAP. 5

■ Transforme todos os objetos em um cenário concreto (se você não consegue


mostrar um cenário, ele não é real).
■ Você pode se dar por satisfeito quando tiver um modelo que possa tratar to-
dos os casos de uso.
Esta técnica é barata e de fácil utilização. Pouco esforço é investido na feitura das
classes, e elas podem ser facilmente descartadas. E mais, o método estimula comunicação
e não intimida os principiantes no assunto.
Historicamente, esta técnica é mais apropriada para pensar sobre objetos e classes,
e desenhá-los do que para identificá-los. É possível que você já tenha objetos e classes
com os quais possa utilizar esta técnica para identificar objetos e classes complementares.
Finalmente, os desenvolvedores precisam ter significativas experiência, criatividade e in-
tuição para que esta técnica seja consistentemente bem-sucedida. Entretanto, a versão re-
visada baseada em casos de uso descrita anteriormente é muito eficaz. Ela se dedica à
resolução de muitos problemas do método original.

Abordagens Recomendadas
Que abordagem utilizar depende da situação e da experiência de sua equipe de trabalho.
Não há uma única abordagem ou técnica que será apropriada para todos, especialmente
quando é levado em consideração o tempo de chegada ao mercado e o custo. Apesar deste
aviso, apresentamos neste capítulo as preferências mais comuns. Se você testou estruturas de
aplicações relevantes e seus repositórios associados, a técnica de “reutilizar estruturas de
aplicações” é o melhor enfoque. Se não existirem repositórios, então deverá haver um es-
forço em separado para a utilização de OODA.
Entretanto, para os novatos, este livro apresenta uma abordagem diferente que usa
muitas das técnicas já mencionadas (que se mostrou mais utilizável por iniciantes nesse
novo paradigma orientado a objeto).
Iniciaremos fazendo o seguinte:
1. Como ainda é transmitido para a maioria dos analistas/desenvolvedores um do-
cumento de requisitos em inglês narrativo (língua nativa) que utiliza os termos
dos especialistas em domínios, não é insensato utilizar a técnica da “Utilização
de Nomes” proposta por Abbott/Booch juntamente com a ressalva de que esta
técnica é usada para encontrar objetos potenciais e que ela não descobrirá todos
os objetos.
2. Identifique todos os “objetos potenciais” no domínio do problema pelo diálogo
interativo com o especialista em domínios. Lembre-se de que nós, e esse espe-
cialista, estamos lidando com objetos todos os dias. Além do mais, os especialis-
tas em domínios criam modelos mentais e utilizam abstração para lidar com a
complexidade de seus respectivos negócios. Necessitamos capturar os objetos
que estão no modelo mental desses especialistas. Se conseguirmos fazê-lo, sere-
mos capazes de lançar um software no mercado e aperfeiçoar esse pacote de
software mais rapidamente porque ele será consistente com o modelo mental
dos especialistas em domínios. O objetivo desta etapa é o de identificar todos os
objetos que esses especialistas conseguiriam identificar.
3. Use a técnica “Utilizar os Itens a Serem Modelados” para obter mais objetos po-
tenciais. Em uma tentativa de encontrar objetos, alguns estudiosos sugeriram
essa técnica como um modo de acionar nosso reconhecimento de “objetos poten-
CAP. 5 DESCOBRINDO OS OBJETOS 87

ciais”. As categorias fornecidas por três equipes de ponta formadas por conhe-
cidos especialistas são mostradas nas Tabelas 5.1, 5.2 e 5.3.
4. Como orientação para ajudar a eliminar alguns objetos potenciais “falsos” do
domínio do problema aplique o seguinte teste de definição. Um objeto pode:
■ Ser qualquer entidade do mundo real.
■ Ser importante para a discussão dos requisitos.
■ Ter uma fronteira muito bem-definida.

TABELA 5.1 Categorias Segundo Coad e Yourdon


Categorias Explicação

Estrutura Relacionamentos “tipo de” e “parte de”


Outros sistemas Sistemas externos
Dispositivos
Eventos lembrados Um evento histórico que precisa ser registrado
Papéis realizados O(s) diferente(s) papel(éis) realizado(s) pelos usuários
Localizações
Unidades de organização Grupos à que pertencem os usuários

TABELA 5.2 Categorias Segundo Shlaer e Mellor


Categorias Explicação

Tangível Carros, dados de telemetria, sensores


Papéis Mãe, professor, programador
Incidentes Aterrissagem, interrupção, colisão
Interações Empréstimo, encontro, casamento
Especificação Especificação de produtos, padrões

TABELA 5.3 Categorias Segundo Ross


Categorias Explicação

Pessoas Humanos que executam alguma função


Locais Áreas deixadas de lado para pessoas ou itens
Coisas Objetos físicos
Organizações Conjunto de pessoas, recursos, instalações e
habilidades com uma missão definida
Conceitos Princípios ou idéias não-tangíveis, per si
Eventos Coisas que ocorrem (geralmente em uma
determinada data e hora), ou como etapas em
uma seqüência ordenada

Uma entidade do mundo real tenta manter a análise no domínio do problema e


auxilia a eliminar objetos de implementação (objetos de desenho), tais como pi-
lhas, teclados e linguagens de programação. A frase “importante para a discus-
são dos requisitos” coopera na exclusão de alguns dos objetos não-relevantes ao
presente problema. Por exemplo, um ônibus espacial é uma entidade do mundo
88 UML E C++ CAP. 5

real, mas é difícil de acreditar que ele seja importante para nosso domínio do
problema “cortar a grama”. A “fronteira muito bem-definida” surgiu do traba-
lho de Booch e coopera na exclusão de frases verbais, como “indo para a loja”,
para que sejam consideradas como um objeto.
5. Se você praticou a técnica análise de casos de uso, considere passar por estas eta-
pas complementares:
■ Utilize os casos de uso para gerar cenários.
■ Utilize os cenários para encontrar classes ausentes.
■ Registre os cenários.

Exemplo
Retornemos ao nosso exemplo de corte de grama e apliquemos nossa técnica. Na etapa
1, aplicamos a técnica de “utilização de nomes”. A seguir é apresentada a declaração do
exemplo de corte de grama com os nomes sublinhados.
Temos uma família com o pai, John, a mãe, Jane, dois filhos, Peter e Paul, e duas
filhas, Elizabeth e Mary. John é um ator e Jane, uma dentista. Todas as crianças são
estudantes e o cachorro da família chama-se Lassie. A médica da família é Alice.
Esta família possui uma casa nos subúrbios da cidade de Nova Iorque e, embora o
corte do gramado da família seja, normalmente, uma tarefa para o pai, ela pode
também ser uma tarefa paga a qualquer um dos filhos. Contudo, é Jack quem tra-
balha como cortador de grama profissional nos arredores desta casa.

TABELA 5.4 Lista Inicial de Objetos Potenciais para o Exemplo do Corte de Grama
Lista de Objetos/Classes Potenciais
John Jane
Peter Paul
Elizabeth Mary
Lassie Gramado da família
Jack Alice
Família Filhos
Cortador de grama Médica
Cortador de grama profissional Médica da família
Dentista Ator
Mãe Pai
Filha Filho
Cidade de Nova Iorque Cachorro
Estudantes Cachorro da família
Casa Subúrbio
Tarefa Tarefa paga
Escritório Estúdio
Arredores Gramado
“Cortando a grama” É casado

Se aplicarmos neste momento nosso conhecimento do domínio, etapa 2, é possível


que acrescentemos cortador de grama como uma generalização de cortador de grama
profissional, grama como uma generalização de gramado da família, e cachorro como
uma generalização de cachorro da família. Além do mais, poderíamos adicionar estúdio
CAP. 5 DESCOBRINDO OS OBJETOS 89

como um local onde John trabalha e escritório como um lugar onde Jane e Alice praticam
suas profissões. Na etapa 3, poderíamos adicionar o relacionamento “é_casado” e “cor-
tando a grama” para a lista. A Tabela 5.4 ilustra um resultado típico de aplicação da téc-
nica combinada. Isso poderá nos deixar com alguns elementos “falsos positivos” — por
exemplo, “cortando a grama” poderia ser melhor modelado como um serviço do que
como um objeto.

■■ RESUMO
As quatro etapas padrão que recomendamos são:
1. Dado um documento de requisitos em inglês narrativo (língua nativa) que uti-
liza os termos dos especialistas em domínio, use a técnica “Utilize Nomes” pro-
posta por Abbott/Booch juntamente com a ressalva que esta técnica é usada
para encontrar objetos potenciais e que ela não descobrirá todos os objetos.
2. Identifique todos os “objetos potenciais” no domínio do problema pelo diálogo
interativo com os especialistas em domínio. Necessitamos capturar os objetos
que estão no modelo mental desses especialistas.
3. Use a técnica “Utilizar os Itens a Serem Modelados” para obter mais objetos po-
tenciais.
4. Como orientação para ajudar a eliminar alguns objetos potenciais “falsos” do
domínio do problema aplique o seguinte teste de definição. Um objeto pode ser:
(1) qualquer entidade do mundo real, (2) importante para a discussão dos requi-
sitos e (3) tendo uma fronteira muito bem-definida.
Quando você obtiver mais experiência, sugerimos as seguintes etapas para desco-
brir uma lista de objetos/classes potenciais:
1. Sublinhe todos os nomes no documento de requisitos ou nos casos de uso.
2. Filtre a lista de nomes para identificar itens fora do escopo do sistema. De modo
geral, eles são “objetos externos” que formam interfaces com o sistema. Esses ob-
jetos externos serão úteis para o diagrama de contexto, mas é vantajoso mantê-
los nesse mesmo diagrama. Tecnicamente, eles não são objetos no modelo final
da aplicação/sistema e, portanto, não se trata de objetos que queremos refinar.
Podemos então eliminá-los de nossa lista de objetos potenciais como parte da
aplicação/sistema.
3. Normalmente, diferentes nomes, ou frases tendo nomes, são utilizados para des-
crever a mesma coisa (conceito ou idéia). Um único termo deve ser selecionado,
e os termos alternativos, eliminados. Por exemplo, o “local de trabalho” e o “es-
critório” são provavelmente o mesmo conceito em praticamente todos os domí-
nios de problema. Se for utilizado um nome diferente para descrever o mesmo
item físico em um diferente domínio semântico (ou seja, para capturar um con-
ceito diferente), será preciso capturar ambos os conceitos. Um exemplo disso é
se você tivesse utilizado “mãe” e “dentista” como termos do domínio do proble-
ma aplicáveis a Jane em nosso exemplo do corte de grama. Entretanto, cada ter-
mo captura um conceito diferente, de modo que esses termos representam dois
objetos potenciais diferentes. Especificamente, “mãe” captura um conceito que
precisa lidar com o domínio semântico de parentesco, enquanto que “dentista”
captura um conceito em um domínio semântico de trabalho/na área de saúde.
90 UML E C++ CAP. 5

4. Algumas vezes o mesmo nome é utilizado para capturar dois conceitos diferen-
tes; um novo(s) termo(s) deve(m) ser criado(s) para garantir que cada conceito,
ou “coisa”, seja capturado. Por exemplo, considere o termo “assoalho”. Há dois
conceitos que podemos capturar utilizando essa palavra: (1) podemos nos referir
a um assoalho como parte de um aposento e (2) podemos nos referir a um piso
(ou um nível) em um prédio. Eles são dois conceitos (ou idéias), e não poderão
ser representados pelo mesmo objeto. Lembre-se de que o objeto é um modo de
capturar um conceito ou idéia.
5. Utilize a lista de categorias fornecida por nossos especialistas para verificar se
existem outros conceitos ou idéias que devemos acrescentar à lista.
6. Como orientação para ajudar a eliminar alguns objetos potenciais “falsos” do
domínio do problema, aplique o seguinte teste de definição. Um objeto pode ser:
(1) qualquer entidade do mundo real, (2) importante para a discussão dos requi-
sitos e (3) tendo uma fronteira muito bem-definida.
7. Se você tiver casos de uso, talvez queira utilizar nossa versão modificada.
Identificando Responsabilidades
IDENTIFICANDO RESPONSABILIDADES
6.Identificando Responsabilidades

A presentem-se como objetos reconhecidos, em lampejos e com a glória que


não lhes pertencem.
William Wordsworth

N a primeira etapa de nosso modelo, criamos uma lista de “objetos potenciais”. Agora,
precisamos determinar se esses objetos potenciais são “objetos reais” que gostaría-
mos de introduzir em nosso modelo. Portanto, a segunda etapa em nosso modelo é determi-
nar se existe alguma responsabilidade para esses objetos em nossa aplicação/sistema.
Antes que possamos fazer isso, entretanto, precisamos saber o que é um objeto e o
que pretendemos quando falamos em responsabilidade.

O Que É um Objeto?
No Capítulo 3, definimos um objeto a partir da perspectiva de um desenvolvedor, pois
ele se encontra relacionado com a produção de software. No Capítulo 5, fornecemos uma
definição diferente para utilização no descobrimento de objetos no domínio do problema.
Agora, precisamos usar uma definição mais técnica, sob a perspectiva do domínio do
problema, que nos auxilie a eliminar “objetos falsos” da lista de objetos potenciais. Uma
definição, ou visão, da análise de domínio de um objeto é:
Objeto. Um objeto é uma abstração de alguma coisa no domínio do problema que
reflete os recursos de um sistema de reter informações sobre ele, interagir com ele,
ou ambas as coisas.
Os seres humanos sempre formaram conceitos com vistas a entender o mundo.
Cada conceito captura uma idéia ou compreensão particular que temos do mundo no
qual vivemos. À medida que obtemos e organizamos mais conceitos, os utilizamos para
ajudar-nos a ver razão e sentido nas coisas de nosso mundo.

91
92 UML E C++ CAP. 6

Um objeto é uma dessas coisas para as quais aplicamos nossos conceitos. Exemplos
de objetos compreendem fatura, empregado, salário, trem, locomotiva, vagão de carga,
vagão de passageiro, vagão-restaurante, computador, teclado de computador, joystick,
tela, ícone em uma tela, mouse pad, organização, departamento, escritório e o processo
de escrever esta linha. Observe nos exemplos que um trem é composto de uma locomo-
tiva, vagão de carga, vagão de passageiros e vagão-restaurante. Dessa maneira, um objeto
pode ser composto de outros objetos. Esses objetos, por sua vez, podem ser compostos
de outros objetos e assim por diante. Por exemplo, um vagão de passageiros é composto
de portas, assentos, janelas etc. Outro exemplo é o de uma máquina composta de subcon-
juntos feitos de outros subconjuntos. E mais, o objeto poderá ser um item real (por exem-
plo, trem, carro, computador) ou um item abstrato (por exemplo, processo, casamento,
tempo).
No método da análise e desenho orientados a objeto, estamos interessados em um
objeto para os serviços de negócio. Lembre-se de que o modo como um sistema orientado
a objeto opera é o de um objeto solicitando o serviço de outro objeto por meio do para-
digma da passagem de mensagens. Assim, de uma perspectiva externa (ou interna), um
objeto é definido por seus serviços públicos; ou seja, um objeto é definido pelos serviços
que anuncia. Portanto, tecnicamente, o protocolo define a classe/objeto. Entretanto, sabe-
mos como desenvolvedores de software que apenas o uso do protocolo (coleção de pro-
tótipos) em si não é apropriado. Durante a etapa de análise, esperamos identificar os
dados e os métodos de negócio associados (como se provêem os serviços). Para preservar
os princípios de encapsulamento e ocultação de informações, um objeto ainda define uma
visão externa de seus métodos públicos para acesso por outros objetos. Essa visão externa
corresponde a seus serviços de negócio públicos. Esses serviços públicos são definidos
por protótipos e pelo único veículo por meio do qual outro objeto poderá ter acesso a
seus métodos e, por conseguinte, a seus dados. Segundo Wirfs-Brock (1990), a responsa-
bilidade é o conjunto de serviços públicos que um objeto provê e os dados associados ne-
cessários para prover esses serviços.
Para grande parte das pessoas, entretanto, é mais natural ter-se a capacidade de re-
lacionar atributos (ou seja, dados) a um objeto do que definir seus serviços, de forma que
consideraremos a identificação conjunta de atributos e serviços de um objeto como uma
única etapa.

O Que É um Atributo?
As coisas no mundo real apresentam características. Por exemplo, uma pessoa pode ser
descrita por sua altura, peso, cor do cabelo e assim por diante. Cada característica comum
a todas as instâncias do objeto/classe é abstraída como um atributo em separado. Por
exemplo, Joe tem 1,83m de altura, pesa 79kg, tem cabelo ruivo e olhos castanhos, enquan-
to que James tem 1,77m de altura, pesa 72,5kg, tem cabelo preto e olhos verdes. Para uma
pessoa, os atributos potenciais são altura, peso, cor do cabelo e cor dos olhos. Observe
que as características abstraídas em atributos são extremamente dependentes do proble-
ma. Considere o objeto “pessoa”. A maioria de nós pode, conceitualmente, surgir com um
grande número de características para uma “pessoa”. Quando limitamos nossa abstração
de uma pessoa a um domínio de problema específico ou a um problema específico, re-
duzimos o número de características aplicáveis.
Portanto, para fins de análise, o atributo é uma abstração de uma característica in-
dividual aplicável ao domínio de negócio e é possuído por todas as entidades que, em
CAP. 6 IDENTIFICANDO RESPONSABILIDADES 93

seus próprios termos, eram abstraídas como objetos. Sob uma perspectiva técnica, um
atributo é alguma variável (item de dado ou informação de estado) para a qual cada ob-
jeto (instância) tem seu próprio valor. Cada atributo deve ser provido de um nome ex-
clusivo dentro do modelo objeto/classe. Pelo fato de cada atributo ser capaz de assumir
valores, a gama de valores legais permitida para um atributo deveria ser também capturada.
De acordo com alguns autores orientados a objeto, existem quatro tipos de atribu-
tos: descritivos, de identificação, de informação de estado e referenciais. Atributos de in-
formação de estado são utilizados para manter uma história da entidade; isto é geralmente
necessário para capturar os estados das máquinas de estado finitas utilizadas para imple-
mentar o aspecto dinâmico do comportamento. Atributos referencias são fatos que vin-
culam um objeto a outro e são utilizados para capturar relacionamentos. Entretanto,
capturar estados e relacionamentos com o uso de atributos é uma questão de implemen-
tação. Neste livro, os estados e relacionamentos serão representados pictorialmente (grafica-
mente), e não como parte da lista de atributos de um objeto ou classe. Neste capítulo,
abordaremos exclusivamente atributos descritivos e de identificação.

Atributos Descritivos
Atributos descritivos são fatos intrínsecos a cada entidade. Se o valor de um atributo des-
critivo muda, isso apenas representa que algum aspecto de uma entidade (instância) mu-
dou. Sob a perspectiva do domínio do problema, ela ainda é a mesma entidade. Por
exemplo, caso Joe adquira um peso extra de 0,5kg, partindo-se de quase todas as pers-
pectivas do domínio do problema, ele ainda é uma pessoa. E, de forma mais importante,
Joe também é a mesma pessoa; exatamente como era antes de ganhar esse peso extra.

Atributos de Identificação
Atributos de identificação são utilizados para nomear ou rotular uma entidade. Normal-
mente, eles são um tanto arbitrários e freqüentemente utilizados como identificadores ou
como parte de um identificador. Se o valor de um atributo de identificação muda, isso
apenas significa que um novo nome foi dado à mesma entidade. Na realidade, os atribu-
tos de identificação não precisam ser exclusivos. Por exemplo, se Joe muda seu nome
para James, isso é tudo que é mudado; seu peso, altura etc. continuam sendo os mesmos.1

O Que É um Serviço?
Um serviço pode ser definido como um trabalho realizado por outros. De certo modo, os
serviços de um objeto são os trabalhos públicos ou anunciados que um objeto está dis-
posto a realizar quando solicitado por outro objeto por meio de um paradigma de passa-
gem de mensagens. Estes serviços são definidos por protótipos.
O protótipo é composto de duas partes: (1) nome do serviço (denominado seletor
por alguns especialistas), e (2) argumentos do serviço (denominados assinatura por al-
guns especialistas). Portanto, todos os objetos devem definir seus protótipos para cada
serviço que planejam prover.

1. Durante a análise, no passado, muitos desenvolvedores também requeriam um único atributo de


identificação, que era utilizado como uma chave para traduzir objetos em banco de dados relacional.
Hoje em dia, existe um melhor suporte para tecnologia orientada a objeto de modo que este requisito
não é mais necessário.
94 UML E C++ CAP. 6

A coleção definida de protótipos é o protocolo da classe/objeto, que é a interface do


objeto (ou seja, todos os seus serviços anunciados). O seletor (ou seja, o nome do serviço)
deverá ser externamente focado. Por exemplo, o serviço de um restaurante local poderá
ser “trocar notas por moedas”.2 A expressão “trocar notas por moedas” está definindo
um serviço sob a perspectiva do usuário, enquanto que a nomeação do serviço “trocar
moedas por notas” é uma perspectiva interna do serviço. Nomear serviços é muito difícil
porque precisamos de nomes que reflitam uma perspectiva externa e sejam consistentes
com o domínio semântico no qual o objeto resida.

O Que É um Método?
Tecnicamente, um método é um conjunto detalhado de operações que um objeto executa
quando outro objeto solicita um serviço.3 Entretanto, por definição, um comportamento
é um conjunto de ações que um objeto é responsável por exibir — por conseguinte, alter-
nativamente, um método especifica um comportamento de um objeto. O método é simi-
lar a uma função na decomposição funcional. Todavia, existem algumas diferenças muito
importantes. Lembre-se de que estes métodos somente podem ser acessados por meio do
paradigma da passagem de mensagens, e que cada método pode utilizar unicamente seus
próprios dados e os dados transmitidos a ele por meio de sua lista de argumentos.4 Tal-
vez mais importante, esses serviços deveriam ser especificados a um nível de profundi-
dade consistente com o domínio de semântica no qual resida o objeto.

Identificação de Atributos
A questão principal aqui é quais os dados que acreditamos que o objeto tem a responsa-
bilidade de conhecer e deter. As questões a seguir devem ser respondidas sobre cada ob-
jeto potencial:
■ Como é descrito este objeto em geral?
■ Que partes da descrição geral são aplicáveis a este domínio de problema?
■ Qual é a descrição mínima necessária para esta aplicação?
Se for adotada a abordagem oriental ou taoísta para a análise orientada a objeto, o
desenho de seu sistema será feito efetuando-se somente as duas primeiras perguntas.
Você não ficará preocupado com a aplicação específica que estiver implementando. Cons-
tatamos que com esse enfoque há uma tendência de produzir um modelo mais flexível e
robusto partindo-se de uma perspectiva de negócio. Você, então, estará capacitado a res-
ponder às mudanças de mercado mais rapidamente. Essa flexibilidade é conseguida nor-
malmente à custa de desempenho e de utilização de espaço.
Se forem respondidas todas as três perguntas e verificada unicamente a presente
aplicação (abordagem ocidental), há uma tendência de produzir-se um sistema de alto
desempenho, finamente ajustado e com boa utilização de espaço, que faz um uso mais

2. A razão pela qual o restaurante provê o serviço pode ser porque ele obtenha uma grande quantidade
de trocado a partir de gorjetas, e o banco não aceite moedas para depósito.
3. Inversamente, um serviço de um objeto define como outro objeto pode ter acesso a um
comportamento específico (método/função).
4. Em um capítulo posterior, constataremos que o objeto também tem acesso a outros dados por meio
de serviços de outros objetos com os quais ele mantenha relacionamentos.
CAP. 6 IDENTIFICANDO RESPONSABILIDADES 95

eficiente do hardware. Entretanto, isso será conseguido à custa de se ter menos clas-
ses/objetos reutilizáveis e menos flexibilidade para responder ao mercado.
Os atributos são raramente descritos por completo em um documento de requisitos.
Felizmente, eles raras vezes afetam a estrutura básica do modelo. É preciso desenhar com
base no conhecimento do domínio da aplicação e do mundo real para descobri-los.
Pelo fato de a maioria das diretrizes de identificação de atributos não ajudarem a
diferenciar entre atributos falsos e reais, Rumbaugh (1991) propôs as seguintes sugestões
para eliminar atributos “falsos”:
1. Objetos. Se a existência independente em vez de somente o valor do atributo for
importante, então o atributo será um objeto e haverá a necessidade de um vín-
culo para ele. Por exemplo, considere um objeto Pessoa. O endereço ou cidade
em que a pessoa mora é um atributo ou outro objeto? Se, em sua aplicação, você
não manipular o endereço sem conhecer a quem ele pertence, então ele será um
atributo. Entretanto, se você manipular o endereço como uma entidade em si,
então esse endereço deverá ser um objeto com um vínculo entre este e a pessoa.
2. Qualificadores. Se o valor de um atributo depende de um contexto particular,
então considere reafirmá-lo como um qualificador. Por exemplo, o número de
um funcionário não é na realidade um atributo de um objeto Pessoa. Considere
uma pessoa com dois empregos. Isso realmente qualifica como um vínculo “em-
prega” entre o objeto empresa e o objeto pessoa.5
3. Nomes. Um nome é um atributo quando ele não depende do contexto. Por
exemplo, o nome de uma pessoa é um atributo de Pessoa. Note que o atributo,
como no nome de uma pessoa, não tem de ser exclusivo. Entretanto, nomes são
geralmente qualificadores e não atributos. Como tal, eles normalmente definem
um papel em uma associação ou definem uma abstração de subclasse ou super-
classe. Por exemplo, pai e professor não são atributos de Pessoa. Ambos são pro-
vavelmente papéis para associações. Um outro exemplo é uma pessoa masculina
e outra feminina. Há dois modos de capturar isso: considerar sexo um atributo
de Pessoa ou montar duas subclasses.6
4. Identificadores. Certifique-se de não listar o identificador único de que as lin-
guagens orientadas a objeto necessitam para, inequivocamente, referenciar um
objeto. Isso é implicitamente suposto como parte do modelo. Entretanto, relacio-
ne os identificadores do domínio de aplicação. Por exemplo, um código de conta
é um atributo de Conta, enquanto que a identificação de uma transação prova-
velmente não é um atributo.
5. Atributos de vínculos. Se o atributo proposto depende da presença de um vín-
culo, então ele é um atributo do vínculo e não dos objetos do vínculo. Faça do
vínculo um objeto associativo, e do atributo proposto, um de seus atributos. Por
exemplo, suponhamos que Jim seja casado com Mary. A data do casamento de-
les é um atributo da associação “é_casado”, e não um atributo de Jim ou de
Mary.

5. Se não for importante para a aplicação que uma pessoa tenha um segundo empregador, então
torná-la um atributo do objeto Pessoa talvez seja satisfatório.
6. Tecnicamente, criar duas subclasses corresponde ao modelo mais preciso. Entretanto, se nunca
tivermos serviços ou relacionamentos específicos de um sexo, então será apropriado fazer de sexo
um atributo durante a implementação.
96 UML E C++ CAP. 6

6. Detalhes finos. Omita os atributos menores que não afetam os métodos.


7. Atributos discordantes. Um atributo que indique ser completamente não-apa-
rentado com todos os outros atributos poderá indicar que o objeto talvez precise
ser fracionado em dois objetos. Uma classe deverá ser coerente e simples (ou
seja, deverá representar um conceito que opere em um domínio semântico
simples).
Para ajudá-lo a descobrir atributos, sugerimos que você comece utilizando os adje-
tivos e frases possessivas existentes no documento de requisitos. Por exemplo, carro ver-
melho, o homem de 40 anos de idade, a cor do caminhão e a posição do cursor. Em seguida,
após a identificação de alguns atributos, faça as perguntas precedentes para identificá-los
em um maior número.

Especificando Atributos
Coad e Yourdon (1991) expressaram isto muito bem quando declararam: “Faça cada atri-
buto capturar um conceito atômico”. O conceito atômico significa que um atributo con-
terá um único valor ou um grupo de valores estreitamente relacionados que a aplicação
trata como um todo. Exemplos de atributos incluem itens de dados individuais (tais como
idade, salário e peso) e itens de dados compostos (tais como nome, endereço e data de
nascimento).
Questões sobre normalização, desempenho, identificação de objetos e retenção de
informações recalculáveis deveriam ser deixadas para o projeto e implementação. Entre-
tanto, a forma dos dados (caractere, número inteiro, série, cor etc.) deveria ser especifica-
da. Sua amplitude, restrições e invariantes também deveriam ser capturadas.
Recomendamos que se capturem restrições e invariantes pela utilização da semântica de-
clarativa. Veja os capítulos posteriores para discussões sobre regras.
Em virtude da identificação de atributos ser um processo difícil, Shlaer e Mellor
(1992) ofereceram propriedades para as quais um atributo deve aderir. Acrescentamos
uma propriedade, identificada nesta lista como Propriedade Zero.
Propriedade Zero. Um atributo precisa capturar uma característica consistente com
o domínio semântico no qual este objeto (como um conceito ou idéia) reside. Por
exemplo, considere o objeto Programador. Uma característica de um programador
poderá ser os anos de experiência em escrever programas de computadores. Entre-
tanto, a idade provavelmente não é um atributo de Programador; ela é talvez um
atributo de Pessoa, que é um objeto diferente de Programador. Agora que fizemos
a perigosa suposição de que todos os programadores são também pessoas, então
podemos criar um objeto Programador Humano fazendo-o herdar os atributos de
programador (por exemplo, anos dedicados à escrita de programas de computado-
res) do objeto Programador e os atributos humanos (por exemplo, idade) do objeto
Pessoa. Portanto, o objeto Programador Humano é uma composição de dois objetos.
Propriedade Um. Uma instância (entidade) tem exatamente um valor (dentro de
sua amplitude) para cada atributo em qualquer determinado tempo. Por exemplo,
podemos selecionar cor dos olhos como um atributo do objeto Pessoa com as varia-
ções de preto, castanho, azul e verde. Se descobrirmos que uma pessoa, Carey, que
CAP. 6 IDENTIFICANDO RESPONSABILIDADES 97

seria uma instância de Pessoa, tem um olho verde e um olho castanho, então não
poderemos atribuir verde e castanho como a cor dos olhos dela7.
Propriedade Dois. Um atributo não deverá conter uma estrutura interna. Por exem-
plo, se fizermos de nome um atributo de Pessoa, então não estamos interessados em
manipular independentemente o nome e o sobrenome no domínio do problema.
Propriedade Três. Um atributo deverá ser uma característica da entidade como um
todo e não uma característica de suas partes divisíveis. Por exemplo, se especificar-
mos “computador” como um objeto que é composto de um monitor, teclado, mouse
e CPU, o tamanho da tela será um atributo de terminal, não do computador.
Propriedade Quatro. Quando um objeto é uma abstração de um conceito que inte-
rage com outros objetos (especialmente objetos tangíveis), o atributo do objeto deve
estar associado ao conceito e não aos outros objetos. Por exemplo, suponhamos que
precisemos transferir óleo de um tanque de armazenamento a um tanque separador,
e que definamos um objeto Transferência de Óleo para capturar o conceito sobre o
volume de líquido que se move. Então, se designarmos o atributo galão ao objeto
Transferência de Óleo, ele deverá representar o número de galões que serão trans-
feridos. Ele não poderá ser utilizado para representar o número de galões nos tan-
ques de armazenamento ou de separação.
Propriedade Cinco. Quando um objeto tem um relacionamento com um outro ob-
jeto, especialmente um objeto do mesmo tipo (classe), o atributo deverá capturar as
características do objeto, e não o relacionamento ou o(s) outro(s) objeto(s) no rela-
cionamento. Por exemplo, se acrescentarmos salário como um atributo e relaciona-
mento marital ao objeto Pessoa, não poderemos utilizar o pagamento do cônjuge
como o valor para o atributo do salário de um cônjuge que não esteja trabalhando,
e a data do casamento deles não será um atributo de qualquer um dos dois cônjuges.

Identificando Serviços
De acordo com Coad e Yourdon (1991), os serviços podem ser classificados quer como
algoritmicamente simples, quer como algoritmicamente complexos. Dentro de cada uma
dessas categorias, serviços podem ser decompostos em vários tipos. Cada categoria e seus
tipos associados são apresentados nas Tabelas 6.1 e 6.2. Coad e Yourdon acreditam que
80% a 90% dos serviços serão algoritmicamente simples. Consideramos que esse número
está mais próximo de 60%.
Serviços algoritmicamente simples normalmente não são colocados em um modelo
orientado a objeto. Supõe-se que toda classe/objeto tenha esses serviços. Isso torna o mo-
delo mais simples e colaborará quanto à interpretação de modelos extensos e complexos.
Nesta etapa, estamos interessados apenas em identificar aqueles serviços de negócio al-
goritmicamente complexos que devem ser providos pelo objeto.
Para ajudar-nos a encontrar serviços, deveríamos utilizar os verbos presentes em
nosso documento de requisitos. Normalmente, uma sentença é encontrada na forma “su-
jeito — verbo — objeto”. Neste caso, o verbo geralmente está definindo um método que deve ser
provido pelo objeto da sentença.8 Por exemplo, “uma pessoa bate na bola”. A tendência de

7. Isso pode ser facilmente resolvido tendo-se a cor dos olhos como um atributo de um objeto Olho, e
Pessoa possuindo (ter um atributo referencial a) dois olhos. Isso requer a modificação do modelo,
mas a mudança é para melhor — o modelo reflete com maior precisão a realidade do domínio.
8. No paradigma orientado a objeto, a interação entre objetos é via passagem de mensagens.
98 UML E C++ CAP. 6

um principiante é definir um serviço “bater” para o objeto Pessoa. Em OD, a sentença é


utilizada para definir um serviço “recebendo uma batida” para o objeto Bola. Para o ob-
jeto Pessoa bater no objeto Bola, este último deve ter um serviço protótipo dentro de seu
protocolo para receber o pedido de mensagem “bater” a partir de Pessoa.

TABELA 6.1 Serviços Algoritmicamente Simples


Criar Cria e inicializa um novo objeto
Conectar Conecta um objeto a outro objeto
Acessar Obtém ou estabelece valores de atributos
Desconectar Desconecta um objeto de outro objeto
Eliminar Elimina um objeto

TABELA 6.2 Serviços Algoritmicamente Complexos


Calcular Cálculos que o objeto é responsável por realizar
aplicados a seus valores
Monitorar Monitora aquilo pelo qual o objeto é responsável
a fim de detectar ou responder a sistema ou
dispositivo externo ou objeto interno
Questionar Calcula um valor funcional sem modificar o
objeto

Após utilizar os verbos para identificar serviços (aplicação — caso específico), nós de-
veríamos considerar a generalização do nome de serviço para o domínio. Lembre-se de que
o nome deverá ser dado sob uma perspectiva externa (usuário do serviço). Queremos utilizar
uma palavra tão genérica quanto possível para conceder uma oportunidade de encontrarmos
classes abstratas, que são os objetos/classes mais difíceis de serem descobertos.

Especificando Serviços
A especificação de serviços é feita definindo-se o protótipo para o serviço. Lembre-se do
Capítulo 3 em que o protótipo é composto do nome e da assinatura do serviço. O nome
selecionado deve refletir quer um item externo, quer a visão de serviço de um usuário.
A assinatura é uma lista de argumentos que precisam ser passados para o objeto
para que ele realize o serviço nomeado. Esse é o dado complementar que um objeto não
tem e espera que seja fornecido pelo objeto solicitador.
Normalmente, é uma boa prática não determinar mais argumentos do que o neces-
sário para o específico objeto realizar (executar) seu método associado ao serviço. Entre-
tanto, em virtude de estarmos tentando capturar conceitos e não definições técnicas, a
lista de argumentos poderá ser ajustada para beneficiar-se do polimorfismo mais adiante.
Assim, durante esta etapa, o nome do serviço deveria ser considerado muito cuidadosa-
mente, e podemos nos descuidar um pouco mais dos argumentos.

Abordagem Recomendada
Nossa abordagem para identificar responsabilidades é:
CAP. 6 IDENTIFICANDO RESPONSABILIDADES 99

1. Identificar atributos
a. Examine todos os adjetivos e frases possessivas existentes no documento de
requisitos.
b. Faça as seguintes perguntas:
1. Como é descrito este objeto em geral?
2. Que partes da descrição geral são aplicáveis a este domínio de problema?
c. Se você quiser seguir a escola ocidental, faça também esta pergunta:
1. Qual é a mínima descrição necessária para esta aplicação?
d. Utilize as sugestões de Rumbaugh para eliminar atributos falsos.
2. Especificar atributos
a. Faça de cada atributo um “conceito atômico”.
b. Elimine atributos passíveis de cálculo ou deriváveis dos atributos básicos.
c. Elimine atributos que abordem normalização, desempenho ou identificação
de objetos durante esta etapa.
d. Teste se o atributo adere a todas as propriedades sugeridas por Shlaer e
Mellor e o fato de que, como um grupo, os atributos estão no mesmo domí-
nio semântico (boa coesão).
3. Identificar serviços
a. Examine os verbos presentes em um documento de requisitos. Lembre-se de
que o verbo normalmente define os serviços do objeto da sentença.
b. Examine os cenários do usuário que, em geral, identificam indiretamente um
grande número de serviços.
c. Examine cada característica que, geralmente, requer serviços de muitos objetos.
4. Especificar serviços
a. Confira um nome ao serviço que é externamente (relativamente a ele pró-
prio) enfocado.
b. Defina a assinatura do serviço identificando sua lista de argumentos.

Exemplo
Retornaremos ao nosso exemplo do aparo e corte de grama. Neste exemplo, temos pou-
cos adjetivos que nos ajudariam a definir atributos para os objetos potenciais que identi-
ficamos na etapa 1a. Entretanto, estamos lidando com objetos que conhecemos e com um
domínio de problema de fácil compreensão para todos. Dessa maneira, podemos começar
com a etapa 1b, da primeira questão. Iniciaremos com os oito objetos Pessoa: John, Jane,
Peter, Paul, Elizabeth, Mary, Jack e Alice. Exemplos de atributos descritivos que os obje-
tos Pessoa provavelmente têm são data de nascimento, altura, peso, cor do cabelo, cor dos
olhos e sexo; os atributos de identificação são Nome e NúmeroDaPrevidênciaSocial (veja
a Tabela 6.3).
Antes de nos movermos para a segunda questão, temos de decidir sobre o domínio
do problema. Podemos decidir que queremos definir nosso domínio do problema para
abranger tudo. Se isso for verdadeiro, nossa lista de atributos teria de ser estendida para
abranger a área da saúde de uma pessoa, emprego, impostos, investimentos, relaciona-
mentos sociais e assim por diante. Isso provavelmente tornaria o objeto muito grande, e
tampouco manutenível ou utilizável em alguma aplicação, a menos que ele contasse com
recursos de CPU e espaço ilimitados. (É aí que entra a idéia do gerenciamento de defini-
ção de uma classe/objeto para toda sorte de uso.) Entretanto, isso não é tão ruim como
100 UML E C++ CAP. 6

parece. Normalmente, qualquer negócio é confinado pelo tipo de domínio para o qual ele
se encontra nos negócios.
Agora, limitaremos nosso domínio às tarefas caseiras comuns aos proprietários de
casas. Estamos interessados em capturar aplicações como cortar a grama, fertilizar o ter-
reno, semear o terreno, aparar os arbustos, limpar a piscina, limpar o carpete, pintar a
casa, reparar o telhado, desobstruir as calhas, limpar as chaminés etc.
Se pensarmos muito cuidadosamente sobre o domínio do problema, provavelmente
concluiremos que nenhum dos atributos que identificamos é aplicável a nosso domínio de
problema de “tarefas caseiras comuns aos proprietários de casas”. Entretanto, partindo-se da
descrição de requisitos, podemos ver que o cronograma de tempo de uma pessoa é útil, e que
talvez seja necessário alguma informação de estado (Papai está cansado ou não?).
Além do mais, uma vez que médicos da família não estão dentro do domínio de “tare-
fas caseiras comuns aos proprietários de casas”, podemos excluir Alice e MédicaDaFamília
de nossa lista de objetos potenciais. Aplicando-se lógica similar, muito provavelmente pode-
mos excluir de nossa lista Estúdio, Dentista, Ator, Estudante e Subúrbio. Provavelmente,
também é seguro excluir Pai, Mãe, Filho e Filha da lista,9 particularmente nos tempos mo-
dernos de igual oportunidade de trabalho para todos. Mantivemos Lassie e Cachorro na lista,
pelo fato de que um cachorro pode ser uma alternativa para fertilizante químico. Precisa-se
da casa para outras aplicações, como para a pintura dela. Nós não estamos certos sobre Ta-
refa e TarefaPaga. Suspeitamos que necessitaremos de CortadorDeGrama, CortadorDeGra-
maProfissional e GramadoDaFamília para nossa aplicação.

TABELA 6.3 Atributos e Valores para uma Seleção de Objetos no Exemplo


do Aparo e Corte de Grama
Nome do Atributo John Jane Peter Paul
Data de Nascimento 12/9/50 24/2/52 30/8/79 05/3/81
Altura 1,76m 1,82m 1,87m 1,98m
Peso 49,8kg 72,5kg 95,2kg 88,4kg
Cor do cabelo grisalho branco preto loiro
Cor dos olhos azul cinza verde preto
Sexo masculino feminino masculino masculino
Nome John Doe Jane Doe Peter Doe Paul Doe
Previdência Social 123-45-6789 234-56-7899 345-67-1234 456-78-0123

Depois de aplicarmos a segunda questão, nossa lista de atributos revisada se pare-


cerá com o seguinte:
Para John, Jane, Peter, Paul, Elizabeth e Mary, há um atributo: prazo10.
Para Jack e CortadorDeGramaProfissional, os atributos são endereço, número do te-
lefone e programação de tarefas.
Para Lassie e Cachorro, há um atributo: programação de tarefas.

9. Isto é na verdade incorreto. Veremos em capítulo posterior que necessitamos utilizar o


relacionamento pai-filho. Entretanto, pelo fato de esse tipo de relacionamento constituir um diferente
domínio semântico do que “tarefas caseiras comuns aos proprietários de casas”, temos a tendência
de eliminar estes objetos durante esta etapa.
10. O prazo poderia facilmente ser bastante complexo a ponto de necessitar ser modelado como um
objeto com um vínculo para a pessoa.
CAP. 6 IDENTIFICANDO RESPONSABILIDADES 101

Para Casa, os atributos são endereço, número do telefone, data da última pintura,
data do último reparo no telhado etc.
Para GramadoDaFamília, os atributos são altura da grama, data da última colocação
de sementes, data da última fertilização etc.
Para Tarefa e TarefaPaga, não foram encontrados quaisquer atributos.11
Neste momento, estamos prontos para a terceira questão; nossa aplicação é “cortan-
do a grama”. Se restringirmos nós mesmos unicamente à nossa aplicação, eliminaremos
da consideração os seguintes objetos potenciais: Lassie, Cachorro, Tarefa, TarefaPaga e
Casa.
Depois de aplicarmos a terceira questão, nossa lista de atributos revisada se parece-
rá com o seguinte:
Para John, Jane, Peter, Paul, Elizabeth e Mary, há um atributo: programação de ta-
refas.
Para Jack e CortadorDeGramaProfissional, os atributos são endereço, número do te-
lefone e programação de tarefas.
Para GramadoDaFamília, há um atributo: altura da grama.
Ao aplicarmos a terceira questão, eliminaremos objetos (e, se tivéssemos um grande
número de atributos, provavelmente alguns atributos) aplicáveis ao domínio geral do
problema, mas não à nossa aplicação específica.
Na filosofia taoísta, o foco está mais na trajetória do que no destino final ou, em nos-
sa terminologia, mais no processo do que no objetivo. Quando traduzimos isso para a
modelagem orientada a objeto, a filosofia taoísta informa-nos para capturar os objetos no
domínio do problema em vez de nos objetos que cooperarão na resolução do problema
imediato. É crença dessa filosofia que se nos concentrarmos no objetivo talvez possamos
ignorar informaçes valiosas, ao passo que o foco no processo possibilitará à trajetória nos
revelar o caminho. Traduzido para a tecnologia orientada a objeto, a concentração no pro-
blema específico ou na aplicação fará com que ignoremos conceitos importantes e, como
resultado, nossas classes (objetos) se tornarão menos reutilizáveis.
Se você estiver tentando produzir software flexível e reutilizável, deverá aplicar
uma filosofia oriental ou taoísta para a resolução de problemas e modelagem orientada a
objeto. Na filosofia oriental, não teríamos respondido à terceira questão. Seria de esperar
que a modelagem correta do domínio do problema fosse também automaticamente con-
ter nossa solução de negócio para a nossa aplicação. Esse tipo de filosofia é baseado na
experimentação, distintamente de nosso clássico pensamento ocidental — o qual é basea-
do em planejamento.
A etapa 2 para atributos é deixada como exercício ao leitor.
Agora realizaremos as etapas 3 e 4 para serviços. Pelo fato de termos planejado um
exemplo muito simples, o único serviço é “cortar a grama”. Se examinarmos as pessoas
em geral, o número de serviços que elas provêem é interminável; mas, não queremos cap-
turar todos os serviços. Se estivéssemos inclinados a apenas considerar serviços no domí-
nio de “tarefas caseiras comuns aos proprietários de casas”, teríamos acrescentado
serviços como pintar a casa, fertilizar o terreno, semear o terreno, limpar a casa, limpar
as calhas, varrer a calçada, aparar os arbustos e juntar as folhas com ancinho. Entretanto,

11. A soma de dinheiro paga poderia ser um atributo de TarefaPaga.


102 UML E C++ CAP. 6

quando retornamos à nossa aplicação específica, estamos novamente diante de um servi-


ço: “cortar a grama”.
Portanto, temos os seguintes objetos com um serviço de “cortar a grama”: John, Pe-
ter, Paul, Elizabeth, Mary, Jack e CortadorDeGramaProfissional.
Observe que o gramado da casa da família já possui um serviço mudarAltura ou de-
finirAltura “altura da grama” pelo fato de ser algoritmicamente simples.
Tão útil quanto a filosofia oriental nos auxilia a definir uma melhor especificação
para objetos, nós ainda não queremos incluir em nosso modelo final objetos ou serviços
que não sejam necessários para nossa aplicação específica. Assim quando tudo estiver
pronto, será preciso aplicar os seguintes testes para assegurar que tenhamos unicamente
os objetos necessários (mas, neste momento, provavelmente não em número suficiente).
Testes:
1. O objeto deverá prover algum serviço para algum outro objeto na aplicação/sis-
tema ou um serviço externo na interface para algum objeto externo.
2. Em geral, o objeto deverá ter múltiplos atributos. Há casos em que um objeto
não terá nenhum atributo e somente proverá serviços.12
Às vezes é útil manter um objeto (uma abstração) que possa nos ajudar na organi-
zação de nosso modelo estrutural, capturando os relacionamentos entre objetos. Nesse
caso, os únicos serviços daquele objeto talvez sejam seu construtor e destruidor.

■■ RESUMO
Nossa abordagem para a identificação de responsabilidades é:
1. Identificar atributos
2. Especificar atributos
3. Identificar serviços
4. Especificar serviços

12. Entretanto, este tipo de objeto deverá ser raro e de modo geral não é descoberto durante esta etapa.
Especificando
Comportamento Estático
ESPECIFICANDO COMPORTAMENTO ESTÁTICO
7.Especificando Comportamento Estático


∇2ψ = − (E−V) Ψ
h
Equação de Schroedinger Independente do Tempo

N a segunda etapa de nosso modelo criamos uma lista de “objetos reais”. No processo
de descobrimento desses “objetos reais”, identificamos seus atributos e serviços. En-
tretanto, durante aquela etapa, adotamos uma visão externa do sistema. Mais especifica-
mente, não ficamos preocupados com a forma como um objeto iria prover (executar) o
serviço. Na etapa 3 de nosso modelo, precisamos capturar como cada objeto em particu-
lar provê os serviços identificados na etapa 2. No processo de especificação desses servi-
ços, também é necessário que identifiquemos serviços complementares que deverão ser
providos por outros objetos.

O Que É Comportamento?
No capítulo anterior, definimos um serviço como “trabalho feito por outros”.1 O compor-
tamento pode ser definido como o conjunto de ações que um objeto é responsável por
exibir quando ele provê um serviço específico. Um outro objeto poderá acessar um com-
portamento específico de um objeto unicamente por meio do serviço apropriado. Esse
comportamento é geralmente capturado como um método (função) do objeto.

1. Tecnicamente, um objeto pode prover serviços a si mesmo. Isso é bastante comum para objetos
complexos.

103
104 UML E C++ CAP. 7

Um comportamento é definido quando forem especificados os seguintes itens: (1)


todos os inputs (argumentos do serviço), (2) todos os outputs e (3) a forma como (da pers-
pectiva do domínio) o objeto irá prover o serviço. O comportamento pode ser estático ou
dinâmico (veja o Capítulo 2). Neste capítulo, serão discutidas técnicas para se capturar o
comportamento estático. As técnicas para a captura do comportamento dinâmico serão
tratadas no Capítulo 8.
No comportamento estático, o conjunto de ações é capturado pelas operações (códi-
go) dentro do método. Por definição, as operações dentro do método não serão afetadas
por eventos externos ou internos2 (ações). Um bom exemplo de comportamento estático
é o serviço de “raiz quadrada” para Número. Se for solicitado o serviço de “raiz quadra-
da” do número 4, que é uma instância de Número, o resultado será sempre 2. Não há
ação externa ou interna que faria o método de Número mudar o algoritmo para o cálculo
da raiz quadrada e, em seguida, produzir um resultado diferente.
O modo mais natural de documentar comportamento3 é pelo uso de qualquer idio-
ma natural. Infelizmente, todo o idioma é rico em ambigüidades e inconsistências. Quan-
do ele é falado, um pouco de clareza é fornecido pela entonação, movimento das mãos e
pela linguagem corporal. Em muitas situações, especialmente em especificações, até mes-
mo a fala das palavras não atenuará as ambigüidades ou inconsistências. O problema é
que os idiomas são conjuntos de elementos atômicos (palavras) que carecem de uma se-
mântica bem-definida (definição bem-clara e consistente no domínio do problema). Dessa
maneira, a coleção resultante de palavras que formam sentenças ou parágrafos torna-se
ambígua ou inconsistente, e, portanto, as descrições de comportamento redigidas em um
idioma também se tornam inconsistentes e ambíguas.
Uma solução é construir um invólucro em torno do idioma com uma semântica
bem-definida. Essa técnica é utilizada em todos os campos de trabalho. Por exemplo, em
contabilidade, palavras como livro-razão, débito e crédito têm semântica bem-definida e
muito precisa. Similarmente, no mundo dos computadores, palavras como input, output,
bit e byte têm semântica bem-definida.
Quando construímos um invólucro em torno do idioma inglês para prover um con-
junto mais rico de construções semanticamente claras, isto é realmente modelagem. O
propósito de um modelo é prover um conjunto de construções mais ricas, de mais alto
nível e mais semanticamente precisas (normalmente palavras) do que aquelas do idioma
subjacente. O modelo é desenhado para reduzir ambigüidades e inconsistências, adminis-
trar complexidade, facilitar exames quanto à perfeição e para melhorar a compreensão.
Associadas com cada modelo individual, existem técnicas que capturam o comportamen-
to (função/método) do modelo. Portanto, as técnicas descritas a seguir para a documen-
tação de comportamento são baseadas em um modelo subjacente relativamente formal.

Técnicas de Especificação de Comportamento Estático


Há pelo menos dois modos de especificar o comportamento estático4: (1) fornecendo as
condições anteriores e posteriores de sua execução e (2) decompondo o serviço em uma
série de atividades e tarefas que podem ser traduzidas em operações básicas da classe ou
como uma classe de serviço para outros objetos.

2. Se eles fossem verdadeiros, nós modelaríamos isto com comportamento dinâmico.


3. Por exemplo, de que forma o objeto proveria o serviço.
4. Isto é chamado de especificação de operações na UML.
CAP. 7 ESPECIFICANDO COMPORTAMENTO ESTÁTICO 105

Partindo-se de uma perspectiva formal da língua, o primeiro modo é o preferido.


De fato, a UML utiliza esta técnica para a especificação de operações, da qual o compor-
tamento estático é um caso especial. Entretanto, constatamos que os analistas têm uma
enorme dificuldade de reconhecer as pré-condições e pós-condições necessárias. Além do
mais, esta técnica não nos auxilia a entender o negócio e nem descobre serviços comple-
mentares não identificados em nosso processo. Portanto, preferimos fazer uma análise
dos serviços do negócio, que é o segundo modo de especificarmos comportamento estático.
Um serviço é composto por uma série de atividades que executam o trabalho anun-
ciado por esse particular serviço. As atividades discretas são, por sua vez, compostas de
tarefas. Dessa maneira, uma atividade é um conjunto organizado e com procedimentos e
tarefas para consumar um específico subobjetivo. As tarefas e atividades são interdepen-
dentes, e há um fluxo de controle bem-definido entre elas. A definição do comportamento
é a identificação das atividades e tarefas que deverão ser realizadas para suporte do ser-
viço identificado (nomeado).
Quando da captura de atividades e tarefas, as seguintes perguntas deverão ser res-
pondidas:
■ Como elas são realizadas?
■ Por que elas são realizadas?
■ Quais são seus inter-relacionamentos?
Para fins de utilização no controle de especificações e desenho, as seguintes infor-
mações complementares deverão ser reunidas:
■ Isto é parte de uma transação (se você estiver utilizando o paradigma de transação)?
■ Sincronização
■ Freqüência
■ Volume
■ Variantes
■ Regras de negócio sobre verificação e validação
■ Algoritmos de processamento
■ Dados salvos e/ou armazenados
■ Relatórios
■ Pontos de controle e de verificação, se houver
■ Detecção, correção e recuperação de erros
As etapas são muito simples. Inicie com o disparador de serviços5, documente todas
as etapas mentais e manuais que devem ser realizadas, documente todos os pontos de de-
cisão, documente cada teste e cálculo, ou mude o atributo, e documente todos os resulta-
dos possíveis partindo de um ponto de decisão. Finalmente, considere as exceções e os
casos especiais.
É apropriado tomar cuidado com os três pontos seguintes:
1. Tenha cuidado ao modelar processos manuais. O ideal por detrás do paradigma
orientado a objeto é que o objeto que detém os dados realize o trabalho. Assim,
muitos processos manuais não deverão ser executados por um objeto conceitual-
mente humano do sistema; esses tipos de processos são executados pelo próprio
objeto. Por exemplo, uma ordem de compra pode ser preenchida por ela mesma.

5. O serviço invoca algum outro objeto. O outro objeto é geralmente um objeto externo.
106 UML E C++ CAP. 7

Talvez isso possa parecer um pouco estranho para um novato, mas a ordem de
compra tem todos os dados necessários para processar a si mesma. Similarmen-
te, os cheques na maioria das aplicações bancárias poderiam basicamente pro-
cessar a si próprios.
2. Em muitas situações, apenas as atividades estão no mesmo domínio semântico
do objeto — as tarefas estão em um domínio semântico diferente. Em uma situa-
ção desse tipo, a tarefa precisa ser modelada sob a forma de serviços prestados
por outros objetos. Esses outros objetos geralmente estão relacionados com o
presente objeto, seja por meio de herança ou de agregação. Capturar cada servi-
ço no correto objeto é crítico no sentido de se ter um modelo com fraco acopla-
mento e boa coesão. Lembre-se de que fraco acoplamento e boa coesão dos
objetos tendem a originar software flexível e reutilizável.
3. As operações que participam como parte da especificação de serviço de um ob-
jeto estão vinculadas. Essas operações podem somente mudar dados detidos
pelo objeto (isto é, atributos) e unicamente podem ter acesso aos dados do objeto
e aos dados passados a ele via sua lista de argumentos. Além disso, um deter-
minado objeto pode somente acessar os serviços de outros objetos que ele reco-
nheça.6 No próximo capítulo, trataremos como um objeto sabe sobre outro
objeto.

Técnicas de Especificação de Controle


Descobrir os serviços e descrevê-los individualmente como uma seqüência de ações que
produzem o resultado tencionado é uma das questões que não foram muito bem aborda-
das na maioria dos livros ou cursos orientados a objeto. Nós tentaremos oferecer ao leitor
algumas diretrizes nesta área.
Acreditamos que, da mesma forma como tomamos emprestadas técnicas para a do-
cumentação de comportamento de outras metodologias, devemos igualmente tomar em-
prestado uma técnica de um método existente (McMenamin e Palmer, 1984) para poder
especificar o comportamento de objetos e capturar os serviços necessários a fim de satis-
fazer todos os requisitos. Este método foi adotado e modificado por Jacobson (1992) para
a captura de requisitos em análise orientada a objeto em seu método Objectory.
As etapas descritas por McMenamin e Palmer são muito simples:
1. Liste todos os eventos internos e externos para os quais a aplicação/sistema pre-
cisa responder. Os usuários e o(s) sistema(s) da interface são externos ao sistema.
Uma boa maneira de obter uma lista de eventos externos é fazer uma análise de
tarefas para os usuários e os sistemas externos. Na terminologia do método Ob-
jectory, isso seria denominado de identificação de atores.
2. Para cada evento, normalmente uma mensagem, determine de que forma e em
qual seqüência as mensagens serão passadas entre objetos de modo que satisfa-
çam o pedido. Essa série particular de interações entre objetos é denominada ce-
nário. Um cenário mostra uma execução individual da aplicação/sistema no
tempo. Quando o cenário requer seqüenciamento e sincronização específicos

6. Estas restrições são consistentes com os princípios de encapsulamento e ocultação de informações do


paradigma orientado a objeto. Fundamental ao construir sistemas de boa qualidade é o fato de que
os serviços de cada objeto devem estar no mesmo domínio semântico.
CAP. 7 ESPECIFICANDO COMPORTAMENTO ESTÁTICO 107

além do que é provido como parte dos serviços dos objetos no cenário, deverá
ser criado um objeto tarefa para capturar os controles do cenário. Um cenário é
equivalente a uma transação em muitas aplicações. Dessa maneira, um objeto tarefa
é geralmente utilizado para capturar os aspectos de controle de uma transação.
3. Capture essas seqüências desenhando um diagrama de seqüência para cada ce-
nário e suas variantes. Esses cenários, ou seqüências de mensagens, são úteis
para o entendimento da aplicação/sistema e para fins de testes integrados. Esta
etapa é diferente das etapas constantes no método de McMenamin e Palmer e
no método Objectory de Jacobson. Em métodos estruturados, geralmente uma
máquina de estado para todo o sistema é projetada para capturar os cenários; si-
milarmente, no método Objectory, um modelo de estado é utilizado para repre-
sentar o sistema. Isso faz sentido, pois ambos os métodos são utilizados na parte
inicial do sistema anteriormente à decomposição. Estamos utilizando esta técni-
ca após a análise de detalhes (análise de domínio) da aplicação/sistema para ve-
rificar a validade da análise de domínio quanto à abordagem da aplicação
específica. Isso corresponde a uma diferença crítica; cremos que usar cenários
para validar nosso modelo de domínio é um modo mais apropriado para desen-
volver classes reutilizáveis do que iniciar o processo de modelagem para a apli-
cação, conforme sugerido por McMenamin e Palmer.
4. Capture os detalhes da forma como cada serviço é provido. Ao pensar sobre
cada cenário, o analista/desenvolvedor será capaz de especificar precisamente o
que deverá ser feito em cada um dos serviços do objeto. A seguir, os detalhes
das ações que precisam ser executadas dentro de um serviço são capturados.
Novamente, isto é diferente do método clássico, que teria adotado uma visão do
sistema. Estamos agora adotando uma visão decomposta do sistema em termos
de objetos, e não de funções.
5. Caso você ainda não tenha procedido dessa maneira, considere modelar os ce-
nários como casos de uso para reduzir o número de diagramas de cenário.

Técnicas de Documentação de Controle


Diagramas de Atividade7
Esta técnica utiliza processos ou tarefas como seu bloco de construção. A resposta é cap-
turada como uma série de processos que operam em um conjunto de dados. Partindo de
uma perspectiva técnica, pode-se argumentar que as atividades e tarefas são capturadas
como “processos” (ou seja, “bolhas” no diagrama) em diagramas de nível mais baixo. Se
estivéssemos praticando decomposição funcional, essa abordagem seria correta. Entretan-
to, na análise orientada a objeto, nem sempre é correto mapear cada atividade ou tarefa

7. Um diagrama de atividade tem sempre sido um item confuso. Ele foi originalmente destinado a
capturar os processos em um nível mais alto. Foi também utilizado em um nível de caso de pré-uso
ou uso compartilhado. Atividade não é OO, exatamente como os casos de uso. Provavelmente é
melhor introduzir atividade como um meio de documentar o processo no “domínio”/nível de
negócio. Em seguida, as atividades ou processos dentro dos quais o “sistema será utilizado”
precisarão ser orientados a objeto. Na verdade, não há necessidade de que tudo seja OO. O que
precisa ser OO é o sistema e as partes interligadas a ele.
108 UML E C++ CAP. 7

para um serviço de um objeto.8 De modo geral, nós traduziremos uma tarefa em relação
a um objeto.
Um diagrama de atividade descreve como é feita a coordenação de atividades. Al-
guns autores consideram que os diagramas de atividade são um melhor mecanismo para
mostrar dependências essenciais entre atividades realizadas por várias entidades.
A notação da UML para um diagrama de atividade está ilustrada na Figura 7.1. O
círculo sólido representa o ponto de entrada. O diamante representa pontos de decisão.
Os retângulos com bordas arredondadas identificam tarefas que estão prestes a serem
realizadas. As setas identificam transições de uma tarefa para outra. As linhas sólidas são
barras de sincronização que identificam sincronização para términos de tarefas (fluxos
entrando na barra) e inícios de tarefas (fluxos saindo da barra). As atividades realizadas
por diferentes indivíduos podem ser expressas utilizando-se partições denominadas
swimlanes.

FIGURA 7.1 Notação da UML para um diagrama genérico de atividades.

Diagrama de Colaboração99
Um diagrama de colaboração mostra o fluxo de controle enfatizando os relacionamentos
estruturais entre as instâncias na interação, bem como as mensagens transmitidas entre
elas. Ele mostra os objetos e vínculos existentes exatamente antes de o serviço iniciar, bem

8. Mapear uma tarefa a um objeto é uma solução técnica. Uma abordagem mais correta seria fazer uma
reengenharia no processo de modo que ele pudesse ser traduzido para o serviço de um objeto
individual.
9. O termo em UML é diagrama de colaboração, mas anteriormente os artigos de OO utilizavam outros
termos. Muitas das fontes mais antigas referem-se a este diagrama como um diagrama de interação
entre objetos.
CAP. 7 ESPECIFICANDO COMPORTAMENTO ESTÁTICO 109

como os objetos e vínculos criados (e possivelmente destruídos) durante a realização do


serviço.10
Uma mensagem de um objeto a outro é indicada por um rótulo consistindo de um
número de seqüência, o nome do serviço requisitado e a lista de argumentos com uma
seta mostrando o sentido do fluxo de mensagens. Na UML, existem mais opções para
controle da sincronização, com outros encadeamentos de controle, seqüências aninhadas
de procedimentos de chamada, encadeamentos concorrentes, interações e expressões con-
dicionais possíveis. Na Figura 7.2, é apresentado um diagrama de colaboração.

FIGURA 7.2 Notação da UML para um diagrama de colaboração.

Diagrama de Seqüência11
Um diagrama de seqüência mostra o fluxo de controle por ordenamento de tempo e en-
fatiza a passagem de mensagens à medida que elas vão se desenrolando durante o tempo.
Ele revela a seqüência de mensagens que implementam um serviço ou transação. Esta
técnica utiliza objetos como bloco de construção. Nessa análise técnica, a resposta de um
evento é rastreada como uma série de mensagens trocadas entre objetos. Cada mensagem
é uma solicitação por serviços de um outro objeto. Quando se utiliza esta técnica, deve-se
sempre ter alguma idéia sobre os objetos que estão no sistema. Essa é uma das razões por
que a identificação de objetos é a primeira etapa de nosso método orientado a objeto.
A notação da UML para diagramas de seqüência está ilustrada na Figura 7.3. Uma
linha reta vertical é utilizada para representar um objeto. Um evento (solicitação externa
de serviço) é utilizado para rotular o diagrama. Uma chamada de serviço a um outro ob-
jeto é representada por uma seta direta com o protótipo da chamada de serviço como
seu rótulo.

10. O diagrama de colaboração é muito semelhante ao diagrama de seqüência. Entretanto, ele captura
informação adicional sobre como os objetos estão mutuamente relacionados. A incorporação de
detalhes extras é uma coisa boa, mas é difícil de ser seguida. Apesar de preferirmos o uso de
diagramas de colaboração, é muito freqüente o caso em que as pessoas não os lêem tão
cuidadosamente como eles merecem.
11. Os diagramas de seqüência foram previamente conhecidos como diagramas de traços de eventos, e
tiveram origens em metodologias orientadas a objeto anteriores à UML.
110 UML E C++ CAP. 7

FIGURA 7.3 Notação da UML para um diagrama de seqüência.

Técnicas de Documentação de Comportamento Estático


Pré e Pós-condições
Nas especificações da UML, o modo recomendado de especificar pré e pós-condições é
por meio da especificação textual. A especificação textual teria as seguintes seções: nome
do serviço, inputs, output(s), elementos do objeto modificado, pré-condições e pós-condi-
ções. Recomendamos acrescentar condições invariantes à relação, caso seja feita a opção
por esta técnica.

Confecção de Diagramas de Fluxo


Um diagrama de fluxo é a técnica mais antiga de especificar um método/função. Existem
muitos métodos orientados a objeto que utilizam esta técnica e que têm notações altamen-
te especializadas para a documentação do comportamento.
Uma técnica genérica de confecção de diagramas de fluxo é descrita. Utiliza-se um
retângulo para capturar os cálculos ou operações, e o símbolo do diamante é utilizado
para capturar a decisão. O fluxo de controle é exibido por meio de setas. Palavras são co-
locadas nas setas que partem de um diamante (ponto de decisão), pois cada seta que par-
te do diamante representa um dos possíveis resultados.
CAP. 7 ESPECIFICANDO COMPORTAMENTO ESTÁTICO 111

Diagramas de Fluxo de Dados


Os diagramas de fluxo de dados (DFDs — data flow diagrams) são parte integral de vários
métodos (por exemplo, o método de Rumbaugh). Cada método em particular utiliza uma
notação ligeiramente diferente. É descrita uma notação genérica. O fluxo de dados (ou
seja, informações) é representado por uma seta rotulada. Operações (ações ou transfor-
mações) são representadas por “bolhas” rotuladas. As fontes de informações ou sorve-
douros são representados por retângulos rotulados. Atributos (informações armazenadas)
são representados por uma linha horizontal dupla. As palavras-chave and e or são utili-
zadas para vincular fluxos de dados.

Linguagem Estruturada
A linguagem estruturada é também utilizada com alguns métodos. Este método é ampla-
mente usado por engenheiros de sistemas, que não apresentam qualquer treinamento for-
mal em computadores.
As diretrizes genéricas recomendadas para a utilização de linguagem estruturada
são as seguintes:
1. Utilize verbos de comando (imperativos) para descrever operações, transforma-
ções ou ações.
2. Utilize nomes de atributos para dados que devam ser manipulados.
3. Utilize preposições e conjunções para mostrar relacionamentos lógicos.
4. Poderão ser utilizados termos matemáticos, físicos, de negócios e técnicos de
compreensão comum (semanticamente precisos).
5. Equações matemáticas, bem como ilustrações tais como tabelas, diagramas e
gráficos, também poderão ser utilizadas para fins de clareza.
6. Palavras distintas daquelas que acabaram de ser listadas deverão ser utilizadas
de maneira econômica, e somente para contribuir na documentação do compor-
tamento.
7. Estruturas de parágrafos ou sentenças devem ser simples, com construções de
entrada e saída individuais. Essas construções deverão consistir somente do se-
guinte:
■ Seqüência: ações que ocorrem em uma determinada seqüência de tempo.
■ Concorrência: mais de uma ação ocorrendo simultaneamente.
■ Decisão: uma ramificação no fluxo de ações é feita com base nos resultados
de um teste.
■ Repetição: a(s) mesma(s) ação(ões) é (são) repetida(s) até ser atingido algum
limite ou resultado especificado.
São apresentados alguns exemplos na Figura 7.4. As palavras que aparecem em
maiúsculo são atributos (ou seja, dados) e as linhas indentadas representam sujeição.
112 UML E C++ CAP. 7

1. Exemplo de Seqüência
Find JUROS DEVIDOS como
TAXA x PERÍODO DE INSTALAÇÃO x PRINCIPAL.
Next, subtract JUROS DEVIDOS de SALDO DE CONTA.
Next, issue BALANÇO REMANESCENTE como SALDO DE CONTA.
2. Exemplo de Concorrência
Calculate DISTÂNCIA DE APOIO À NAVEGAÇÃO como distância do grande círculo de
POSIÇÃO DA AERONAVE à POSIÇÃO DE APOIO À NAVEGAÇÃO.
Calculate DIFERENÇA NA ALTURA como
ALTITUDE DA AERONAVE — ALTITUDE DE APOIO À NAVEGAÇÃO.
3. Exemplo de Decisão
If QUANTIA SOLICITADA for maior que LIMITE,
then:
return “NÃO APROVADO”
else:
subtract QUANTIA SOLICITADA de LIMITE.
Next, return “APROVADO”
endif:
4. Exemplo de Repetição
Para cada membro de Contas especiais:
peça para cada conta mudar sua TAXADEJUROS a NOVATAXADEJUROS
endfor:

FIGURA 7.4 Exemplos de linguagem estruturada.

Enfoque Recomendado
Muito embora a utilização de diagramas de fluxo de processos possa ser mais familiar
para a maioria de nós, não recomendamos esta técnica. Quando esses diagramas são uti-
lizados, os analistas e desenvolvedores ficam propensos a traduzir atividades em serviços
de objetos e usam a análise como especificações de serviços. Isso constitui uma prática
muito ruim porque muitas de nossas atividades violam os princípios de encapsulamento
e ocultação de informações da tecnologia orientada a objeto. Quando esses princípios são
violados, é criado um sistema com um nível muito alto de acoplamento e de fraca coesão.
Um sistema de objetos com acoplamento muito alto e fraca coesão indica que o software
tem uma manutenção mais difícil e é menos flexível. Pensamos que o uso inadequado de
diagramas de fluxo de processos causará o desenvolvimento de software sem recursos.
Recomendamos a utilização do método existente de McMenamin e Palmer para a
especificação do comportamento de objetos. As etapas são muito simples:
1. Liste todos os eventos internos e externos para os quais a aplicação/sistema pre-
cisa responder.
2. Para cada evento, normalmente uma mensagem, determine de que forma e em
qual seqüência as mensagens serão passadas entre objetos de modo que satisfa-
çam o pedido.
3. Capture essas seqüências desenhando um diagrama de seqüência para cada ce-
nário e suas variantes. Esses cenários, ou seqüências de mensagens, são úteis
para o entendimento da aplicação/sistema e para fins de testes integrados.
CAP. 7 ESPECIFICANDO COMPORTAMENTO ESTÁTICO 113

4. (Acrescentado pelos autores) Caso você ainda não tenha criado um modelo de
caso de uso, crie um modelo deste tipo para os cenários. Isso quase sempre é ne-
cessário para sistemas de maior porte, pois de outra forma o número de cenários
ou diagramas de seqüência seria um tanto assustador.
5. Capture os detalhes da “forma como cada serviço é provido”. Ao pensar sobre
cada cenário, o analista/desenvolvedor será capaz de especificar precisamente o
que deverá ser feito em cada um dos serviços do objeto. Então, os detalhes das
ações que precisam ser executadas dentro de um serviço são capturados utili-
zando-se linguagem estruturada.12

Exemplo
Com base na análise anterior, temos os seguintes objetos atrelados a um serviço de cor-
teDeGrama: John, Peter, Paul, Elizabeth, Mary, Jack e CortadorDeGramaProfissional.
Lembre-se de que estamos supondo que o gramado da família já tenha uma mudança,
ou tenha estabelecido o serviço alturaDoGramado, pelo fato de ele ter um algoritmo sim-
ples. A definição do serviço de corteDeGrama para cada objeto é mostrada na Figura 7.5
utilizando-se linguagem estruturada.
Para que este modelo funcione, necessitaremos de um objeto externo (Cron job) que,
periodicamente (ou seja, a cada 15 minutos), pedirá a cada objeto, inclusive John, para
que inicie o serviço/tarefa que se encontra em sua programação. Isto é um evento externo
muito comum; muitos serviços são solicitados com base em uma programação predeter-
minada. Suponhamos que o serviço corteDeGrama de John tenha programado o corte de
grama propriamente dito (serviço “cortarGramadoDaFamília”) para ocorrer às 19h00
quando John voltar para casa. Assim que chegar em casa, ele imediatamente realizará o
serviço “cortarGramadoDaFamília”. A Figura 7.5 define esse serviço.
Nós não refinamos o serviço corteDeGrama propriamente dito. Assumimos ainda
que John tenha um modo de contatar todos os filhos. Revelaremos como o contato com
os filhos pode ser consumado em um capítulo posterior.

12. Muito embora haja vantagens e desvantagens em todas as três técnicas de documentação de
comportamento, recomendamos a utilização de linguagem estruturada pelas seguintes razões: (1)
Em muitas organizações a análise é realizada por cientistas e programadores de áreas distintas da
computação, e, para eles, a linguagem estruturada é mais fácil de entender do que diagramas de
fluxo de dados ou diagramas de fluxo. (2) Os diagramas de fluxo e os DFDs deveriam ser utilizados
com uma ferramenta CASE (computer aided software engineering). Infelizmente, a maioria das
ferramentas CASE atuais não provê suporte satisfatório de ponta a ponta. (3) Além do mais, hoje em
dia, as ferramentas CASE para métodos orientados a objeto não são fáceis de usar nem são
produtivas da forma como realmente fazemos análise e desenho. (4) Muitos de nós ainda assumem
que, ao utilizarmos DFDs ou diagramas de fluxo, podemos voltar à decomposição funcional. Ao
assim procedermos, anulamos os benefícios de irmos em direção à tecnologia orientada a objeto.
Novamente, a maioria das ferramentas CASE não tem aplicado as restrições semânticas
complementares de DFDs e diagramas de fluxo realmente sugeridas quando se utilizam estas
mesmas técnicas para documentar comportamentos de objetos (classes).
114 UML E C++ CAP. 7

NOME DO OBJETO John

SERVIÇO: “cortarGramadoDaFamília (sem argumentos)”

if MINHA CONDIÇÃO for igual a “cansado”,

then:

for each filho em Filho selecionado

pedir a cada filho para “cortar a grama” por US$ 5.

if resposta é “sim”,

then:

remover “cortarGramadoDaFamília” de PROGRAMAÇÃO.

return;

else:

endif:

endfor:

realizar corte do gramado.

else:

realizar corte do gramado.

endif:

FIGURA 7.5 Provisões de serviço de John para o corte do gramado.

Agora, examinemos o serviço corteDeGrama para os filhos. Todos os filhos, com ex-
ceção de Peter, delegarão qualquer solicitação de corte de gramado que retribua mais do
que cinco dólares a Jack, que aparará e cortará qualquer gramado por quatro dólares. O
serviço deles é definido na Figura 7.6. Peter sempre corta o gramado, independentemente
do pagamento. Seu serviço é definido na Figura 7.7.
Os filhos têm um serviço de corteDeGrama em vez de um serviço cortarGramado-
DaFamília, pois eles cortarão qualquer gramado. Por causa dessa flexibilidade, necessita-
mos associar um endereço do gramado a esse serviço. E mais, para que eles possam ter
acesso a Jack, supõe-se que tenham acesso ao número do telefone dele por meio da lista
telefônica. Em capítulo posterior, discutiremos os vários veículos por meio dos quais um
objeto tem acesso aos serviços de outro objeto. A definição do serviço de corteDeGrama
para Jack e para o CortadorDeGramaProfissional são deixados como exercício ao leitor.
CAP. 7 ESPECIFICANDO COMPORTAMENTO ESTÁTICO 115

NOMES DE OBJETOS Paul, Elizabeth, Mary

SERVIÇO: “cortar a grama (ENDEREÇO_DO_GRAMADO, QUANTIA_EM_DINHEIRO)”

if QUANTIA_EM_DINHEIRO for inferior a US$ 5,

then:

colocar PROGRAMAÇÃO para o período noturno (19h00 21h00)

Next, se PROGRAMAÇÃO tiver endereço de memória aberto,

then:

colocar corteDeGrama em endereço de memória.

associar ENDEREÇO_DO_GRAMADO com corteDeGrama.

return “sim, eu cortarei a grama nesta noite”.

else:

return “não, eu não posso cortar o gramado”.

endif:

else:

obter o NÚMERO DO TELEFONE de Jack da Lista Telefônica

pedir para Jack cortar o gramado (ENDEREÇO_DO_GRAMADO, self, ENDEREÇO)

se a resposta de Jack for “sim”,

then:

retornar “Sim, eu cortarei a grama nesta noite”.

else:

retornar “Não, eu não posso cortar o gramado”.

endif:

endif:

FIGURA 7.6 Cartão CRC de Paul, Elizabeth e Mary para o exemplo do corte do gramado.
116 UML E C++ CAP. 7

NOME DO OBJETO Peter

SERVIÇO: “cortar a grama


(ENDEREÇO_DO_GRAMADO, QUANTIA_EM_DINHEIRO)”

colocar PROGRAMAÇÃO para o período noturno (19h00 21h00)

Next, se PROGRAMAÇÃO tiver endereço de memória aberto,

then:

colocar corteDeGrama em endereço de memória.

associar ENDEREÇO_DO_GRAMADO com corteDeGrama.

retornar “Sim, eu cortarei a grama nesta noite”.

else:

retornar “Não, eu não posso cortar o gramado”.

endif:

FIGURA 7.7 Cartão CRC de Peter para o exemplo do corte do gramado.

Se pensarmos sobre quando uma pessoa realiza um serviço, que é uma seqüência
de ações, rapidamente concluiremos que a maioria de nossos comportamentos tem rela-
ção com o tempo ou com o tempo decorrido. Por exemplo, acordamos depois de termos
dormido por x números de horas, dormimos às y horas, e fazemos nossas refeições em
uma hora determinada. Portanto, o único evento externo nessa aplicação é o tempo de-
corrido. Um modelo razoável dessa situação do “mundo real” é assumir que ele somente
precisa ser modelado em intervalos de 15 minutos, de forma que uma pessoa possa pro-
gramar sua vida em intervalos de 15 minutos e sem um nível de precisão mais acurado.
Então, o diagrama de seqüência de alto nível poderá se assemelhar àquele apresentado
na Figura 7.8.
O Programador é um novo objeto: seu único objetivo é solicitar a um objeto que uti-
lize programação para realizar uma tarefa requerida baseada em qual é o horário neste
momento. Cada objeto opera como se pudesse executar algum tipo de serviço (função)
de 15 em 15 minutos. Se um dos objetos estivesse programado para “cortarGrama” às
19h00, o programador enviaria uma mensagem a esse objeto para que ele executasse seu
callSchedJ com o argumento de 19h00. Em cada objeto, o método callSchedJ é o mesmo;
ele verifica sua programação para ver que serviço (função) necessita ser realizado. Neste
caso, seria o serviço “cortarGrama”.
CAP. 7 ESPECIFICANDO COMPORTAMENTO ESTÁTICO 117

FIGURA 7.8 Diagrama de seqüência de Programador (Scheduler) para o exemplo do corte do


gramado.

■■ RESUMO
A seguir se encontram as etapas em detalhes de nosso enfoque para a especificação de
comportamento estático e para que novos serviços sejam identificados:
1. Liste todos os eventos internos e externos para os quais a aplicação/sistema pre-
cisa responder.
2. Para cada evento, normalmente uma mensagem, determine de que forma e em
qual seqüência as mensagens serão passadas entre objetos de modo que satisfa-
çam o pedido.
3. Defina e desenhe um diagrama de seqüência para cada cenário e suas variantes.
4. Se o número de cenários for elevado (e você ainda não tiver utilizado casos de
uso), considere a criação de um modelo de caso de uso para reduzir o número
de diagramas.
5. Capture os detalhes de cada serviço associado ao diagrama de seqüência pela
utilização de uma das técnicas de documentação apresentadas neste capítulo.
118 UML E C++ CAP. 7
Comportamento Dinâmico
COMPORTAMENTO DINÂMICO
8.Comportamento Dinâmico

A ação é algo transitório — uma passada, um sopro; o movimento de um


músculo, desta ou de outra forma.

William Wordsworth, The Borders

E ste capítulo apresenta técnicas para a captura de comportamento dinâmico. Para apli-
cações não baseadas em controle, a maioria dos objetos (ou seja, classes) não sofrerá
mudanças significativas de estado, de modo que somente bem poucas (se houver algu-
ma) irão requerer diagramas de estado. Entretanto, em muitas aplicações baseadas em
controle, os diagramas de estado poderão ser o aspecto dominante do modelo.
Tecnicamente, os diagramas de estado são as especificações formais do comporta-
mento de uma classe e, por conseguinte, de um objeto. O comportamento estático é na
verdade um caso especial de um diagrama de estado isento de modelagem. Um diagrama
desse tipo ocorre quando um objeto (classe) sempre responde da mesma forma a eventos
(estímulos) externos e internos. Note que cenários não são diagramas de estado, e sim
exemplos de execução do sistema. Eles normalmente envolvem diversos objetos desem-
penhando vários papéis. Dessa maneira, eles são instâncias de comportamento, e como
tal somente podem ilustrar comportamento; não podendo defini-lo. Sob o ponto de vista
técnico, quando todos os diagramas de estado tiverem sido criados, todos os cenários po-
derão ser derivados deste conjunto completo de diagramas de estado. De fato, muitos de-
senvolvedores empregam cenários para verificar o equilíbrio do modelo. Similarmente,
em virtude de que um cenário é uma instância de um caso de uso, um caso de uso é uma
“porção” do comportamento do sistema ao longo de diagramas de estado oriundos de
múltiplas classes.

119
120 UML E C++ CAP. 8

Introdução
A análise orientada a objeto é freqüentemente descrita em termos de estrutura, compor-
tamento e regras. A análise estrutural captura a visão estática de como os objetos estão
relacionados uns com os outros; ela captura essencialmente a semântica de dados da apli-
cação. Uma metáfora espacial e visual é utilizada para documentar esses relacionamen-
tos. Em todos os capítulos anteriores, focamos a captura dos aspectos estruturais da
aplicação. Em contraste, a análise comportamental captura os aspectos dependentes do
tempo da aplicação. Por exemplo, ela é utilizada para especificar como contratar funcioná-
rios, demiti-los, afixar diagramas a um documento ou eliminar uma palavra de um docu-
mento. Portanto, quer o comportamento seja estático ou dinâmico, a descrição do método
captura a semântica procedural da aplicação no decorrer do tempo. As regras, que serão dis-
cutidas em um capítulo posterior, capturam a semântica declarativa da aplicação.
Se a aplicação não tiver qualquer comportamento dependente do tempo1, capturar
os aspectos estruturais do sistema e desenvolver análise comportamental estática serão
suficientes para a construção da aplicação. Essa é a situação de nosso estudo de caso até
o momento. Entretanto, o mundo em que vivemos não é estático; ele muda com o tempo.
Mecanismos dinâmicos de modelagem dotam-nos de um modo de capturar o comporta-
mento de objetos e, portanto, o comportamento da aplicação no decorrer do tempo.
Os relacionamentos temporais são difíceis de serem capturados. A maioria das apli-
cações é melhor entendida quando se examina inicialmente sua estrutura estática (semân-
tica de dados); isto é, a estrutura de seus objetos e de seus relacionamentos (herança,
associação, agregação) mútuos em um dado instante de tempo. Após a captura desse as-
pecto da aplicação, necessitamos examinar as mudanças nos objetos e em seus relaciona-
mentos com o tempo. Esses aspectos (que são partes da semântica procedural) de uma
aplicação, que estão envolvidos com estas mudanças no decorrer do tempo, são captura-
dos no modelo dinâmico. Assim, este capítulo apresenta mecanismos que nos ajudam a
capturar o fluxo de controle, interações e seqüência de operações, em uma aplicação
orientada a objeto. Os conceitos mais importantes da modelagem dinâmica são os eventos,
que são estímulos, e os estados, que são configurações de objetos existentes entre eventos.
Dessa maneira, uma aplicação pode ser descrita em termos de comportamentos de obje-
tos; ou seja, uma seqüência organizada de mudanças de estado de objetos no decorrer do
tempo, e o comportamento de um objeto é capturado como uma cadeia (ou, provavel-
mente, uma rede) de causa (estímulo) e efeito (mudança de estado) no decorrer do tempo.
Por exemplo, a cadeia de causa e efeito para um objeto Pedido em um sistema de
processamento de pedidos pode ser o seguinte: na colocação do pedido — estado de re-
quisição; ao atender um item de linha do pedido — estado de parcialmente atendido; ao
remeter esse item de linha — estado de parcialmente remetido; e, quando todos os itens
de linha tiverem sido cumpridos e remetidos — estado de remetido. Assim que o pedido
é remetido, um objeto Fatura é criado e os dados do objeto Pedido são arquivados; com
o objeto sendo eliminado do sistema.2 A Figura 8.1 mostra um diagrama em cadeia de-
notando este fato.

1. O comportamento que está sendo utilizado não é de fato dependente do tempo; ele é na verdade
dependente da história passada do sistema.
2. O arquivamento de dados associados ao objeto Pedido possibilitará que se reconstrua este objeto no
evento em que o cliente contestar a fatura.
CAP. 8 COMPORTAMENTO DINÂMICO 121

FIGURA 8.1 Cadeia de causa e efeito para um objeto Pedido.

Técnicas de Identificação de Comportamento Dinâmico


Um objeto no mundo real normalmente tem uma existência (tempo de vida). De modo
geral, ele é criado ou adquire existência, avança ao longo de certos estágios e em seguida
perde a existência ou desaparece. Por exemplo, um ser humano é concebido por seus
pais, desenvolve-se por certos estágios (bebê, criança, aluno de pré-escola, estudante de
primeiro grau, adolescente, jovem, adulto, de meia idade, ancião) e finalmente morre. Al-
guns indivíduos não visitam todos os estágios; e outros parecem que retornaram a um
estágio anterior. Entretanto, todos os seres humanos seguem este modelo básico de com-
portamento dinâmico ao longo de suas vidas.
Partindo de nossas observações gerais sobre padrões de comportamento de diferen-
tes coisas do mundo real, concluímos o seguinte:
■ A maioria das coisas passa por vários estágios durante suas existências.
■ A ordem com que uma coisa progride pelos estágios forma um padrão que colabora
na classificação do tipo de coisa que ela é.
■ Em um padrão, nem todas as progressões entre estágios são permitidas. Algumas
progressões são proibidas pelas leis da física, outras pelo estatuto etc.
■ Existem incidentes/eventos no mundo real que fazem uma coisa progredir (ou in-
dicam que ela tem progredido) entre estágios.
Por razões técnicas, nós também acrescentamos as seguintes hipóteses:
■ Uma coisa está em exatamente um e único estágio de seu padrão de comportamento
em um determinado instante de tempo.
■ As coisas progridem, instantaneamente, de um estágio para outro.
Note que o grau de pormenor com que é tratado o tempo depende do grau de abs-
tração e pode variar nos diferentes níveis da aplicação. Basicamente, entretanto, a pro-
gressão (transição) deve ser tratada como atômica, ou seja, como não-interruptível no
nível de abstração dado. Dessa maneira, o evento ou incidente que provoca a progressão
deve ser tratado como atômico, pois um incidente ocorre em um ponto no tempo enquan-
to um estágio (estado) é uma configuração de objetos existente entre incidentes.
Permita-nos testar essas observações e hipóteses em um segundo exemplo. A Figura
8.2 apresenta o padrão de comportamento para um avião. Nesse exemplo, o avião passa
por numerosos estágios, que são mostrados pelo texto em negrito. Eles incluem: estacio-
nado no portão, taxiando até a pista e assim por diante. O padrão é simples e está repre-
sentado na figura pela utilização de setas. O padrão de progressão pelos estágios
aplica-se a todas as instâncias de avião. Note que nem todas as progressões entre estágios
são permitidas, pois as leis da física impedem que o avião possa progredir entre o estágio
de estacionado no portão e a etapa de vôo. Existem incidentes/eventos que sinalizam a
progressão entre estados. Esses incidentes/eventos são representados por rótulos sobre
as setas. Por exemplo, quando o avião estiver no estágio “decolando”, o rótulo “rodas
122 UML E C++ CAP. 8

deixando o solo” sinalizará a progressão do estágio “decolando” no estágio “em vôo”. As


rodas deixam o solo instantaneamente e, em um dado instante de tempo, podemos supor
que o avião se encontre em um dos estágios apresentados.

FIGURA 8.2 Padrão de comportamento para um avião.

É muito importante notar que os estágios são definidos pela nossa percepção (mo-
delo) da realidade, e que alguns dos incidentes/eventos são de fato indicadores da pro-
gressão (mudança de estado) de um estágio para outro. Veremos mais adiante que as
progressões (mudanças de estado) são utilizadas como um veículo que gera a ocorrência
de uma ação (execução de código) durante a progressão “instantânea” de um estágio para
outro. Portanto, é necessário que se definam estados pelo fato de que a coisa talvez ne-
cessite atuar quando ocorrer o incidente/evento. Por exemplo, no caso do avião, quando
as “rodas deixam o solo”, elas precisam ser novamente suspensas para dentro do avião.
Da mesma forma, quando o avião “aterrissa”, os freios são aplicados e normalmente os
motores são forçados a entrar em reverso.

Formas Comuns de Ciclo de Vida


O padrão que caracteriza uma Classe recebe o nome de forma de ciclo de vida. Muito em-
bora seja possível a existência de qualquer padrão/forma, aparentemente duas formas
dominam a modelagem de aplicações voltadas a computadores na atualidade. Sally
Shlaer e Stephen Mellor (1988) atribuíram nomes a estes padrões. Eles são os seguintes:
■ Ciclo de vida circular
O ciclo de vida circular é geralmente aplicado quando o objeto/classe tem um ciclo
operacional para seu comportamento.
Exemplos incluem o avião, um forno de microondas, uma perfuratriz motora (robó-
tica).
■ Ciclo de vida de nascimento-e-morte
Quando uma instância é criada e eliminada (ou permanece em um estágio final) du-
rante a vida do sistema que se está examinando, a classe é uma boa candidata para
esta forma. Exemplos compreendem o homem na história da humanidade, uma
CAP. 8 COMPORTAMENTO DINÂMICO 123

conta em um sistema bancário, um registro de entrada em um sistema e um doce


no decorrer de alguns dias.

Modelos de Captura de Ciclo de Vida


Historicamente, os modelos de estado foram utilizados com métodos estruturados para
revelar como um sistema se comporta quando recebe eventos externos oriundos de obje-
tos externos a ele. Uma das deficiências desta técnica era que nós aglomerávamos o sis-
tema em um grande objeto e o número de estados que era necessário “explodir”. Hoje,
atribuímos um modelo de estado (máquina) por objeto (ou seja, o modelo de estado é
parte da definição da classe de um objeto). Isso pode reduzir o problema de “explosão”
de estados e designar o estado com os objetos apropriados que exibem o comportamento
dinâmico do sistema. Isto também torna a administração da complexidade do comporta-
mento dinâmico muito mais fácil de ser mantida e muito mais flexível.
Um modelo de estado estabelece relacionamentos entre estados de um objeto. Os even-
tos estabelecem transições entre estados como resultado de eventos, ações e atividades reali-
zadas pelo objeto.
■ Modelo de Estado
Seqüência de estados que um objeto percorre durante sua vida em resposta a even-
tos. Inclui igualmente as respostas aos eventos.
■ Estado3
Representa um estágio no ciclo de vida de um objeto típico. Um estado é, tecnica-
mente, um período de tempo durante o qual um objeto está esperando pela ocor-
rência de um evento.
■ Evento
Um evento é uma condição que pode ser detectada pelo objeto. Eventos podem cau-
sar uma transição para um outro estado e/ou podem provocar o desencadeamento
de uma ou mais ações. Tecnicamente, um evento é uma ocorrência em um ponto no
tempo onde o nível de pormenor do tempo depende do grau de abstração. Muito
embora esse nível de pormenor de tempo possa variar nos diferentes níveis da mes-
ma aplicação, um evento pode ser atômico (ou seja, não-interruptível) no nível de
abstração dado. Um evento é uma transmissão4 assíncrona de informações, de uma
via, de um objeto para outro. Ela poderá ter parâmetros com nomes e tipos como
parte da mensagem enviada.
■ Transição
Uma transição é uma resposta de um objeto a um evento que ele tenha recebido. A
resposta origina uma mudança no objeto, que pode constituir uma mudança de es-

3. O estado interno total de um objeto é a combinação dos valores de dados dos atributos dentro desse
objeto. Isso poderá levar a milhares ou milhões de estados, de forma que o estado que selecionamos
para descrever em um modelo de estado depende de um grupamento de valores de dados e faixas,
ou depende do grupo de operações permitidas (baseadas em valores de atributos) do objeto durante
diferentes partes de seus ciclos de vida. Portanto, um estado é uma abstração dos valores de
atributos de um objeto.
4. O fluxo de informações de duas vias (ou seja, chamada e retorno) pode ser sempre modelado como
fluxos de informações de uma via.
124 UML E C++ CAP. 8

tado.5 O mecanismo para identificar se ocorre uma mudança de estado é uma con-
dição de guarda6. Uma condição de guarda é uma expressão booleana em termos de
parâmetros de eventos e de variáveis de estado e funções do objeto a que pertence
o diagrama de estado. Quando a transição é disparada por um evento, o valor da
condição de guarda é avaliado. Se o valor for considerado como verdadeiro, a tran-
sição ocorre; de outra maneira, ela não ocorre. Nem todas as transições têm associa-
da uma condição de guarda7.
■ Ação
Uma ação é uma atividade ou operação feita dentro de um estado ou em uma tran-
sição. Uma ação é atômica e instantânea; ou seja, ela não é interruptível no nível de
abstração do estado associado. Uma ação poderá fixar ou modificar um dos mem-
bros de dados do objeto, desencadear um evento em outro objeto, executar uma das
operações no objeto ou invocar uma das operações públicas de outro objeto. Uma
ação pode ocorrer durante uma transição, ou em uma entrada em um estado, du-
rante todo o período em que um objeto está em um estado, em uma saída de um
estado, ou na chegada de um evento que não provoque uma transição de estado.
■ Atividade
■ Uma atividade é uma operação ou conjunto de operações que está executando du-
rante todo o período em que um objeto está em um estado. Uma atividade não é
atômica e poderá ser interrompida por um evento enquanto ela estiver executando.
Quatro formas de modelos de estado são amplamente utilizadas em análise: (1)
Mealy, (2) Moore, (3) Harel e (4) Harel modificado. O modelo de estado da UML é basea-
do no modelo modificado de Harel. Esses modelos diferem com relação ao ponto em que
as ações são posicionadas no modelo. No modelo de Mealy, uma ação é executada quan-
do está ocorrendo uma transição. No modelo de Moore, uma ação é executada quando
um objeto entra em um estado. No modelo de Harel, uma ação é executada quando uma
transição está ocorrendo, porém, ele acrescenta subestados e outras construções impor-
tantes. No modelo modificado de Harel, uma ação pode ser executada quando uma tran-
sição está ocorrendo, quando um objeto entra em um estado e quando ele sai de um
estado. A UML admite ainda a ocorrência de atividades enquanto um objeto se encontra
em um estado. Por conseguinte, a UML representa um caso generalizado e pode englobar
todos os modelos aceitos de estado.8 Os modelos de estado, de maneira geral, são docu-
mentados graficamente pela utilização de diagramas de transição de estado, conforme mos-
trado mais adiante neste capítulo.
Na UML, são adicionadas três novas construções: estado de história, atividade e marca
de tempo. Um estado de história é utilizado para capturar o conceito que um estado deve
“lembrar-se” de seu subestado quando ele sai e ser capaz de entrar no mesmo subestado

5. Lembre-se de que podemos selecionar nossos estados para refletir faixas em atributos em vez de em
valores individuais de dados para limitar o número de estados. Daí, uma mudança nos atributos
talvez não seja suficiente para ocasionar uma mudança no estado.
6. As condições de guarda em transações correspondem a uma notação da UML. Historicamente, essas
condições em modelos de máquina de estado eram chamadas de regras de transição.
7. Algumas condições de guarda são simplesmente o valor booleano verdadeiro. Isso é um enfoque
muito útil quando uma ferramenta requer uma condição de guarda.
8. Isto possibilita às pessoas familiarizadas com os modelos de Mealy, Moore ou Harel capturarem seus
modelos de estado com o uso da UML.
CAP. 8 COMPORTAMENTO DINÂMICO 125

em uma reentrada subseqüente nesse estado. Uma atividade é uma operação ou conjunto
de operações dentro de um estado que leva tempo para se completar. Portanto, ela não é
instantânea e pode ser interrompida. Algumas atividades continuam até serem termina-
das por um evento externo (normalmente uma mudança de estado), e outras terminam
espontaneamente. Uma construção de marca de tempo é utilizada para capturar restrições
em tempo real na transição. O uso mais comum de uma marca de tempo é o de capturar
os limites máximos no tempo decorrido entre eventos.

Identificando e Especificando Eventos


Partindo-se da discussão anterior do modelo de ciclo de vida, um dos componentes-cha-
ve que necessitamos identificar é (são) o(s) evento(s). Nesta seção, aprenderemos técnicas
de identificação e especificação de eventos.

Caso de Uso e Cenário


Conforme descrito no Capítulo 4, um caso de uso é uma descrição genérica de toda uma
transação envolvendo diversos objetos. Um cenário é uma instância de um caso de uso.
Ele mostra uma série particular de interações entre objetos em uma execução exclusiva
do sistema. Essa execução individual do sistema tipicamente constitui uma transação
(partindo-se das perspectivas do objeto externo) entre o objeto externo e a aplicação/sis-
tema.
Os cenários podem ser ilustrados de duas formas diferentes:
1. Diagrama de seqüência
Mostra a interação entre um conjunto de objetos em ordem temporal, que é mui-
to útil para o entendimento de questões de tempo. Uma forma alternativa é um
diálogo sob a forma de texto; maneira essa amplamente utilizada por escritores
de requisitos que não dispõem de formação técnica.
2. Diagrama de colaboração
Mostra as interações entre um conjunto de objetos como nódulos em um gráfico,
que é muito útil para o entendimento de estruturas de software, pois todo tipo
de interação que afeta um objeto é localizado em torno dele.

Diagrama de Seqüência
Dessas duas formas de exibição de cenários, apenas discutiremos o diagrama de seqüên-
cia neste livro.9 Os elementos básicos de um diagrama de seqüência foram introduzidos
anteriormente. Há uma forma de diagrama de seqüência que captura seqüências de pro-
cedimentos de chamada em situações em que há um ponto único de controle em qual-
quer determinado tempo. Nesse esquema, é utilizada uma linha dupla para representar
o período de tempo no qual um objeto tem uma cadeia de controle. Portanto, uma linha
simples indica que o objeto está bloqueado (fora de controle) e esperando por um evento
que lhe confira controle.

9. Apesar de os diagramas de colaboração proverem maiores detalhes sobre os relacionamentos entre


objetos, eles tendem a ser mais complicados, e a maioria dos principiantes na área dá apenas uma
olhada neles, ou os interpreta de forma errada.
126 UML E C++ CAP. 8

Exemplo
Para uma melhor compreensão, iremos nos valer de um exemplo de Shlaer e Mellor. Su-
ponha que queiramos modelar um forno de microondas em pequena escala — o Micro-
ondas de Um Minuto. Os requisitos do produto são os seguintes:
1. Há uma única tecla de controle disponível para os usuários do forno.
2. Se a porta do forno estiver fechada e algum usuário acionar essa tecla, o forno
entrará em funcionamento (ou seja, energizará o tubo) durante 1 minuto.
3. Se o usuário acionar a tecla a qualquer momento enquanto o forno estiver em
funcionamento, ele obterá um minuto extra no tempo de preparação dos alimen-
tos. Por exemplo, se o usuário ainda tiver 31 segundos de tempo de finalização
de preparo e ele acionar duas vezes o botão, o tempo de preparo ficará fixado
em 2 minutos e 31 segundos.
4. A utilização da tecla com a porta aberta não apresenta qualquer efeito.
5. Há uma luz dentro do forno.
6. Sempre que o forno estiver em funcionamento, a luz deverá estar acesa (de
modo que o usuário possa visualizar através do vidro da porta do forno e veri-
ficar se o alimento está “borbulhando”).
7. Sempre que a porta estiver aberta, essa luz deverá ficar acesa (de forma que o
usuário possa ver o que está em seu interior ou ter luz suficiente para limpar o
forno).
8. O usuário pode interromper o funcionamento do forno abrindo a sua porta.
9. Se o usuário fechar a porta, a luz se apagará. Esta é configuração normal quando
alguém acabou de colocar algum alimento dentro do forno mas ainda não acio-
nou a tecla de controle.
10. Se o forno operar apropriadamente (preparar a comida até o tempo preestabele-
cido desejado), ele desligará o tubo de potência e a luz. Nesse instante, ele ainda
emitirá um sinal sonoro de alerta para avisar ao usuário que a comida está pronta.
Dos requisitos textuais, são identificados os seguintes incidentes pertinentes:
■ Abertura da porta
■ Fechamento da porta
■ Utilização da tecla de controle
■ Término do intervalo prescrito para o preparo da comida
Esses incidentes são eventos que podem fazer com que o forno tenha de realizar al-
gumas operações e, também, mude de estado. Esses incidentes são abstraídos ou captu-
rados como eventos. Com esses eventos externos, podemos criar o seguinte conjunto de
diagramas de seqüência para o forno de microondas utilizando casos de uso e cenários:
o cenário 1 é o caso normal, e está mostrado na Figura 8.3. Queira observar que todos os
cenários são desenvolvidos partindo-se da perspectiva de um usuário externo. No cenário
2, não foi acrescentado qualquer tempo extra. Isso é mostrado na Figura 8.4. No cenário
3, o usuário abre a porta enquanto a comida está sendo preparada. Isso é mostrado na
Figura 8.5.
CAP. 8 COMPORTAMENTO DINÂMICO 127

FIGURA 8.3 Diagrama de seqüência para o forno de microondas no cenário 1.

Esses cenários abordam todas as diversas seqüências pertinentes à construção do


modelo de estado. Tecnicamente, o usuário abre a porta, e ela notifica ao forno que está
aberta; o mesmo acontecendo com o fechamento da porta. O usuário também aciona a
tecla, e esta notifica ao forno que a tecla foi pressionada. Entretanto, nem a tecla ou a por-
ta realiza qualquer ação por seus próprios termos a partir desses incidentes. Nós o mo-
delamos como se o usuário ao abrir a porta enviasse um sinal diretamente ao forno; isto
simplifica o modelo sem que haja qualquer perda de informação para nossos propósitos.
Isso é mostrado na Figura 8.6.
128 UML E C++ CAP. 8

FIGURA 8.4 Diagrama de seqüência para o forno de microondas no cenário 2.


CAP. 8 COMPORTAMENTO DINÂMICO 129

FIGURA 8.5 Diagrama de seqüência para o forno de microondas no cenário 3.


130 UML E C++ CAP. 8

FIGURA 8.6 Diagrama de seqüência para o forno de microondas no cenário 4. O usuário está
abrindo e fechando a porta.

Especificando o Comportamento Dinâmico


Os casos de uso e cenários não são documentação suficiente para fins de desenvolvimen-
to. Agora, examinaremos certos modos de documentação de eventos e de documentação
do comportamento dinâmico sob uma forma mais apropriada para a programação.

Lista de Eventos
Um evento é a abstração de um incidente ou sinal no mundo real que informa a algum
objeto do sistema que ele está (ou poderá estar) movendo-se para um novo estado. No
processo de abstração, deverão ser especificados quatro aspectos de um evento:
1. Significado
O significado de um evento é geralmente capturado em uma frase curta que nos
informa o que está ocorrendo no mundo real. Por exemplo, “roda deixa o solo”
no exemplo do avião, e “porta aberta” no do forno de microondas.
2. Destino
O objeto10 que recebe o evento. Por convenção, o evento é enviado somente a
um único receptor.

10. O modelo de estado é parte de um objeto. Em virtude dos padrões de ciclo de vida serem definidos
por classe, definimos o modelo de estado para toda a classe.
CAP. 8 COMPORTAMENTO DINÂMICO 131

3. Rótulo
Um rótulo único deve ser provido para cada evento para distinguir os diferentes
eventos entre si. Isso é de crucial importância quando há eventos distintos com
significados similares. Embora o rótulo seja arbitrário, o formato sugerido para
a identificação é o de utilizar uma combinação entre letras e números. Uma con-
venção apropriada é utilizar um processo de identificação baseado no objeto-
destino, em que todos os eventos recebidos pela mesma classe começam com
a(s) letra(s)-chave desta.11
4. Dados de eventos
Um evento deverá ser pensado como uma solicitação de serviço. Dessa maneira,
ele pode, e geralmente irá, portar dados. Esses dados são conferidos ao objeto
como parâmetros da solicitação de serviço.
Uma lista de eventos é simplesmente uma listagem de todos os eventos dos cenários
que são aplicáveis à definição dos modelos de estado para os objetos dentro da aplica-
ção/sistema. A lista de eventos para o exemplo do forno de microondas é mostrada na
Tabela 8.1.

TABELA 8.1 Lista de Eventos


Rótulo Significado Fonte Destino Dados

V1 Porta aberta Usuário Forno Nenhum


V2 Porta fechada Usuário Forno Nenhum
V3 Tecla acionada Usuário Forno Nenhum
V4 Fim do tempo Timer Forno Nenhum
L1 Acender luz Forno Luz Nenhum
L2 Desligar luz Forno Luz Nenhum
P1 Ligar tubo Forno Tubo Nenhum
(energizar tubo)
P2 Desligar tubo Forno Tubo Nenhum
(desenergizar tubo)
T1 Posicionar timer em Forno Timer Nenhum
1 minuto
T2 Acrescer 1 minuto Forno Timer Nenhum
ao timer
T3 Zerar timer Forno Timer Nenhum

Sem o diagrama de seqüência, a maioria de nós consideraria “introduzir alimento”


e “retirar comida” como incidentes significativos. Entretanto, o diagrama de seqüência re-
vela que eles não provocam a ocorrência de qualquer ação e, portanto, não são materiais

11. A(s) letra(s)-chave da classe é (são) um modo definido pelo desenvolvedor para referir-se a uma
classe com uma abreviação em vez de com o nome completo da classe. Por exemplo, uma classe
denominada “Microwave” poderia utilizar “mw” como as letras-chave. O uso de letras-chave é um
artefato das origens de C++ na programação C, na qual funções que operavam em estruturas de
dados específicas utilizaram letras-chave para possibilitar aos programadores que recordassem de
uma associação entre a função e a estrutura de dado sobre o qual ela deveria operar.
132 UML E C++ CAP. 8

ao processo de modelagem. Isto talvez possa parecer estranho aos principiantes, pois o
objetivo principal de um forno de microondas é o de aquecer alimentos. Na realidade,
este forno operará sem qualquer alimento nele, e podemos ligá-lo sem que haja qualquer
alimento em seu interior. Portanto, nosso modelo de fato captura precisamente como é,
na realidade, projetado e construído um forno de microondas. O sinal sonoro de aviso é
um incidente, mas o processamento deste evento é controlado pelo objeto externo (usuá-
rio). Assim, ele não é um evento que o sistema processará; portanto, não se encontra em
nossa lista de eventos.

Tabela de Transição de Estado


Na modelagem de comportamento dinâmico, são conferidos a um estado um número e
um nome exclusivos dentro do modelo de estado. O número é utilizado na tabela de tran-
sição de estado para descrever o próximo estado e não prescreve a ordem com a qual um
objeto ocuparia os estados. Uma ação é um conjunto de operações que precisa ser realiza-
do quando ocorre a transição. São permitidas as seguintes operações:
■ Ler e escrever operações que envolvam seus próprios atributos.
■ Gerar um evento a ser enviado a qualquer outro objeto, inclusive a ele próprio.
■ Gerar um evento para algo fora do escopo da análise (por exemplo, um operador,
um dispositivo de hardware ou um objeto em outro sistema/subsistema).
■ Criar, eliminar, fixar, restabelecer, ler um timer.
■ Acessar serviços de outro objeto, inclusive os dos objetos da mesma classe.
Em virtude de as operações permitidas serem muito liberais em uma ação, é respon-
sabilidade do analista/desenvolvedor assegurar a consistência do modelo de estado
como um todo. Portanto, os analistas deveriam garantir o seguinte:
■ Deixar o objeto consistente.
Se um atributo for atualizado, qualquer atributo que computacionalmente dependa
dele também deverá ser atualizado.
■ Garantir consistência de relacionamento.
Se uma ação criar ou eliminar um objeto, ela deve garantir que quaisquer relaciona-
mentos envolvendo esses objetos se tornarão consistentes com as regras determina-
das no modelo.
■ Deixar subtipos e supertipos consistentes.
■ Se uma ação migrar um objeto de um tipo para outro, ela deverá assegurar que to-
dos os objetos e relacionamentos apropriados sejam gerenciados.
Em uma tabela de transição de estado, cada linha representa um dos possíveis estados
(estágios) do modelo de estado12, e cada coluna representa um evento que tem este mo-
delo de estado como seu destino. As células da tabela são preenchidas com a especifica-
ção do que acontece quando uma instância de classe (por exemplo, o seu forno de
microondas específico) em um determinado estado (linha) recebe um evento em particu-
lar (coluna na tabela).

12. Um modelo de estado pertence a um objeto.


CAP. 8 COMPORTAMENTO DINÂMICO 133

O processo de criação da tabela de transição de estado para cada classe13 é descrito


como se segue:
1. Coloque todos os eventos que tenham o mesmo destino nas colunas da tabela.
2. Comece com uma linha se você não tiver identificado quaisquer estágios ou uti-
lize seus estágios como estados possíveis.
3. Adote cada cenário e inicie o preenchimento das células na tabela, indicando o
próximo estado acima da linha e a ação debaixo dela.
4. Quando você deparar com algum conflito (ou seja, uma célula que precise res-
ponder a um evento diferentemente para dois cenários distintos), acrescente um
novo estado ao modelo; isso significa que houve uma mudança no estado (está-
gio) que não foi capturada na análise original.14
5. Mantenha a execução das etapas 3 e 4 até que todos os cenários sejam captura-
dos de maneira consistente na tabela.
6. Agora, examine as células vazias e decida se elas correspondem a eventos igno-
rados ou a situações do tipo “não pode acontecer”.15
7. Finalmente, caso esteja utilizando o modelo de Mealy, reduza quaisquer linhas
idênticas a uma única linha.
As tabelas de transição de estado para o exemplo do forno de microondas são apre-
sentadas nas Tabelas 8.2, 8.3, 8.4 e 8.5.

TABELA 8.2

Estados L1: Acender L2: Apagar

1. Ligado Evento ignorado [4] 2


Nenhum

2. Desligado 1 Evento ignorado [4]


Nenhum

Nota 1: Quando a luz estiver acesa, ignore a solicitação “acesa”; similarmente para o estado
“apagada”.

13. Pelo fato de os padrões de comportamento aplicarem-se a uma classe inteira, definimos o modelo
de estado para a classe. Cada objeto obtém seu próprio modelo de estado, da mesma forma que os
atributos.
14. Isto é geralmente visto como a necessidade de executar diferentes ações quando o evento for
recebido nos diferentes cenários.
15. As etapas 6 e 7 são muito importantes no sentido de assegurar a perfeição da análise.
134 UML E C++ CAP. 8

TABELA 8.3 Forno de Microondas

Estados V1: Porta V2: Porta V3: Tecla V4: Fim do


Aberta Fechada Acionada Tempo

1. Ocioso com Não pode 2 Evento Não pode


porta aberta acontecer [1] Apagar ignorado acontecer
a luz [2]

2. Ocioso com 1 Não pode 3 Não pode


porta fechada Acender a luz acontecer Posicionar acontecer
[3] timer em 1 [2]
min; acender a
luz; ligar o tubo

3. Período 6 Não pode 4 5


inicial de Desligar o acontecer Acrescer 1 min Desligar o
preparo dos tubo: zerar [3] ao timer tubo; apagar a
alimentos timer luz; sinal
sonoro de aviso

4. Período 6 Não pode 4 5


estendido de Desligar o acontecer Acrescer 1 min Desligar o
preparo tubo: zerar [3] ao timer tubo; apagar a
timer luz; sinal
sonoro de aviso

5. Preparo 1 Não pode Evento Não pode


finalizado Acender a luz acontecer ignorado acontecer
[3] [2]

6. Preparo Não pode 2 Evento Não pode


interrompido acontecer Apagar a luz ignorado acontecer
[1] [2]

Nota 2: A porta já está aberta.


Nota 3: O timer não está “ativado”.
Nota 4: A porta já está fechada.
CAP. 8 COMPORTAMENTO DINÂMICO 135

TABELA 8.4 Timer

Estados T1: Posicionar T2: Acrescer T3: Zerar Timer T4: Tique do T5: DISPARO
Timer Tempo Relógio

1. Ocioso 2 Não pode Não pode Não pode Não pode


Estabelecer acontecer [6] acontecer [6] acontecer [7] acontecer [9]
tempo
remanescente
em 1 min;
configurar
mecanismo de
tique-taque
2. Fixação Não pode 5 1 3 Não pode
acontecer [8] Acrescer 1 min Zerar tempo Subtrair um acontecer [9]
ao tempo remanescente: tique de tempo
remanescente desconfigurar do tempo
mecanismo de remanescente
tique-taque
3. Contagem Não pode 5 1 3 4
regressiva acontecer [8] Acrescer 1 min Zerar tempo Subtrair um Gerar sinal de
ao tempo remanescente: tique de tempo T3 para efetivar
remanescente desconfigurar do tempo transição de
mecanismo de remanescente; disparo a ocioso
tique-taque verificar se
tempo
remanescente é
<=0; em caso
positivo, gerar
sinal interno T5
para provocar
transição ao
estado de
disparo
4. Disparo Não pode Evento 1 Evento Não pode
acontecer[8] ignorado [10] Zerar tempo ignorado [10] acontecer [9]
remanescente:
desconfigurar
mecanismo de
tique-taque
5. Acréscimo Não pode 5 1 3 Não pode
acontecer [8] Acrescer 1 min Zerar tempo Subtrair um acontecer [9]
ao tempo remanescente: tique de tempo
remanescente desconfigurar do tempo
mecanismo de remanescente
tique-taque
Nota 5: O timer não está ativado.
Nota 6: O mecanismo de tique não está ativado.
Nota 7: O timer já está configurado.
Nota 8: T5 é um sinal interno gerado somente quando ele estiver no estado 3.
Nota 9: Tarde demais.
136 UML E C++ CAP. 8

TABELA 8.5 Tubo de Potência

Estados P1: Ligar P2: Desligar

1. Ligado Evento 2
ignorado [5] Nenhum

2. Desligado 1 Evento
Nenhum ignorado [5]

Nota 10: Quando o tubo estiver energizado, ignore a solicitação “ligado”


similarmente para o estado “desligado”.

Observe que os estados 1 e 6 correspondem a idênticas transições de estado; os es-


tados 3 e 4 são também idênticos. Entretanto, em cada caso, a pré-condição para a entrada
em cada estado é diferente. Por exemplo, somente é possível entrar no estado de inter-
rupção do preparo a partir de estados anteriores quando o tubo de potência estiver ligado
e o timer ativado. Uma pessoa pode estar abrindo e fechando a porta do forno para pro-
vocar a passagem do modelo de estado do estado 1 para o estado 2, e de volta ao estado
1, não requerendo nem que o timer seja zerado ou que o tubo seja desenergizado. Quan-
do abordarmos a diagramação de modelos de estado, veremos o impacto dessas pré-con-
dições nos diferentes modelos de estado.
Podemos supor que estes objetos tenham métodos que realmente ligam e desligam
os aparelhos físicos.

Documentando o Comportamento Dinâmico


Examinaremos o diagrama de estado segundo uma forma gráfica para documentar o mo-
delo de estado em uma classe.16

Diagramas de Estado
Um diagrama de estado é uma forma gráfica de documentar um modelo de estado. Ele des-
creve, de forma ilustrada, todos os modos possíveis pelos quais os objetos respondem a
eventos enviados por outros objetos.17 Um diagrama de estado simples da UML para um
objeto com dois estados é ilustrado na Figura 8.7. O estado de partida é indicado por uma
transição que incide sobre ele a partir de um círculo sólido. Esse círculo sólido freqüen-
temente é interpretado como a criação inicial do objeto, sendo a transição no estado de
partida um resultado da finalização da inicialização do objeto.18 O estado final19 do ob-
jeto é indicado por um círculo sólido dentro de um círculo.

16. Ainda que cada objeto tenha seu próprio modelo de estado, definimos o gabarito para o modelo
de estado na classe. Lembre-se de que todos os objetos na mesma classe têm cópias do mesmo
modelo de estado.
17. Eventos podem ser externos ou internos.
18. Esta perspectiva permite-nos lidar com situações nas quais um objeto possivelmente não esteja em
um estado consistente até que a inicialização tenha sido finalizada. A transição para o estado inicial
é uma indicação de que o objeto atingiu consistência.
19. O estado final é um estado que não tem qualquer transição de saída (ou seja, é possível entrar e não
sair). Esta situação não indica que haja destruição de um objeto.
CAP. 8 COMPORTAMENTO DINÂMICO 137

Um estado é composto de:


■ Nome
Uma string de texto que distingue este estado de outros estados. Um estado pode
ser anônimo (sem nome).
■ Ação de entrada (palavra-chave: entry)
Ações executadas durante a entrada no estado.
■ Ação de saída (palavra-chave: exit)
Ações executadas durante a saída do estado.
■ Transições internas (palavra-chave: on)
Transições que são tratadas sem causar uma mudança no estado.
■ Atividade (palavra-chave: do)
Um cálculo em curso que ocorre durante todo o período em que o objeto está em
um estado.
■ Subestados
Uma estrutura aninhada do estado. Ela pode envolver subestados desconexos se-
qüencialmente ativos ou subestados concorrentemente ativos.
■ Eventos diferidos (palavra-chave: defer, NEW)
■ Uma lista de eventos que não são tratados nesse estado. Estes eventos são adiados
e postos em fila para serem tratados por objetos em outro estado.
Na UML, os estados são representados por retângulos com bordas arredondadas
com uma linha horizontal que separa o nome do estado dos outros componentes. As tran-
sições são setas direcionadas que ligam o estado inicial ao final. Na UML, quando uma
ação é relacionada dentro de um estado, ela é precedida de um rótulo que indica a hora
em que a ação supostamente ocorrerá. Existem quatro tipos possíveis de rótulos:
■ Entry (Entrar): a ação é executada quando da entrada do estado.
■ Exit (Sair): a ação é executada momentos antes da transição ao novo estado.
■ Do (Fazer): a ação é executada continuamente durante todo o período em que o ob-
jeto está neste estado.
■ On (No) qualquer nome de evento: a ação é executada quando o evento ocorre e o ob-
jeto permanece no mesmo estado.
Uma transição é composta de:
■ Estado-fonte
O estado afetado pela transição (ou seja, o estado ativo). A transição dispara quando
um evento é recebido e uma condição de guarda, se houver, é satisfeita (obviamente
que o objeto também deve estar no estado-fonte naquele momento).
■ Evento
A recepção torna a transição selecionável para disparo.
■ Condição de guarda
Uma expressão booleana que pode ser avaliada quando há o disparo de uma tran-
sição. Se a avaliação for verdadeira, a transição será desencadeada. Se a avaliação
for falsa, ela poderá não se desencadear, e, se não houver outra transição que possa
ser desencadeada pelo evento, este é perdido.
■ Ação
Um cálculo atômico executável. Neste contexto, ele é executado durante a transição.
138 UML E C++ CAP. 8

■ Estado-alvo
O estado que é ativo após a conclusão da transição.
■ Sinais
Uma lista de eventos (sinais) que podem ser gerados durante a transição.
Na UML, uma transição é rotulada por:
■ O evento que origina a transição (obrigatório)
■ Qualquer condição de guarda (opcional)
■ Qualquer ação executada durante a transição (opcional)
■ Qualquer evento gerado pela transição (opcional)
O evento associado à transição é identificado por um rótulo seguido pelos dados do
evento colocados entre parênteses. Se não houver qualquer dado de evento, os parênteses
ficarão vazios. Toda condição de guarda aparecerá depois do evento, colocada dentro de
colchetes. Toda ação executada durante uma transição aparece no diagrama após um
evento desencadeador e a condição, e é separada destes por um símbolo “/”. Um evento
gerado aparece depois da ação de transição e é precedido por um símbolo “^”. O alvo do
evento é separado por um rótulo separado do nome do evento por um período. O evento
gerado tem seus argumentos associados. Deve-se notar que uma transição deverá ter um
evento associado, mas não necessariamente uma condição de guarda, ação ou evento ge-
rado associado.
O diagrama de estado simples ainda ilustra uma característica nova: uma transição
para self (para ele próprio). Esta é uma transição de um estado inicial de volta ao mesmo
estado inicial. Há uma distinção entre lidar com um evento dentro de um estado e com
uma transição para self. No caso em que um evento é tratado dentro de um estado, as
ações “entrar” e “sair” não são executadas. Entretanto, em uma transição para self, ambas
as ações são realizadas assim como todas as ações especificadas na transição.
O diagrama de estado simples apresentado na Figura 8.7 é suficiente para modelos
de estado simples. Modelos de estado mais complexos irão incorporar estados aninhados
(ou seja, subestados). O diagrama da UML para representação de um diagrama de estado
aninhado é apresentado na Figura 8.8. A transição desde o círculo sólido aninhado iden-
tifica o subestado de entrada. Ações são associadas aos estados aninhados em vez dos es-
tados circundantes.
A UML suporta modelos de estado ainda mais complexos ao permitir a presença de
subestados concorrentes. Subestados concorrentes são visualizados (para fins de modela-
gem) como encadeamentos de controle separados. Ou seja, cada subestado pode atuar in-
dependentemente do outro. Um exemplo de subestados concorrentes para o
preenchimento de formulários de matrícula em cursos em uma secretaria escolar é apre-
sentado na Figura 8.9. Nesse exemplo, uma transição para o próximo estado ocorre so-
mente quando todos os estados concorrentes aninhados atingem as condições de saída.
Isso deveria ser comparado com a situação apresentada na Figura 8.10.
CAP. 8 COMPORTAMENTO DINÂMICO 139

FIGURA 8.7 Diagrama de estado simples.

FIGURA 8.8 Diagrama de estado aninhado.

Conforme declarado anteriormente, a UML suporta os diagramas de estado de Mea-


ly e Moore. A escolha de qual modelo de estado é capturado depende da preferência do
analista. Em nosso exemplo, ambas as abordagens são aceitáveis. As Figuras 8.11 e 8.12
representam os diagramas de estado (Mealy e Moore) para o diagrama de transição de
estado do exemplo do forno de microondas. Um dos aspectos mais expressivos da UML
é que ela consegue suportar os modelos de Mealy e Moore. No modelo de Mealy, os es-
tados da tabela de transição eram “eliminados” em virtude de as ações estarem associa-
das a uma transição.
140 UML E C++ CAP. 8

FIGURA 8.9 Subestados concorrentes aninhados, com a saída exigindo que ambos os subes-
tados concorrentes atinjam estados de saída antes do estágio circundante.

FIGURA 8.10 Subestados concorrentes aninhados, com a saída exigindo o cumprimento de


qualquer um destes subestados concorrentes.

No modelo de Moore, a ação está associada ao estado. Não podemos eliminar linhas
idênticas do modelo. Em nosso modelo, a ação está associada à entrada em um estado.
Existem modelos que também possibilitam associar a ação à saída de um estado. A UML
suporta um modelo que possibilita a ação com uma transição, entrada em um estado e
com a saída de um estado. Entretanto, na prática, nós normalmente associamos a ação a
uma transição. O modelo de Harel também associa a ação a uma transição, mas ele per-
mite a formação de subestados e outros recursos importantes. Modelos de estado não são
o único tipo de modelo que pode ser utilizado para capturar comportamento dinâmico.
Outros modelos, como o caso do de Petri Nets, existem para tratar de comportamentos
dinâmicos mais sofisticados. De uma maneira geral, o modelo de estado atende às neces-
sidades de grande parte dos profissionais.
CAP. 8 COMPORTAMENTO DINÂMICO 141

FIGURA 8.11 Notação da UML para o diagrama de estado de Mealy para o exemplo do forno
de microondas.

FIGURA 8.12 Diagrama de estado de Moore para o exemplo do forno de microondas.


142 UML E C++ CAP. 8

Enfoque Recomendado
As etapas recomendadas para a realização de análise comportamental são as seguintes:
1. Prepare cenários de todas as seqüências de interação típicas.
2. Prepare cenários de todos os casos aceitos de exceção de negócios.
3. Se apropriado, prepare cenários de todas as falhas e casos raros. As falhas in-
cluem condições de erro, falhas de equipamento e comportamentos indesejáveis
e incomuns.20 Nos métodos orientados a objeto, as condições de falha que de-
vem ser processadas pela aplicação/sistema deveriam ser formalizadas no mo-
delo de análise exatamente como os comportamentos normais.21
4. Desenvolva diagramas de seqüência para todo o caso que ajudará a identificar
eventos.
5. Utilize o diagrama de seqüência para gerar uma lista de eventos. Observe que
no exemplo assumimos que a transição apenas é dependente do recebimento de
um evento. Em algumas situações, uma transição poderá depender da chegada
de dois ou mais eventos a partir de diferentes fontes. Há dois modos de lidar
com esta situação. Primeiro, é possível criar um estado intermediário para man-
ter a recepção de um dos eventos e utilizá-lo como um estado permanente para
recepção do(s) outro(s) evento(s). A outra opção é acrescentar um atributo ou
objeto para reter os dados. Isso exigiria do programador a inicialização e elimi-
nação do atributo ou objeto em momentos apropriados.
6. Utilize a lista de eventos e seu conhecimento sobre classes para criar tabelas de
transição de estado.
7. Utilize a tabela de transição de estado para criar seus diagramas de estado.
8. Verifique os diagramas de estado analisando os cenários em comparação com o
diagrama.

■■ RESUMO
Historicamente, a captura de mudanças em objetos e seus relacionamentos com o decor-
rer do tempo tem sido muito difícil de ser gerenciada. Neste capítulo, apresentamos um
conceito e seus mecanismos associados para capturar esta propriedade da aplicação/sis-

20. Muitos de nós (controle de processo em tempo real) conhecemos isso como análise de falhas e
entendemos que uma análise apropriada é altamente dependente do problema. O propósito da
análise de falhas é o de levar em consideração o efeito de certos tipos de defeitos e erros e combinar
estratégias no sentido de lidar com esses problemas. No controle do processo, a meta é tanto manter
controle, ou recuperá-lo, ou ainda ter uma paralisação segura e suave de um processo industrial ou
externo. Freqüentemente, a análise de falhas demanda novas exigências, instalação de novos
sensores, colocação de proteções elétricas/mecânicas, redação de manuais de instrução de
procedimentos de emergência etc.
21. Uma palavra de atenção ao estudioso meticuloso: é muito fácil ser levado pela “onda” e investigar
uma miríade de possíveis falhas. As limitações econômicas e de tempo, balanceadas com a
segurança, deverão ser aplicadas para restringir a análise de falhas a cenários reais e sensatos.
Cenários “forçados” não devem ser desenhados para o sistema. Entretanto, caso haja uma
preocupação de dano econômico ou com a segurança, é muito recomendado que seja escrito um
manual de procedimentos para o tratamento destas situações. Comportamentos anormais são via de
regra extremamente complexos e podem provocar um crescimento em maior proporção do modelo
com maior complexidade. Isso levará a um sistema mais difícil de ser mantido e modificado.
CAP. 8 COMPORTAMENTO DINÂMICO 143

tema. Esse conceito é denominado comportamento dinâmico. Aprendemos os seguintes


pontos sobre comportamento dinâmico:
■ Como identificá-lo, reconhecendo formas comuns de ciclos de vida: circular e de
nascimento-e-morte.
■ Como identificar e especificar eventos, que são os estímulos-chave para provocar a
mudança de estado de objetos.
■ Como utilizar os eventos para nos ajudar a identificar e especificar os comporta-
mentos dinâmicos utilizando tabelas de transição de estado.
■ Como documentar essa especificação segundo uma forma gráfica pela utilização de
um diagrama de estado.
■ Um par de tipos de máquinas de estado finitas que pode ser utilizado para im-
plementar o comportamento dinâmico capturado utilizando-se a tabela de tran-
sição de estado.
144 UML E C++ CAP. 8
Identificando Relacionamentos
IDENTIFICANDO RELACIONAMENTOS
9.Identificando Relacionamentos

U ma relação fraca é a coisa mais irrelevante na natureza.

Charles Lamb

N a terceira etapa de nosso método, capturamos como cada objeto provê serviços. No
processo de especificar de que forma isso foi feito, demonstramos ainda que alguns
objetos precisam acessar serviços (funções) de outros objetos a fim de executarem as ope-
rações necessárias para prestar o serviço original. Entretanto, no paradigma orientado a
objeto (diferentemente do paradigma procedural), um objeto não pode simplesmente in-
vocar um serviço (função). O paradigma de passagem de mensagens requer que uma re-
quisição de serviço seja direcionada a um objeto. Na quarta etapa, abordaremos como um
objeto acessa os serviços de outro objeto.

Acessando Serviços de Outro Objeto


Uma das diferenças fundamentais entre o paradigma orientado a objeto e o paradigma pro-
cedural é que, no primeiro, toda requisição de serviço (procedimento de chamada) deve ser
enviada a um objeto específico, enquanto que no paradigma procedural uma função pode
ser invocada diretamente. Por exemplo, para que o objeto A envie uma mensagem1 ao objeto
B, o objeto A deve ter um “identificador” (handle)2 do objeto B. Dessa maneira, na tecnologia
orientada a objeto, os analistas/desenvolvedores precisariam entender os vários veículos dis-
poníveis para conferir a um objeto acesso ao identificador de outro objeto.
Existem três modos básicos pelos quais um objeto acessa os serviços de outro objeto:3

1. Em C++, invocar uma função.


2. Em C++, uma referência ou indicador.
3. Existe um quarto método, o acesso de funções estáticas de classes, que pode ser considerado uma
função gerenciada global.

145
146 UML E C++ CAP. 9

1. O objeto solicitador, que de alguma forma tem um identificador, passa o identifica-


dor do outro objeto como um dos argumentos da assinatura da função (mensagem).
2. O objeto solicitado tem um relacionamento (agregação ou vínculo4) com o outro
objeto. Um relacionamento confere um ou mais identificadores aos outros obje-
tos do relacionamento.
3. O serviço solicitado pertence a uma classe “ancestral”5.

Relacionamentos
Em um mundo de reutilização e portabilidade perfeitas, cada objeto e/ou classe deveria
ser independente de qualquer outro objeto e/ou classe. Assim, durante a etapa 3 (quando
capturamos como cada objeto provê serviços), deveríamos ser capazes de especificar to-
das as operações de todos os métodos com operações que somente utilizem dados per-
tencentes ao objeto e/ou que são passados ao objeto desde o objeto solicitador. Nesse
mundo perfeito, todos os serviços seriam providos utilizando-se somente dados associa-
dos ao objeto em si e o objeto solicitador. Isso faria da reutilização e da portabilidade um
tópico muito simples.6
Infelizmente, nenhum objeto é uma ilha, independente de todos os outros objetos. Objetos
tipicamente dependem de outros objetos para prover serviços e, possivelmente, para ma-
nuseio de erros, dados constantes e tratamento de exceções. Alguns objetos são compo-
nentes de outros objetos, e alguns objetos são utilizados para unir outros objetos. Além
do mais, os objetos são por definição instâncias de classes, e classes podem herdar atri-
butos e serviços de outras classes. Na maioria das aplicações, o modelo necessita capturar
essas interdependências entre os vários objetos.
Essas interdependências são denominadas relacionamentos. Um relacionamento não
é meramente um vínculo que “amarra” um objeto a outro de modo que ele possa acessar
os seus serviços. O relacionamento também carrega um significado semântico. A tecno-
logia orientada a objeto confere a analistas e a desenvolvedores um conjunto muito rico
de mecanismos ou construções para capturar esses relacionamentos semânticos.
Neste capítulo, abordaremos três relacionamentos muito importantes: generaliza-
ção, vínculos e agregação.7 Esses conceitos certamente não são novos; lidamos com eles
todos os dias. Por exemplo, todos nós aprendemos sobre generalização quando estuda-
mos taxinomia na biologia clássica. Um excelente exemplo de vínculo é o casamento, e
um exemplo de agregação (todo-partes) é o carro. Além do mais, os vínculos, sob a forma

4. Um vínculo é um relacionamento entre objetos, e uma associação é um relacionamento entre classes.


5. O termo padrão orientado a objeto para classe ancestral é superclasse. Em síntese, o serviço é
herdado da classe ancestral.
6. Há especialistas orientados a objeto que argumentarão que o uso de relacionamentos (associação e
agregação) viola os princípios de encapsulamento e de ocultação de informações. Essa é uma
afirmativa correta. Infelizmente, todos os relacionamentos, inclusive generalização (também
conhecida como herança), violam aqueles princípios; existem alguns especialistas que
argumentariam contra a herança e o polimorfismo. Nós adotamos uma abordagem mais prática em
relação à tecnologia orientada a objeto. Se um mecanismo nos auxilia a administrar a complexidade,
então nós queremos utilizá-lo. Na prática, é preciso que haja um ou mais grupos de objetos
colaboradores para implementar um número grande de serviços. A experiência tem demonstrado
que todos os mecanismos/construções (e, portanto, alguns deles) que estamos discutindo são
necessários para implementar qualquer aplicação/sistema com qualquer capacidade significativa.
7. Infelizmente, poucas linguagens de programação suportam explicitamente todos estes mecanismos.
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 147

de associações, têm sido extensamente utilizados durante muitos anos8 na comunidade


da modelagem de bancos de dados.

Generalização
A generalização, da forma como a utilizaremos, tem suas origens no paradigma de repre-
sentação de conhecimento utilizado em inteligência artificial. O modelo psicológico de
Ross Quillian da memória associativa é um exemplo. Naquela representação, Quillian in-
troduziu o modelo de “nó-vínculo” para representar uma rede de conhecimento semân-
tico. Os nós representavam classes, e os vínculos representavam relacionamentos entre
objetos nas respectivas classes nos nós. Em uma rede semântica, tanto o nó como o vín-
culo tinham rótulos. Um dos rótulos mais expressivos representava o relacionamento de
generalização. Esse vínculo foi denominado é_um [is_a]. O conceito do relacionamento
é_um é bastante simples. Se o objeto A tem um relacionamento é_um com o objeto B, en-
tão todos os atributos e serviços do objeto B são atributos e serviços do objeto A. Por
exemplo, considere a situação em que tenhamos dois objetos: Pessoa e Funcionário. Atri-
butos adequados para Pessoa poderiam ser nome, idade, peso e altura. Mas, um Funcio-
nário é_uma Pessoa. Pela definição do relacionamento é_um, os atributos de Pessoa são
também os atributos de Funcionário. Note que, de modo geral, o Funcionário tem atributos
complementares, como é o caso de salário, posição, número da organização e número de
identificação do funcionário. Neste exemplo, o objeto Pessoa é referido como o nó ancestral
(ou pai), e o objeto Funcionário como o nó descendente (ou filho). O ancestral é uma genera-
lização do descendente; e, inversamente, o descendente é uma especialização do ancestral.
Os relacionamentos de generalização colocam algumas importantes propriedades
que os diferenciam de outros relacionamentos.9 Essas propriedades são as seguintes:
1. Propriedades estruturais
a. Atributos
O descendente terá todos os atributos do ancestral. Por exemplo, Funcionário
terá o atributo idade porque ele é uma classe descendente de Pessoa.
b. Relacionamentos de não generalização
O descendente terá todos os relacionamentos de não generalização do ances-
tral. Por exemplo, se acrescentarmos um vínculo10 de casamento entre duas

8. Alguns autores orientados a objeto acreditam que cada informação deveria ser incorporada a uma
única classe; dessa maneira, um vínculo viola o princípio de encapsulamento e deveria ser proibido.
Entretanto, muitos de nós que desenvolveram aplicações/sistemas extensos e complexos, acreditam
que um pouco de informação transcende uma única classe, e que a falha em tratar um vínculo em
igualdade de condições com os objetos originará um modelo (programa) contendo hipóteses e
dependências ocultas. Assim, durante a análise seria preciso modelar um vínculo para indicar que
a informação que ele contém não é subordinada a um objeto individual (classe), mas sim dependente
de dois ou mais objetos (classes).
9. Na teoria, todos os três aspectos (atributo, serviço e comportamento) podem ser redefinidos na
subclasse. Nós temos limitado a redefinição devido a um propósito muito prático e consistente com
a implementação da herança na C++. Outras linguagens poderão lhe conferir maior flexibilidade na
redefinição e isto pode parecer algo mais expressivo. Entretanto, poderá haver flexibilidade em
demasia. Na maioria das linguagens, a flexibilidade extra resultará em software menos seguro, pois
não podemos garantir que o software seja bem-testado.
10. Um vínculo é um relacionamento entre dois objetos. Aprenderemos mais adiante que Pessoa é
realmente uma classe e, como tal, o relacionamento é realmente uma associação.
148 UML E C++ CAP. 9

Pessoas, Funcionário também terá um vínculo de casamento porque ele é


uma classe descendente de Pessoa.
2. Propriedades de interface
Todos os serviços providos pelo ancestral deverão também ser providos pelo
descendente.11 Por exemplo, se o objeto Pessoa tiver um serviço aumentarPeso,
então Funcionário também terá um serviço aumentarPeso porque ele é um des-
cendente de Pessoa.
3. Propriedades de comportamento
a. Generalização sem polimorfismo (bom filho)
Na generalização sem polimorfismo, todos os métodos supridos pelo ances-
tral para seus serviços são também utilizados pelo descendente para prover
os correspondentes serviços.
b. Generalização com polimorfismo (mau filho)
Na generalização com polimorfismo, alguns métodos supridos pelo ancestral
para seus serviços são também utilizados pelo descendente para prover os
correspondentes serviços. Para os serviços remanescentes do ancestral, o des-
cendente fornece seus próprios métodos customizados, que substituem os
métodos correspondentes apropriados.12
4. Propriedades matemáticas
a. Anti-simetria
Se o objeto A é_um descendente do objeto B, então o objeto B não poderá ter
um relacionamento é_um com o objeto A (o objeto B não é um descendente
do objeto A). Por exemplo, Funcionário é_uma Pessoa, mas nem todas as
pessoas são funcionários.
b. Transitividade
Se o objeto A é_um objeto B e o objeto B é_um objeto C, então o objeto A
é_um objeto C.13 Por exemplo, se acrescentarmos ao nosso exemplo o fato de
que um Vendedor é_um Funcionário, então Vendedor é também uma Pes-
soa. E mais, ele ainda tem o atributo idade por causa das propriedades 1a e 4b.

A generalização/especialização é um mecanismo crítico ao paradigma orientado a


objeto porque encontrar o(s) correto(s) ancestral(ais) para o estabelecimento de serviços e
atributos é crucial para o desenho de um bom modelo. Infelizmente, é muito difícil para
os principiantes perceberem que a maioria dos objetos com que eles trabalham é compo-
sição (composite) de outros objetos.
Para vencer essa dificuldade, os novatos deveriam considerar cada objeto como um
objeto complexo que pode ser visualizado como muitos diferentes subobjetos. Cada
subobjeto representa aquele objeto complexo em um determinado domínio semântico.
Por exemplo, nós somos (1) funcionários no domínio do trabalho, (2) contribuintes no do-

11. Mesmo que o serviço de um descendente efetivamente remova o comportamento, ele ainda deverá
prover a interface para aquele serviço.
12. Esta talvez seja uma boa hora de fazer lembrar ao leitor que a criança “má” é muitas vezes um
excelente modelo na análise e desenho orientados a objeto.
13. A transitividade torna possível a organização de objetos em uma maneira hierárquica. Por causa
dessa propriedade, a generalização é mostrada graficamente como um gráfico acíclico direcionado.
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 149

mínio governamental, (3) pais e/ou filhos no domínio familiar e (4) sócios no domínio de
um clube social.14 Os serviços que provemos e os relacionamentos que possuímos são di-
ferentes para cada domínio. Por exemplo, considere os serviços contratar, promover, apo-
sentar-se e demitir. Todos esses serviços estão intimamente ligados ao fato de que cada um
de nós é também um funcionário. Quando uma pessoa está desempregada, esses serviços
não se aplicariam. Na condição de um contribuinte, poderá existir um relacionamento de
auditoria de imposto com um auditor de impostos. Esse é um relacionamento muito in-
timamente ligado a cada um de nós quando somos contribuintes. Seria inadequado uti-
lizar esse relacionamento nos outros domínios. Por exemplo, geralmente contribuintes
não querem que seus auditores sejam conhecidos por seus empregadores ou clubes sociais.
O uso apropriado do conceito de generalização ajuda-nos a representar objetos com-
postos (composite objects) de maneira que a complexidade seja administrada e, por conse-
guinte, disponibilizado um software de manutenção mais fácil e mais flexível no tocante
a mudanças. Apesar de um descendente ser alguém que possui todos os atributos, rela-
cionamentos de não generalização e serviços de todos os seus ancestrais, é melhor consi-
derá-lo como alguém que tem acesso a todas essas coisas por meio do apropriado
subobjeto do qual ele é uma especialização. Isso nos forçará a manter os atributos, rela-
cionamentos e serviços no domínio semântico apropriado, o que reduz o acoplamento e
provê uma maior coesão. Menor grau de acoplamento e maior nível de coesão promovem
a obtenção de software mais flexível e manutenível.

Identificando e Especificando Generalização/Especialização


Recomendamos a utilização da lista original de objetos potenciais, sem os objetos exter-
nos à aplicação, como nossa lista de objetos que podem ser potencialmente utilizados em
um relacionamento é_um. Com essa lista, aplicamos os seguintes testes para cada par
possível de objetos. Perguntamos: “O objeto A é um objeto B?” e “O objeto B é um objeto
A?”. As respostas admissíveis são sempre, às vezes e nunca. Se a resposta para ambas as
questões for nunca, os dois objetos não terão um relacionamento é_um entre si. Se ambas
as respostas forem sempre, o objeto A e o objeto B serão sinônimos.15 Se a resposta para
“O objeto A é um objeto B?” for sempre e a resposta para “O objeto B é um objeto A?” for
às vezes, então o objeto A terá um relacionamento é_um com um objeto B.16
Por exemplo, examinaremos a seguinte lista de objetos: Diretor, Gerente, Supervi-
sor, Consultor, Engenheiro, Empreiteiro e Representante. Aplicaremos nosso teste para
esses objetos em um meio corporativo (empresarial) típico. As respostas estão apresenta-
das na Tabela 9.1.

14. Embora alguns destes nomes também possam ser nomes de papéis (“personagens”) em algumas
aplicações, adotaremos aqui a captura destes conceitos como objetos.
15. Eles ou são instâncias da mesma classe ou diferentes nomes para a mesma classe.
16. A única combinação remanescente representará que o objeto B tem um relacionamento é_um com o
objeto A.
150 UML E C++ CAP. 9

TABELA 9.1 Tabela de Análise é_um


A é um B? D G S C E Em R

Diretor X s n n n n s
Gerente av X av n n n s
Supervisor n s X n n n s
Consultor n n n X n n s
Engenheiro n n n n X av n
Empreiteiro n n n n s X n
Representante av av av av n n X

* A é a linha, B é a coluna, D = Diretor, G = Gerente, S = Supervisor, C = Consultor, E = Engenheiro, Em = Empreiteiro


e R = Representante. Nas células, s = sempre, av = às vezes e n = nunca.

Para utilizar a Tabela 9.1 de maneira efetiva, examinaremos as colunas com pelo
menos um s e, então, iniciaremos com as colunas contendo o menor número de Ss. Neste
exemplo, seria a coluna de Engenheiro. A partir da tabela, um Empreiteiro é_um Enge-
nheiro. A próxima coluna com o menor número de Ss é a de Gerente. A partir da tabela,
Supervisor é_um Gerente e Diretor é_um Gerente. Finalmente, estamos preparados para
a coluna dos Representantes. Se utilizarmos os resultados da tabela diretamente, teremos
quatro relacionamentos do tipo é_um.
■ Um Diretor é_um Representante
■ Um Gerente é_um Representante
■ Um Supervisor é_um Representante
■ Um Consultor é_um Representante
Entretanto, dos relacionamentos é_um anteriores, sabemos que tanto um Supervisor
como um Diretor são também Gerentes. Assim, provavelmente, a semântica apropriada
seria que um Gerente é_um Representante e que um Consultor é_um Representante. O
Supervisor e o Diretor herdam esse relacionamento de Gerente.
Como um exercício para o leitor, considere o acréscimo dos três objetos seguintes à
Tabela 9.1: (1) Jim, que é um Diretor, (2) Jack, que é um Supervisor e (3) Joe, que é um
Engenheiro. Quando da realização desse exercício, deve-se notar que todos os três objetos
(Jim, Jack, Joe) satisfazem o teste é_um.17

Agregação de Objetos
Perceber um objeto como algo que consiste em subobjetos, cada um dos quais operando
em um domínio semântico,18 não é o mesmo do que visualizar um objeto como algo que
consiste em componentes de objetos. Para suportar essa segunda visão, um outro meca-
nismo, agregação, é provido ao paradigma orientado a objeto. Agregação (ou todo-partes)

17. Aprenderemos em um capítulo posterior que a generalização/especialização é na verdade aplicada


a classes. Jim, Jack e Joe são instâncias. Embora possa ser feito um caso para que uma instância
torne-se uma especialização de um objeto da classe, no paradigma orientado a objeto um diferente
mecanismo é utilizado para capturar este caso especial — instanciamento.
18. Por exemplo, a visão generalização/especialização de um objeto.
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 151

não é um conceito novo para nós. Analistas e desenvolvedores estão constantemente li-
dando com entidades agregadas que consistem em entidades de componentes.19 Por
exemplo, um pedido de compra é composto de itens de linha, uma planilha de horas se-
manais é composta de numerosos registros diários de tempo, e um sistema é composto
de subsistemas constituídos de hardware, software e assim por diante.
A agregação, da mesma forma que a generalização, possui algumas propriedades
importantes:20
1. Propriedades estruturais
As partes devem ter algum relacionamento estrutural ou funcional com o todo
do qual elas são constituintes.
2. Propriedades matemáticas
a. Anti-simetria
Se o objeto A é parte do objeto B, então o objeto B não pode ser uma parte do
objeto A. Por exemplo, um pedido de compra é composto de itens de linha. Por
anti-simetria, um pedido de compra não é parte de um item de linha.
Note que este último poderá ser composto de subitens. Mesmo nesse caso,
um item de linha não poderá ser parte de um item de sublinha.
b. Transitividade
Se o objeto A é parte do objeto B e o objeto B é parte do objeto C, então o
objeto A é uma parte do objeto C. Por exemplo, um pedido de compra é com-
posto de itens de linha, que poderão ser compostos por subitens. Por transi-
tividade, um item de sublinha é também parte do pedido de compra.
Atributos, relacionamentos, serviços e métodos não são herdados na agregação, em
contraste com a generalização. Pelo fato de as propriedades de uma agregação serem
muito fracas, esta pode ser estática ou dinâmica, e um componente de um agregado tam-
bém poderá ser condicional. Uma agregação estática tem componentes fixos (invariantes)
e não pode ser modificada. Uma agregação dinâmica tem componentes que podem variar
durante o tempo. Um componente condicional é ou não um componente de um agregado,
dependendo das condições específicas que apresenta.21
As agregações são muito úteis. Elas reduzem a complexidade tratando muitos obje-
tos como um único objeto. Elas provêem uma construção ou mecanismo que modela,
mais apropriadamente do que um vínculo, entidades específicas do domínio de aplicação
(por exemplo, pedido de compra). As agregações asseguram ainda uma visão apropriada
(ocultação de informações e serviços) das interações entre os componentes. Por exemplo,
os faróis individuais de um semáforo devem ser acesos e apagados em uma seqüência es-
pecífica. Dessa maneira, a criação de um objeto semáforo, que agregue os três faróis in-
dividuais como componentes, possibilitará a modelagem do controle dos componentes

19. O agregado é o todo e o componente é a parte.


20. Estas propriedades precisam ser satisfeitas por todas as instâncias de uma agregação. Além do mais,
quando essas agregações são implementadas, as propriedades devem ser gerenciadas. Infelizmente,
isso raramente é feito, pois a maioria das linguagens de programação não provê os mecanismos de
linguagem para suporte da agregação.
21. Os principiantes precisarão ter muita atenção com componentes condicionais. Eles deveriam ser
raramente utilizados; normalmente, é melhor capturar uma variação pela especialização (formação
de subclasses).
152 UML E C++ CAP. 9

individuais por meio do objeto agregado. Isso é muito expressivo, pois afasta toda a com-
plexidade dos usuários do semáforo.

Classificação da Agregação
Infelizmente, em virtude de o paradigma orientado a objeto não ter definido muito bem
o mecanismo de agregação, a grande maioria das pessoas tem dificuldades na aplicação
correta deste mecanismo na prática. A literatura mais recente sobre este tópico argumenta
que isso se deve ao fato de a agregação em si ser um conceito “ancestral”. Acreditamos
que é preciso utilizar os conceitos de “descendente” (mais especialização) para que pos-
samos utilizar eficazmente este mecanismo. Essas espécies de conceito, ou diferentes tipos
de agregação, capturarão propriedades extras que nos auxiliarão a administrar a comple-
xidade mais corretamente.
Partindo-se de uma perspectiva teórica, lingüistas, lógicos e psicólogos estudaram a
natureza dos relacionamentos. Um dos relacionamentos que tem sido estudado razoavel-
mente bem é aquele entre as partes de um objeto e os conjuntos formados por elas.22 Em
um artigo conjunto, Morton Winston, Roger Chaffin e Douglas Herrmann discutiram o
relacionamento todo-partes. Eles descreveram diversos tipos de agregação (composição
ou relacionamentos meronímicos). Em seus estudos, o tipo de relacionamento é determi-
nado pela combinação das seguintes propriedades básicas:
■ Configuração — quer ou não as partes comportem um relacionamento estrutural ou
funcional particular entre elas ou com o todo.
■ Homomorfia — quer ou não as partes sejam do mesmo tipo de coisa como o todo.
■ Invariância — quer ou não as partes possam estar separadas do todo.
O artigo identificou seis tipos de agregação; nós adicionamos um sétimo tipo:
1. Conjunto-partes (composição componente-todo)
2. Composição material-objeto
3. Composição porção-objeto
4. Composição local-área
5. Composição conjunto-membros
6. Contêiner-conteúdo (composição membro-grupo)
7. Composição membro-parceria

Conjunto-Partes (Composição Componente-Todo)


Nesta agregação conjunto-partes, o todo é constituído de componentes que mantêm suas
identidades mesmo quando são partes do todo. Para ser uma agregação deste tipo, é re-
querido que as partes tenham um específico relacionamento estrutural ou funcional entre
si, bem como com o todo que elas constituem. Por exemplo, fotogramas são parte de um
rolo de filme, cerdas são parte de uma escova, rodas são parte de um carro, geometria
analítica é parte da matemática. Além disso, um objeto integral (todo) é dividido em par-
tes componentes, que são objetos em seus próprios termos. E mais, os componentes talvez

22. O estudo de agregação (particularmente relacionamentos todo-partes) é referido como mereologia (a


lógica da relação entre as partes e o todo). A teoria formal aceita do modelo todo-partes é tipicamente
apresentada em uma das duas estruturas: o Calculus of Individuals, de Leanard e Goodman, e
Meroleogy, de Lesniewski.
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 153

não estejam dispostos ao acaso, mas devem comportar um relacionamento estrutural ou


funcional particular entre eles, ou com o todo. Dessa maneira, o todo exibe uma estrutura
ou organização padronizada. Exemplos incluem semáforos, carros, aviões, brinquedos,
máquinas e computadores.
Estes itens são montados a partir das partes porque, em uma agregação conjunto-
partes, o conjunto não existe sem as partes. O todo pode ser tangível (carro, escova de
dentes, avião, impressora), abstrato (matemática, física, fisiologia, contabilidade, piadas),
organizacional (Otan, Estados Unidos, Exxon) ou temporal (show musical, sessão de cinema).
Entretanto, quando um componente pára de suportar o modelo global do objeto, é
estabelecido um diferente relacionamento. Por exemplo, se uma placa de memória é reti-
rada de um computador, ela não mais é considerada como parte dele. Todavia, a placa
de memória ainda é considerada uma parte ou peça de um computador. Distintamente
de um componente, uma parte ou peça não participa do modelo global do todo e não
provê suporte funcional para o todo. Em um objeto do tipo componente-todo, um com-
ponente do todo pode ser removido sem afetar materialmente o conceito do todo.
Quando procurar por composições de objetos do tipo componente-todo em um do-
cumento de requisitos, faça-o pelas palavras-chave “é parte de” e “é montado a partir
de”. Exemplos delas são os casos descritos a seguir:
■ Um teclado é parte de um computador.
■ A física nuclear é parte da física.
■ Janelas são partes de uma casa.
■ Um recital de piano é parte da apresentação.
■ Cadeiras são partes do escritório
■ Um telefone é montado a partir de suas peças.
■ Uma orquestra é montada a partir de suas diversas divisões de instrumentos.
Conforme evidenciado por esses exemplos, não é difícil identificar relacionamentos
conjunto-partes partindo-se de um documento de requisitos.

Composição Material-Objeto
Na agregação material-objeto, as partes (materiais) perdem suas identidades quando são
utilizadas para conformar o todo. De fato, o relacionamento entre as partes não mais é
reconhecido uma vez que elas se tornem parte do todo. Então, um relacionamento de
composição material-objeto define uma configuração invariante de partes dentro do todo
porque nenhuma parte poderá ser removida do todo. Exemplos são “O pão é constituído
dos seguintes ingredientes: farinha, açúcar, fermento” e “Um carro é constituído de ma-
teriais, tais como ferro, plástico e vidro”.
Note que enquanto a composição material-objeto define do que é constituído o todo,
um objeto componente-todo define as partes do todo. Por exemplo, para descrever um
relacionamento componente-todo nós diríamos “Um carro tem as seguintes partes clara-
mente identificáveis: rodas, motor, portas etc”. Dessa maneira, os componentes podem
ser fisicamente separados do todo porque o relacionamento é extrínseco. Observe que o
relacionamento material-objeto não é extrínseco, pois não é possível separar a farinha do
pão depois que o pão está pronto.
Quando procurar por composições de objetos do tipo material-objeto em um docu-
mento de requisitos, faça-o pelas palavras-chave “é parcialmente” e “é feito de”. Seguem
alguns exemplos:
154 UML E C++ CAP. 9

■ Capucino é parcialmente leite.


■ Uma cadeira é parcialmente ferro.
■ Uma mesa é feita de madeira.
■ Um arranha-céu é parcialmente aço.
■ Um doce é feito parcialmente de açúcar.
■ O pão é feito de farinha.
Observe que o termo “parcialmente” não é necessário para um relacionamento ma-
terial-objeto. Por exemplo, um espelho pode ser feito todo de vidro (não “parcialmente”
de vidro). Além disso, a opção de utilizar o material-objeto ou o objeto do tipo compo-
nente-todo para a representação de um relacionamento talvez seja dependente do domí-
nio. Por exemplo, na maioria das situações, a cerâmica de uma vela de ignição seria
modelada como uma composição material-objeto. Todavia, se em seu domínio de proble-
ma não for possível separar a cerâmica da vela de ignição, será preciso utilizar a compo-
sição componente-todo para capturar este relacionamento.

Composição Porção-Objeto
Na agregação porção-objeto, o relacionamento define uma configuração homomórfica
(mesmo tipo de coisa como o todo) das partes com o todo. Normalmente, as porções dos
objetos podem ser divididas utilizando-se medidas padrão como é o caso de polegadas,
milímetros, litros, galões, horas ou minutos. Dessa maneira, a composição porção-objeto
suporta as operações aritméticas de subtração, adição, multiplicação e divisão.
Quando procurar por composições porção-objeto em um documento de requisitos,
faça-o por palavras como “porção de”, “fatia”, “bocado de”, “segmento de”, “gota de” e
“colherada de”. Seguem alguns exemplos:
■ Uma fatia de pão é uma porção de um pão.
■ Uma colher de cereal é uma porção de uma tigela de cereal.
■ Um segundo é parte de um dia.
■ Um metro é parte de um quilômetro.
■ Uma xícara de café é normalmente parte de uma garrafa de café.
Quando a palavra “pedaço” é utilizada, entretanto, deve-se tomar cuidado para as-
segurar que as peças sejam similares na natureza. Por exemplo, um pedaço de doce é um
doce, e um pedaço de maçã podre é uma maçã, mas um pedaço de um carro que explo-
diu não é um carro.
Observe que cada fatia de pão é considerada como pão e, da mesma maneira, cada
xícara de café é considerada como café. Além do mais, segundo e dia são unidades de
medida nas quais é possível desempenhar uma mistura e combinação para as operações
aritméticas básicas. Essa observação também é verdadeira para as unidades de medida
metro e quilômetro. Entretanto, não será possível misturar e combinar segundos com qui-
lômetros pois eles são conceitos semânticos diferentes. Essa similaridade entre uma por-
ção e o todo permite que o analista/desenhista possibilite a uma porção herdar
seletivamente propriedades do todo. Por exemplo, os tipos de ingredientes em um pão
são os mesmos existentes em uma fatia de pão. A composição entre objetos do tipo com-
ponente-todo também possibilita que certas propriedades do todo se apliquem a suas
partes. Por exemplo, a velocidade de uma bola também pode ser utilizada para sugerir a
velocidade de cada uma de suas partes.
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 155

Composição Local-Área
Na agregação local-área, o relacionamento define uma configuração homomórfica (mes-
mo tipo de coisa como o todo) e invariante das partes com o todo. Este relacionamento é
geralmente utilizado para identificar vínculos entre lugares e locações particulares dentro
deles. Semelhante à composição porção-objeto, todos os locais (pedaços, fatias) devem ser
similares na natureza, mas diferir pelo fato de não poderem ser separados da área da qual
são uma parte.
Quando procurar por composições local-área em um documento de requisitos, faça-
o pela composição porção-objeto prévia e pergunte se este relacionamento é invariante.
Se a resposta for positiva, então ela será uma composição local-área em vez de uma com-
posição porção-objeto. Analise também o relacionamento contêiner-conteúdo (apresenta-
do mais tarde) e pergunte “Todos os conteúdos são homomórficos e não-removíveis?”.
Se a resposta for negativa, novamente tratar-se-á de uma composição local-área em vez
de um relacionamento contêiner-conteúdo. Seguem alguns exemplos:
■ A cidade de Nova Iorque é parte do Estado de Nova Iorque.
■ Los Angeles é parte dos Estados Unidos.
■ Um pico é parte de uma montanha.
■ Um quarto é parte de um hotel.
■ Yosemite é parte da Califórnia.
Gaste um pouco de tempo extra para se convencer de que a afirmação “um quarto
é parte de um hotel” é um exemplo de uma composição local-área.

Composição Conjunto-Membros
A composição conjunto-membros é uma versão especializada da composição local-área.
Além de ser uma configuração monomérica e invariante de partes dentro de um todo, há
uma ordem sugerida para seus membros. Um exemplo é um serviço de reservas de uma
companhia aérea com seus vários segmentos de vôos. Nesse caso, a ordem de cada seg-
mento de vôo no itinerário é uma parte muito importante das reservas. Outros exemplos
incluem planilhas mensais — planilhas diárias; organizadores mensais — planos diários;
pessoas — organograma; nome — lista telefônica; nome — roledex; e arquivo — fichário
de arquivos.
Quando procurar por composições conjunto-membros em um documento de requi-
sitos, faça-o na composição local-área prévia e pergunte se este relacionamento tem uma
ordem sugerida. Se a tiver, então ele é uma composição conjunto-membros em vez de
uma composição local-área.

Contêiner-Conteúdo (Composição Membro-Grupo)


A composição membro-grupo define um conjunto de partes como um todo. As partes
(conteúdos) não suportam relacionamento estrutural nem funcional entre elas ou com o
todo. Além disso, os conteúdos não são homomórficos ou invariantes. O único requisito
é que haja uma conexão espacial, temporal ou social para determinar quando um mem-
bro é parte do grupo. O contêiner existe e tem propriedades e comportamentos próprios.
Ou seja, ele existe mesmo se não houver conteúdos.
Exemplos são uma ordem de compra — itens de linha; sacola — conteúdo da sacola;
caixa — conteúdo da caixa; federação — membros; empresa — funcionários e assim por
diante. No primeiro exemplo, é muito comum que alguém coloque um pedido “em bran-
156 UML E C++ CAP. 9

co” isento de itens de linha. Os itens/ tarefas (itens de linha) são acrescentados mais tarde
e não há qualquer ordem sugerida para eles. Além disso, note que o relacionamento “or-
ganograma” para funcionário tem implicadas uma ordem e uma propriedade invariante,
enquanto que o relacionamento empresa-funcionários não é invariante, tampouco captu-
ra qualquer ordem sugerida.
Este tipo de agregação não poderá ser confundido com a herança (classificação). Por
exemplo, “Jason é um ser humano” e “avião é um veículo de transporte” são classifica-
ções. Jason possui todos os atributos e provê todos os serviços de um ser humano. Simi-
larmente, avião tem todos os atributos e provê todos os serviços de um veículo de
transporte.
O relacionamento contêiner-conteúdo é diferente. Ele normalmente é baseado em
conexões espaciais ou sociais. Por exemplo, para dizer que um arbusto é parte de um jar-
dim implica que ele está dentro dos confins geográficos do jardim e, provavelmente, está
bem próximo de outras plantas dentro do jardim. Todavia, para um arbusto ser classifi-
cado como um jardim, todo arbusto teria de ser um jardim. Similarmente, todo funcionário
teria de ser um clube. Este relacionamento mostra uma tendência de ser um receptáculo
para os relacionamentos do tipo agregação.

Composição Membro-Parceria
A composição membro-parceria define uma forma invariante do relacionamento contêi-
ner-conteúdo. Ela define um conjunto invariante de partes como um todo.
Exemplos deste tipo de relacionamento compreendem os seguintes.
■ Ginger Rogers e Fred Astaire como parceiros de dança.
■ Laurel e Hardy como uma dupla de comediantes.
■ Jacoby e Myers como procuradores.
■ Lee e Tepfenhart como autores deste livro.
Os membros neste tipo de relacionamento não podem ser removidos sem destruir
o relacionamento (parceria). Por exemplo, se Laurel deixar Hardy, a dupla de comedian-
tes de Laurel e Hardy não existirá mais. Hardy poderá então formar uma nova dupla de
comediantes, com um novo parceiro, mas ela constituirá uma diferente parceria.
Quando procurar por relacionamentos deste tipo, verifique a lista prévia de relacio-
namentos contêiner-conteúdo quanto à invariância. Se o relacionamento for invariante,
então o faça ser um relacionamento membro-parceria.

Objetos e Relacionamentos de Agregação


Agora que conseguimos compreender melhor o conceito de agregação, deveríamos fazer
duas observações. Em particular, podemos reconhecer o seguinte:
1. Um objeto pode ser entendido como possuidor de mais do que um tipo de agre-
gação. Por exemplo, um pão pode ser visualizado como um agregado de fatias
de pão (composição porção-objeto), e, da mesma maneira, como feito de farinha,
açúcar, fermento etc. (composição material-objeto). Ambas as visões podem ser
suportadas simultaneamente no paradigma orientado a objeto.
2. A transitividade detém somente agregação do mesmo tipo. Por exemplo, o forno
de microondas é parte de uma cozinha (composição componente-todo) e a cozi-
nha é parte de uma casa (composição local-área). Entretanto, o forno de micro-
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 157

ondas não é parte da casa. Além disso, um computador pode ser uma agregação
de um terminal, estrutura de hardware, teclado e mouse. A estrutura de hard-
ware normalmente é constituída de uma CPU, memória, unidade de disco rígi-
do e unidade de disquete. Por transitividade, a CPU é parte do computador.
Entretanto, um terminal é feito de vidro, silício, aço e plástico. Essa decomposi-
ção do terminal não é a mesma que a decomposição precedente23 da estrutura
de hardware. A maioria das pessoas não aplicaria as propriedades de anti-sime-
tria ou transitividade à composição material-objeto do terminal em conjunção
com a composição componente-todo do computador. Na melhor das hipóteses,
digamos que a composição-material do componente é uma união da composição
material das partes.

Vínculos entre Objetos


A generalização e a agregação contribuirão para que capturemos relacionamentos entre
objetos quando quisermos entender um objeto como um conjunto de objetos. Existem re-
lacionamentos entre objetos que não são generalizações ou agregações, como, por exem-
plo, o relacionamento matrimonial entre uma pessoa e seu/sua cônjuge. Com certeza,
esse relacionamento não é visto como uma generalização. Se assim fosse, uma pessoa te-
ria de herdar todos os relacionamentos familiares de seu/sua cônjuge. Ele também não é
uma agregação, apesar de toda a cerimônia religiosa, pois os divórcios são legais. Dessa
maneira, precisamos de outro mecanismo que capture todos os demais relacionamentos
existentes entre objetos. No paradigma orientado a objeto, este relacionamento tipo “re-
ceptáculo” é denominado vínculo.
Partindo-se de uma perspectiva técnica, um vínculo é um relacionamento (físico ou
conceitual) entre objetos que permite a um objeto saber sobre outro objeto de modo que
um objeto possa requerer os serviços de um outro objeto. Entretanto, um vínculo também
deverá ter um significado semântico consistente com o domínio semântico no qual o ob-
jeto resida.
Na modelagem orientada a objeto, todos os vínculos são considerados bidirecio-
nais.24 Portanto, uma vez estabelecido um vínculo entre dois objetos, pode ser que cada
objeto venha a solicitar os serviços do outro objeto. Um nome de papel identifica unicamen-
te uma extremidade do relacionamento e provê um veículo para que se perceba um rela-
cionamento como uma “passagem” de um objeto para um conjunto de objetos associados.25
O nome de papel permite que um objeto na extremidade de um relacionamento pas-
se (use) o relacionamento sem explicitamente utilizar o nome do relacionamento. Nomes
de papel são necessários para um vínculo (associação) entre dois objetos da mesma classe.
Por exemplo, em um relacionamento supervisionar entre funcionários na classe Funcioná-
rio, os nomes de papel Supervisor e Subordinado ajudariam a distinguir entre dois fun-
cionários participantes deste relacionamento. Os nomes de papel são ainda úteis para

23. Ambas as agregações anteriores são composições do tipo componente-todo.


24. Todos os vínculos são considerados bidirecionais durante a modelagem. Entretanto, na
implementação, é prática comum eliminar-se uma direção de um vínculo.
25. Conjunto significa um ou mais objetos. Da perspectiva de um objeto, a passagem de um
relacionamento é uma operação que produz os objetos afins (associados). Dessa maneira, na
implementação, o nome de papel é um atributo derivado (atributo que é derivado da associação em
vez de estar intrínseco no objeto) cujo valor é um conjunto de objetos correlacionados.
158 UML E C++ CAP. 9

distinguir entre múltiplos vínculos entre dois objetos. Por exemplo, considere uma lava-
gem manual de carros. Os nomes de papel desde carro até funcionário poderiam ser la-
vador, secador, encerador, polidor e lustrador. Para uma instância específica (objeto) de um
carro (classe), Joe (uma instância de Funcionário) é o lavador e lustrador porque ele lava
e lustra o carro.
Além disso, um vínculo pode ser binário (entre dois objetos), ternário (entre três ob-
jetos) ou de nível mais alto. Na prática, é raro encontrar vínculos com um significado se-
mântico que reúna objetos de três diferentes tipos de objetos (classes).26
Uma associação descreve o conjunto de vínculos entre dois (ou mais) objetos de uma
classe individual ou de diferentes classes com o mesmo significado semântico. Portanto,
um vínculo talvez possa ser visto como uma instância de uma associação. Exemplos de
associações entre a mesma classe para a classe Pessoa são: casado_com e trabalha_para.
Hoje em dia, em que normalmente temos em uma família a situação em que tanto o ho-
mem como a mulher trabalham, duas pessoas podem ser casadas e uma delas ser, tam-
bém, o supervisor da outra no local de trabalho. Não podemos utilizar um vínculo para
capturar essa situação, uma vez que os dois relacionamentos têm diferentes significados
semânticos. O casal pode se divorciar, mas ainda continuar a manter o mesmo relaciona-
mento no trabalho. Um exemplo de uma associação entre duas classes é o relacionamento
empregatício conforme aplicado para as classes Empresa e Pessoa. Isto é, “Joe é empre-
gado da Exxon” é uma instância de um vínculo nesta associação. Em virtude de cada vín-
culo (e, portanto, a associação correspondente) ser bidirecional, o vínculo na direção
reversa seria “A Exxon tem Joe como seu funcionário”.
Como uma associação é uma abstração de um conceito, pode ser que ela também
tenha atributos e serviços. Ela poderá ter as mesmas propriedades e recursos de uma clas-
se.27 Pelo fato de, normalmente, não imaginarmos um vínculo como um objeto, os nova-
tos na área deverão ter muito cuidado em não designar atributos do relacionamento a
uma das classes no relacionamento. Por exemplo, considere o salário de uma pessoa. Ele
normalmente é modelado como um atributo da classe Pessoa. Entretanto, ele é na verda-
de um atributo do relacionamento empregatício entre as classes Pessoa e Empresa. Caso
você ainda tenha dúvidas, considere o caso no qual uma pessoa tenha dois empregos com
dois diferentes empregadores.
O último exemplo levanta uma questão pragmática: em que caso se considera salá-
rio um atributo de Pessoa e quando se faz dele um atributo da associação? Teoricamente,
a resposta correta é que salário é um atributo da associação. Partindo de uma perspectiva
de modelagem, recomendamos que seja utilizado o que for mais apropriado para sua si-
tuação de negócio. Decidir o que é apropriado requer um bom julgamento de engenharia.
Recomendamos que se considerem o domínio do problema, a direção futura do produto
e as propriedades da próxima versão como fatores durante a fase de análise.28

26. Um exemplo de um vínculo ternário seria o relacionamento entre concerto, bilhetes do concerto e
pessoas presentes.
27. Entretanto, em muitas situações, nem os atributos nem os serviços do relacionamento precisam ser
capturados no modelo.
28. Em desenho, precisam ser considerados a manteneabilidade, a reutilização, a simplicidade e o
desempenho.
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 159

Identificando e Especificando Vínculos e Agregações


A melhor fonte para inicialmente identificarmos alguns dos vínculos (associações) e agre-
gações é o documento de requisitos. Releia o documento de requisitos e procure pelas
possibilidades de um objeto ser parte de um outro objeto. Estas são agregações poten-
ciais. Utilize as sugestões de palavras-chave e testes anteriormente descritos.
E mais, procure por vínculos no documento de requisitos. Vínculos, da mesma for-
ma que serviços, são, com freqüência, vistos como verbos em um documento de requisi-
tos. Frases que normalmente implicam um vínculo compreendem: “que é obtido de”,
“segue a pista de”, “muda com” e “depende de”. Além do mais, a descrição detalhada
de um serviço também é uma fonte de identificação de vínculos. A maioria dos objetos
que precisa colaborar (ou seja, utilizar serviços) com outros objetos e acessar estes outros
objetos normalmente requer um vínculo.
Outras fontes para ajudar na procura de vínculos são os diagramas de seqüência e
os documentos de especificação de comportamento. Cada pedido de serviço de um objeto
diferente deve ser suportado por algum veículo de acesso. Se o “identificador” não é pas-
sado como um argumento, então deve ser estabelecido um relacionamento entre os dois
objetos. Devem ser tomadas certas precauções para se nomear o relacionamento, geral-
mente um vínculo, de tal maneira que esse nome capture o significado semântico do re-
lacionamento.29
Quando se estiver estudando o documento de especificação de comportamento, so-
mente insira vínculos que tenham significado semântico. Se não for possível encontrar
um bom nome para o vínculo, considere se esse identificador deveria fazer parte da as-
sinatura (deveria ter sido passado a esse objeto desde o objeto solicitador).
Lembre-se das seguintes regras para determinar se foi encontrado um vínculo ou
uma agregação de objetos:
1. Uma agregação não poderá conectar um objeto a ele mesmo. Isso violaria a pro-
priedade de anti-simetria da agregação. Em muitas circunstâncias, a regra é es-
tendida para a idéia de que uma agregação não deveria conectar um objeto de
uma classe a um objeto da mesma classe como um mecanismo para absoluta-
mente evitar que uma agregação conecte um objeto a ele próprio. Por outro lado,
um vínculo pode conectar dois objetos da mesma classe. Por exemplo, “super-
visionar” é uma relação entre dois funcionários (instâncias) da classe Funcioná-
rio. O exemplo mais comum é o casamento entre duas pessoas. Entretanto, esse
exemplo é imperfeito porque nossa sociedade não reconhece legalmente a união
de duas pessoas de qualquer sexo como um casamento. O casamento na maioria
das sociedades dos dias de hoje é um relacionamento entre uma instância da
classe Feminino e uma instância da classe Masculino. Esse exemplo, portanto,
captura as restrições do relacionamento conforme definido pelos atuais padrões
sociais e revela a importância de capturar o vínculo/associação sobre a abstra-

29. Se forem utilizados cartões CRC e os casos de uso impulsionarem o desenho destes cartões, ter-se-á
uma coleção de colaboradores. Encontrar as associações e agregações é muito mais fácil, pois os
colaboradores lhe fornecem uma grande pista. Se o colaborador é transiente, então o objeto
provavelmente quer um identificador do objeto a ele passado a partir do objeto solicitador. Se o
colaborador é persistente, então o objeto tem de ter um relacionamento (quer associação ou
agregação) com o objeto.
160 UML E C++ CAP. 9

ção ou objeto corretos. Portanto, vemos o quanto é difícil modelar corretamente


e capturar todas as restrições envolvidas.
2. Múltiplas conexões entre objetos são permitidas. Cada conexão poderia ser uti-
lizada para capturar um significado semântico distinto. Por exemplo, considere
enviar um carro a um serviço manual de lavagem de carros. São necessários fun-
cionários para lavar, secar, encerar, polir e, finalmente, lustrar o carro. Todas as
tarefas poderão ser realizadas por um único funcionário, ou cada tarefa poderá
ser realizada por um funcionário diferente. Se as modelarmos como vínculos dos
funcionários que realizaram as diversas tarefas, teremos múltiplos vínculos para
uma instância (objeto) de Funcionário (classe). Por exemplo, Joe poderia ter la-
vado e lustrado o carro.
3. Auto-associações são possíveis e comuns. Neste caso, os nomes de papel são es-
senciais para capturar precisamente o relacionamento.
4. Múltipla associação não implica que os mesmos dois objetos estejam correlacio-
nados duplamente.

Administrando Relacionamentos
Uma das tarefas mais difíceis ao construir um modelo orientado a objeto é determinar se um
relacionamento em potencial é mais bem capturado como um argumento na assinatura do
serviço (função), ou como um vínculo, agregação ou generalização/especialização.
A seguir, temos algumas diretrizes para esta tarefa:
■ Se o relacionamento é permanente (estático), então ele deverá ser capturado como
um relacionamento. Mas, o que significa “permanente”? Se você considerar um ce-
nário como uma unidade de tempo, então permanente significa que o relacionamen-
to precisa ser conhecido ao longo dos cenários. Note que permanente é um termo
relativo. Basicamente, se ele tiver de ser armazenado na memória para uso por al-
gum outro processo independente então ele é permanente.
■ Um relacionamento deve capturar algum conceito que se aplica ao domínio do proble-
ma ou a algum subdomínio necessário para implementação. Em outras palavras, deve-
rá existir um significado semântico ligado ao relacionamento. Um serviço somente
deveria passar (usar) o relacionamento quando a utilização dele fosse consistente com
aquele significado semântico. Por exemplo, considere o vínculo para dois objetos Pes-
soa: casado_com. Hoje em dia, em que é normal tanto o homem como a mulher traba-
lharem, é possível que um cônjuge trabalhe para o outro. Teríamos uma modelagem
inadequada e pobre se utilizássemos o relacionamento casado_com para tornar opera-
tivos serviços do domínio do outro cônjuge. Um segundo vínculo (trabalha_para) pre-
cisa ser estabelecido para capturar esse diferente relacionamento semântico.
■ Se você acha que tem uma agregação, certifique-se de que todas as partes estejam
no mesmo domínio e proporcione a mesma configuração estrutural ou funcional
para o todo. Aplique testes de anti-simetria e transitividade para verificar quanto à
consistência. Note que apenas é possível transitividade com agregações do mesmo
tipo. É muito comum que os novatos misturem partes de diferentes tipos de agre-
gação em uma agregação. Isso fará com que o relacionamento falhe no teste de tran-
sitividade. Quando isso acontecer, provavelmente será preciso um exame das partes
para ver se há diferentes tipos de agregados. Por exemplo, considere um edifício
que tenha as seguintes partes: janelas, andares (pisos), escritórios, elevadores, tetos,
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 161

paredes, escadarias, salas de reunião, lanchonete, átrio e loja de miudezas. Caso to-
das essas partes sejam colocadas em uma agregação, teríamos misturado partes de
duas agregações semânticas diferentes. Os escritórios, andares (significando um ní-
vel na construção), salas de reunião, lanchonete, átrio e loja de miudezas estão de-
finindo uma configuração funcional do prédio, enquanto que as janelas, pisos
(significando o assoalho físico), tetos e paredes estão definindo uma configuração
estrutural do prédio. Essas partes devem ser capturadas em duas diferentes agrega-
ções visto que têm semânticas distintas.
■ Uma agregação não poderá conectar dois objetos do mesmo tipo entre si. Isto vio-
laria a propriedade anti-simétrica da agregação. Por exemplo, uma pessoa não po-
derá ser um agregado de outras pessoas. Entretanto, um vínculo poderá conectar
dois objetos do mesmo tipo. Por exemplo, “supervisionar” é um relacionamento en-
tre dois funcionários (instâncias) válido.
■ A agregação é freqüentemente confundida com a inclusão topológica. A inclusão to-
pológica é um relacionamento entre um recipiente, área ou duração temporal e que
é contido por eles. Exemplos são: (1) o cliente está na sala, (2) a reunião é à tarde e
(3) o Monument Valley é o Arizona, Utah. Em cada caso, o sujeito está circundado
pelo recipiente; entretanto, ele não é parte do recipiente em nenhum dos domínios
semânticos significativos. Por exemplo, um cliente não é parte de uma sala, nem
uma reunião é parte de uma tarde. Além disso, nenhuma parte do Monument Val-
ley é o Arizona ou Utah pelo fato de ser parte da reserva dos índios Navahos. A
inclusão topológica é mais comumente confundida com a composição local-área.
Note que qualquer e toda parte de Dalas está no Texas, enquanto que não há ne-
nhuma parte do Monument Valley que se encontra no Arizona.
■ Às vezes, os novatos confundem atributos com agregação. Atributos descrevem o
objeto como um todo (uma abordagem da caixa preta); a agregação descreve as par-
tes que constituem o todo (abordagem da caixa branca). Portanto, uma casa poderá
ter atributos como largura, comprimento e altura, mas ela é feita de madeira, vidro,
tijolos etc.
■ A anexação de um objeto a outro não garante agregação. Certamente que os dedos
dos pés estão ligados aos pés e são partes deles; entretanto, brincos estão presos à
orelha mas não são partes dela. Note que os dedos dos pés provêem suporte fun-
cional aos pés enquanto que brincos não proporcionam qualquer tipo de suporte,
quer ele seja funcional ou estrutural.
■ A posse também pode ser confundida com agregação. Certamente que um carro
tem rodas, e que rodas são partes de um carro. Entretanto, o fato de James possuir
um carro não implica que o carro seja parte de James. Portanto, a posse é capturada
por um vínculo.
■ Múltiplos vínculos entre objetos são válidos. Cada vínculo deveria ser utilizado
para capturar um distinto significado semântico. (Veja o exemplo da lavagem de
carros para Joe.)

Documentando Relacionamentos
No passado, quase todo metodologista orientado a objeto de renome tinha seu próprio
modo de documentar classes, objetos, relacionamentos e comportamentos. Esse não é
mais o caso hoje em dia; a grande maioria dos autores emprega a UML para documentar
162 UML E C++ CAP. 9

relacionamentos. Os padrões de notação para a documentação de relacionamentos na


UML estão mostrados na Figura 9.1.

FIGURA 9.1 Notação da UML para generalização, agregação e associação de objetos.

Em um diagrama de generalização, notamos que uma classe é representada por um íco-


ne retangular, e que a generalização/especialização é desenhada como uma linha sólida que
parte da classe especializada para a classe generalizada com uma ponta de seta cheia e
triangular apontando para a borda da classe generalizada. Normalmente, as classes de
especialização da mesma classe parente são alternativas diferentes no mesmo domínio
semântico e promovem um particionamento da classe parente. Entretanto, algumas apli-
cações requerem que nos especializemos em diversas dimensões ao mesmo tempo; nestes
casos a UML permite a anexação de um rótulo discriminador a um arco de generalização.
Arcos com o mesmo rótulo representam especialização na mesma dimensão. Diferentes
dimensões representam modos abstratos ortogonais de descrever um objeto da classe pa-
rente. Muito embora o princípio da UML não exclua os conceitos de classificação múltipla
e classificação dinâmica, ele não suporta explicitamente estes conceitos.
A agregação é uma forma especial de associação que lida com a composição da clas-
se agregadora. Em um diagrama de agregação, uma classe é representada por um ícone
retangular, e a agregação é representada por linhas sólidas que partem dos agregados
(partes) para o agregador (todo) com uma ponta de seta na forma de diamante apontando
para a borda da classe agregadora. Na UML, são reconhecidas duas formas de agregação.
Na primeira forma, as partes podem existir independentemente do todo; nesta linguagem
de modelagem unificada, isto é representado pelo uso de um diamante parcial e denomi-
nado agregação.
Na segunda forma, somente é possível a existência das partes como uma parte do
todo; nesta linguagem de modelagem unificada, isto é representado pelo uso de um dia-
mante cheio e denominado composição. A multiplicidade da classe agregadora pode ser
uma, muitas e, opcionalmente, uma. A multiplicidade é capturada pela utilização de uma
expressão de texto. A expressão é uma listagem de intervalos de números inteiros sepa-
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 163

rados por vírgulas. Um intervalo é indicado por um número inteiro (o menor valor), dois
pontos e um número inteiro (o maior valor); um número individual e o símbolo ’*’ tam-
bém são intervalos válidos. O símbolo ’*’ indica qualquer número, inclusive nenhum.
Em um diagrama de associação, as classes são representadas por ícones retangula-
res, e uma associação binária é representada por uma linha reta sólida entre dois ícones
retangulares.30 Uma associação poderá ter um nome com uma “seta de direção” pequena
opcional (triângulo sólido sem apêndice) indicando como ler a associação. O nome é co-
locado sobre ou ao lado da linha de associação.31 O nome da associação pode ser omitido
caso sejam utilizados nomes de papel. Em cada extremidade da aplicação há um papel.
Cada papel pode ter um nome que descreve como sua classe é visualizada pela(s) outra(s)
classe(s); isto é denominado nome de papel. Os nomes de papel opostos de uma classe de-
vem ser exclusivos. O nome de papel também determina a multiplicidade de sua classe;
ou seja, o número de instâncias da classe que pode ser associado a uma instância de outra
classe. Captura-se a multiplicidade utilizando-se uma expressão de texto. A expressão é
uma listagem de intervalos de números inteiros separados por vírgulas. Um intervalo é
indicado por um número inteiro (o menor valor), dois pontos e um número inteiro (o
maior valor); um número individual e o símbolo ’*’ também são intervalos válidos. O
símbolo ’*’ indica qualquer número, inclusive nenhum. Se a multiplicidade é maior do
que um, a palavra-chave {ordenada} poderá ser colocada sobre o papel, indicando que as
instâncias na associação têm uma ordem explícita.

Enfoque Recomendado
Nossas etapas para a descoberta de relacionamentos são as seguintes:
1. Obtenha uma lista de objetos potenciais possivelmente envolvidos em generali-
zação.
2. Crie uma tabela desses objetos para o teste “é_um”.
3. Preencha as células da tabela, utilizando somente sempre, às vezes e nunca.
4. Utilize a tabela para encontrar todas as generalizações. Lembre-se de eliminar os
instanciamentos.
5. Desenhe um diagrama hierárquico das generalizações entre classes utilizando a
notação da UML.
6. Releia o documento de requisitos e procure por agregações e vínculos (associa-
ções). Utilize as frases-chave fornecidas anteriormente como pistas para encon-
trar esses relacionamentos.
7. Procure por nomes de papel, que aparecem como substantivos na maioria dos
documentos de requisitos. Embora muitos autores orientados a objeto tenham
estabelecido que os nomes de papel são opcionais, recomendamos fortemente

30. Relacionamentos ternários são desenhados utilizando-se um ícone de diamante extra para unir as
linhas. Esse ícone de diamante é também utilizado para todos os outros relacionamentos de ordem
mais alta. Uma associação que necessite ser uma classe é capturada como uma classe, e sua
associação com a relação é mostrada com uma linha tracejada partindo do ícone da classe
da associação para a associação (linha sólida) entre as duas classes participantes dela.
31. Na teoria, uma associação poderá ter diferentes nomes em cada direção. Nós não recomendamos
tentar nomear associações em ambas as direções.
164 UML E C++ CAP. 9

seu uso pois, na maioria dos casos, é mais fácil e menos confuso atribuir nomes
de papel em vez, ou além, de nomes de relacionamento.
8. Releia as especificações para todos os serviços e identifique os relacionamentos
necessários para suportar aqueles serviços. Pela nova leitura de cada serviço, o
analista/desenvolvedor será capaz de refinar o que deverá ser feito para cada
um dos serviços do objeto.
9. Se os diagramas de seqüência, modelos de caso de uso e as especificações de compor-
tamento forem documentados, leia estes documentos para encontrar novos vínculos.
10. Determine se cada relacionamento em potencial é mais bem capturado como um
argumento na assinatura do serviço (função), ou como um vínculo, agregação
ou generalização/especialização.
11. Documente os resultados utilizando a notação da UML.

Exemplo
Retornemos ao nosso exemplo do corte de grama. Baseado na análise anterior, temos os
seguintes objetos: John, Jane, Peter, Paul, Elizabeth, Mary, Jack e CortadorDeGramaPro-
fissional. Discutiremos a generalização no próximo capítulo. Assim, nesta fase, depois de
termos revisto o documento de requisitos e as especificações de comportamento, perce-
bemos que capturar a família e seus relacionamentos é proveitoso para esta aplicação. As
Figuras 9.2 e 9.3 apresentam agregações e vínculos entre os objetos que consideramos
úteis para nosso exemplo. Os objetos são representados sublinhando-se o nome de acordo
com a especificação da UML.32 O desenho na UML dos diagramas de associação e agre-
gação é descrito no próximo capítulo.
Neste exemplo, Jane tem acesso aos serviços de John por meio do vínculo é_casada.
John tem acesso aos serviços de todos os seus filhos por meio dos vínculos pai/filho.
Mary tem acesso a Jack por meio do vínculo cortar_grama. Os vínculos dos parentes de
Mary entre mãe/filho e pai/filho são permanentes. Eles são sempre aplicáveis no decor-
rer do tempo. Esses vínculos são denominados relacionamentos estáticos (e invariantes).

FIGURA 9.2 Diagrama de agregação para o exemplo do corte de grama.

Entretanto, o vínculo do corte de grama de Mary com Jack é menos permanente.


Mary poderá optar por recorrer a qualquer momento a outro cortador de grama profis-
sional. No entanto, o relacionamento dela com um cortador de grama profissional poderá
ser permanente pelo fato de que há algum cortador de grama profissional por ela utiliza-
do. Nesse caso, consideramos o vínculo estático e variável. No próximo capítulo, utiliza-
remos os vínculos para a definição de associações e a agregação de objetos para definir
agregação de classes.

32. Deve ser notado que muitas das ferramentas disponíveis no mercado não suportam a construção
desses diagramas de objeto.
CAP. 9 IDENTIFICANDO RELACIONAMENTOS 165

FIGURA 9.3 Diagrama de associação para o exemplo do corte de grama.

■■ RESUMO
Em suma, nossas etapas para a descoberta de relacionamentos são as seguintes:
1. Obtenha uma lista de objetos potenciais possivelmente envolvidos em generali-
zação.
2. Crie uma tabela desses objetos para o teste “é_um”.
3. Preencha as células da tabela, utilizando somente sempre, às vezes e nunca.
4. Utilize a tabela para encontrar todas as generalizações. Lembre-se de eliminar os
instanciamentos.
5. Desenhe um diagrama hierárquico das generalizações entre classes utilizando a
notação da UML.
6. Releia o documento de requisitos e procure por agregações e vínculos (associa-
ções). Utilize as frases-chave fornecidas anteriormente como pistas para encon-
trar esses relacionamentos.
7. Procure por nomes de papel, que aparecem como substantivos na maioria dos
documentos de requisitos. Embora muitos autores orientados a objeto tenham
estabelecido que os nomes de papel são opcionais, recomendamos fortemente
seu uso pois, na maioria dos casos, é mais fácil e menos confuso atribuir nomes
de papel em vez, ou além, de nomes de relacionamento.
8. Releia as especificações para todos os serviços e identifique os relacionamentos
necessários para suportar aqueles serviços. Pela nova leitura de cada serviço, o
analista/desenvolvedor será capaz de refinar o que deverá ser feito para cada
um dos serviços do objeto.
9. Se os diagramas de seqüência, modelos de caso de uso e as especificações de
comportamento forem documentados, leia estes documentos para encontrar no-
vos vínculos.
166 UML E C++ CAP. 9

10. Determine se cada relacionamento em potencial é mais bem capturado como um


argumento na assinatura do serviço (função), ou como um vínculo, agregação
ou generalização/especialização.
11. Documente os resultados utilizando a notação da UML.
REGRAS
Regras
10.Regras

A natureza como um todo não é nada mais do que um vínculo de exterio-


ridades segundo regras; e não há absolutamente nada sem regras. Quan-
do acreditamos que acabamos de encontrar uma ausência de regras, somente
é possível afirmar que elas são por nós desconhecidas.

Emanuel Kant

O que tem sido apresentado até o momento é um conjunto de mecanismos integrados


em um paradigma consistente que auxilia a administrar a complexidade dos aspec-
tos procedurais da modelagem funcional1. Entretanto, há aspectos de uma aplicação/sis-
tema que são não-procedurais (declarativos) e são mais bem modelados utilizando-se
outros mecanismos. Neste capítulo, discutiremos como o paradigma orientado a objeto
pode ser estendido para incluir a capacidade de administrar os aspectos declarativos de
uma aplicação.

Introdução
Os conceitos de abstração, encapsulamento, herança, relacionamento e polimorfismo em
métodos orientados a objeto suportam o desenho e a implementação de aplicações pro-
cedurais sofisticadas. A maioria dos métodos orientados a objeto está atualmente baseada
na hipótese de que todos os aspectos da aplicação/sistema serão modelados dentro do
paradigma procedural.

1. A modelagem funcional tem dois aspectos: procedural e declarativo.

167
168 UML E C++ CAP. 10

Entretanto, algumas aplicações têm muitos requerimentos que são fornecidos de


uma maneira declarativa.2 Quando isso ocorre, o tratamento da semântica declarativa (re-
gras e fatos) é deixado para o analista/desenvolvedor. Uma das tarefas mais difíceis para
os desenvolvedores é a de transformar asserções declarativas no paradigma procedural.
É muito natural para os desenvolvedores incorporarem essas asserções declarativas ao
longo dos métodos existentes nas várias classes. No entanto, quando uma asserção decla-
rativa afeta diversos métodos, especialmente ao longo de várias classes, ela precisará ser
escrita em vários locais. Essa não é uma prática muito boa porque (1) há uma transfor-
mação da semântica declarativa em semântica procedural e (2) isso cria acoplamento
oculto entre métodos. O primeiro fator torna o modelo mais difícil de ser compreendido
e viola o objetivo de modelagem da realidade da forma como os especialistas em domí-
nios a vêem. O segundo fator torna muito difíceis a manutenção e a transformação do
modelo.
Aplicações tendem a se desenvolver; à medida que elas tomam mais corpo, os de-
senvolvedores encontram mais situações em que as asserções declarativas foram distri-
buídas pelos métodos. Em breve, o modelo torna-se incapaz de receber manutenção. Por
exemplo, manter uma invariante envolvendo dois objetos poderá requerer que testes si-
milares, mas não idênticos, sejam inseridos em uma variedade de locais dentro do código.
Isso leva a erros de omissão e lógica pelos analistas, desenhistas e programadores logo
que a aplicação é estendida. Em virtude da invariante não estar situada em um local, ela
nunca é estabelecida explicitamente. Hipóteses não estabelecidas fazem com que modifi-
cações do código tornem-se complicadas e propensas a erros. Precisamos de um método
e um mecanismo que manipulem asserções declarativas.
O mecanismo de implementação de que necessitamos é um mecanismo dirigido a da-
dos. Esse mecanismo simplifica a tarefa de manter a integridade do modelo de dois mo-
dos importantes. Primeiro, ele permite que invariantes e restrições sejam estabelecidas
explicitamente em um único local, em vez de tê-las espalhadas em múltiplos locais. Isso
torna o modelo (e, portanto, o código) mais compreensível e modificável. Segundo, pelo
fato de ele ser dirigido a dados, as invariantes e restrições são reavaliadas automaticamente
sempre que forem feitas mudanças relevantes no atributo de um objeto. Isso alivia o ana-
lista/programador do fardo de, explicitamente, ter de incorporar regras de integridade de
dados em sua lógica procedural. A lógica procedural da aplicação não é mais “entulha-
da” com código para manter a integridade do modelo.
O mecanismo dirigido a dados é importante porque muitos de nossos requerimen-
tos declarativos são fornecidos em uma maneira dirigida a dados. Isto nos confere um
modo de capturar a realidade como os especialistas em domínios a vêem — sem a neces-
sidade de transformar um requerimento declarativo (ou solução) em um modelo pura-
mente procedural. As asserções declarativas são normalmente redigidas em um maior
nível de abstração do que as asserções procedurais. A implementação de asserções decla-
rativas utilizando esse mecanismo livra o analista, desenhista e programador de terem
que gerenciar o fluxo de controle para essas asserções.

2. Podemos distinguir entre linguagens ou asserções procedurais e declarativas. A maioria de nós


trabalhou com linguagens de programação procedurais. Elas nos dotam de construções para que
possamos escrever um conjunto de instruções que devem ser executadas seqüencialmente. A
seqüência pode variar dependendo das condições testadas, e é possível que um grupo de instruções
seja executado repetitivamente. Entretanto, linguagens declarativas declaram um conjunto de fatos
e regras. Elas não especificam a seqüência de etapas para a execução do processamento.
CAP. 10 REGRAS 169

Regras
Regras que capturam a semântica declarativa são empregadas para uma variedade de
propósitos, tais como aplicar invariantes em um modelo de domínio, conferir estruturas
de dados complexas, monitorar o estado de uma máquina de estado ou verificar restri-
ções quando um usuário entra com dados. Existem toda sorte de regras: algumas são me-
lhores capturadas diretamente no paradigma clássico orientado a objeto, enquanto outras
são melhor capturadas por outro mecanismo.
Um tipo de regra que é mais bem capturada por outro mecanismo é a regra dirigida
a dados. Isso se deve à propriedade que requer um mecanismo para atuar como um “mo-
nitor” do modelo enquanto ele observa mudanças nos atributos de um objeto e reage
quando é satisfeita uma condição. Muitas aplicações naturalmente requerem esse recurso.
Exemplos são (1) aplicações que monitoram um sistema físico, (2) aplicações que aplicam
diretrizes de negócios ou de engenharia e (3) ferramentas de desenvolvimento de software.
O mecanismo dirigido a dados é bem-apropriado para tratar de regras que monito-
ram coisas. Ele suporta a diretiva situação-ação sem complicar o procedimento lógico de
uma aplicação. Ele ainda satisfaz dois objetivos muito importantes do paradigma orien-
tado a objeto: (1) o modelo deverá ser construído para refletir o modo como os especia-
listas em domínios visualizam a realidade e (2) sempre que possível, o código para a
aplicação deverá ser gerado a partir de um modelo que seja de fácil compreensão para os
especialistas em domínios e usuários finais.
Para satisfazer esses dois objetivos, as asserções declarativas (incluindo as regras)
precisam ser rigorosas. Elas devem ser compreensíveis para o usuário final de modo que
ele possa verificar que as regras corretamente representam políticas de negócio e compor-
tamento desejado para a aplicação/sistema. Portanto, as asserções declarativas, incluindo
as regras, deverão ser redigidas em linguagem estruturada.

Identificando Asserções Declarativas


As asserções declarativas são diferentes das procedurais. Identificar asserções declarati-
vas em um documento de requisitos é relativamente simples. Enquanto as asserções pro-
cedurais são sempre parte de uma seqüência especificada (por exemplo, um
procedimento, uma atividade ou uma tarefa), uma asserção declarativa fica isolada. Uma
asserção declarativa é independente de qualquer seqüência de outras asserções. Ela de-
clara um fato ou uma regra.
Uma asserção declarativa indicando um fato pode ser expressa de diferentes manei-
ras. A seguir têm-se alguns exemplos de fatos:
■ Um registro

Livro Autor(es) Editora

Object-oriented Analysis Coad/Yourdon Yourdon Press


Poor Developer’s Guide to Lee/Tepfenhart Prentice Hall
Object-oriented Programming in C++

■ Um conjunto de valores em uma planilha eletrônica


■ Uma afirmação simples (por exemplo, todos os cirurgiões são médicos)
170 UML E C++ CAP. 10

■ Uma equação isolada


PAGAMENTO_MENSAL_MÍNIMO = (PRINCIPAL x TAXA_DE_JUROS) / 12
As regras geralmente capturam informação sobre como o negócio deve operar. Re-
gras encapsulam o conhecimento do negócio. Palavras-chave comuns em asserções decla-
rativas indicando uma regra são:
■ Deve sempre sustentar que...
■ Deve sempre ser verdade que...
■ Sob todas as condições, quando... então...
■ Sob todas as condições, se... então...
■ Quando... se... então...
■ ... se somente...
■ ... é correto somente se...
Uma regra poderá ser utilizada como um requerimento declarativo para:
■ reforçar “coisas que sempre deverão ser verdadeiras” (invariantes);
■ detectar “coisas que nunca deverão ser verdadeiras” (violações de restrição);
■ manter a integridade de seu modelo de domínio;
■ monitorar quanto a, e reagir contra, eventos importantes;
■ expressar conhecimento do domínio (políticas de negócio, regras de engenharia, e heu-
rística de situação-ação);
■ especificar uma operação (função) que teria de ser usada em muitos métodos;
■ explorar a natureza das regras dirigidas a dados ou eventos.

Especificando e Documentando Regras


Quando um requerimento é redigido como uma asserção declarativa, a melhor prática é
especificá-lo como uma regra.3 Uma técnica que captura regras explicitamente e as torna
de fácil leitura4 é a da linguagem estruturada. Os detalhes das construções disponíveis
em linguagem estruturada devem aguardar até ser feita a classificação das regras (de ne-
gócio).
James Martin e James Odell (1992) construíram o seguinte esquema de classificação
para regras. O esquema deles descreve os seguintes tipos de regras:
■ Regras de integridade, que declaram que algo sempre deverá ser verdadeiro (por
exemplo, um valor de um atributo deve estar compreendido no campo dos números
inteiros, de 1 a 5).
■ Regras de derivação, que declaram como um valor (ou conjunto de valores) é calcula-
do (por exemplo, Imposto Retido = Imposto de Renda Federal + Imposto de Renda
Estadual).
■ Regras de comportamento, que descrevem os aspectos dinâmicos do comportamento,
como no caso de quais condições devem ser verdadeiras para que uma ação seja
executada (por exemplo, quando uma porta é aberta, a luz em um forno é acesa).

3. Fatos podem ser expressos como regras muito facilmente. Eles serão regras de derivação. Regras de
derivação são explicadas mais adiante neste livro.
4. O ideal seria que tivéssemos uma técnica que capturasse regras explicitamente — de uma maneira
que fosse fácil a leitura e que gerasse o código correto.
CAP. 10 REGRAS 171

Para facilitar o mapeamento (tradução) dessas regras para o paradigma orientado a


objeto, podemos refinar ainda mais as categorias de regras de James Martin. Nossas ca-
tegorias estão descritas a seguir:
1. As regras de integridade de dados declaram que algo deverá ser verdadeiro acerca
de um(ns) atributo(s) (por exemplo, um valor de um atributo deve estar com-
preendido no campo dos números inteiros, de 1 a 5).
2. As regras de integridade de relacionamentos declaram que algo deverá ser verdadei-
ro sobre um relacionamento (por exemplo, um gerente não poderá supervisio-
nar mais de dez funcionários).
3. As regras de derivação, incluindo fatos, declaram como um valor ou conjunto de
valores é calculado (PREÇO = 1,5 x CUSTO).
4. As regras de pré-condição de serviços declaram que algo deverá ser verdadeiro an-
tes de um serviço ser realizado (por exemplo, um adiantamento em dinheiro não
será dado a menos que tenha passado o quinto dia do mês).
5. As regras de pós-condição de serviços declaram que algo deverá ser verdadeiro de-
pois da realização de um serviço (por exemplo, o formulário de um pedido é ar-
quivado assim que for corretamente preenchido).
6. As regras de disparo de ações definem o relacionamento casual entre eventos e
ações (por exemplo, quando da aceitação de um pedido envie imediatamente a
fatura).
7. As regras de disparo de dados definem o relacionamento casual entre a condição
de um atributo e uma ação (por exemplo, quando o estoque estiver abaixo do
nível para atender novas encomendas, então, deverão ser feitos novos pedidos).
8. As regras de condição de controle tratam de situações em que múltiplos disparado-
res estão envolvidos na regra (por exemplo, se o produto foi enviado e o dinhei-
ro recebido, ou se a ordem de compra foi cancelada e o depósito em dinheiro
retornou, então a ordem de compra é fechada).
Baseado em nosso esquema de classificação, as construções em linguagem estrutu-
rada mostradas na Figura 10.1 capturam as regras. Nestas construções, uma condição é a
expressão booleana, um evento é um estímulo (sinal) e uma ação é uma invocação de
uma declaração procedural.

1. Para disparadores e condições de controle,


IF condição THEN ação
ou
WHEN evento IF condição THEN ação
2. Para regras de integridade,
IT MUST ALWAYS BE THAT asserção de fato
ou
IT MUST ALWAYS BE THAT IF condição THEN ação
3. Para pré-condição de serviço,
BEFORE serviço a ser executado IT MUST BE THAT fato
4. Para pós-condição de serviço,
AFTER serviço que foi executado IT MUST BE THAT fato
5. Para regras de derivação, fato, normalmente uma equação
ou
WHEN condição ou evento THEN ação
ou
IF condição ou evento THEN ação

FIGURA 10.1 Construções em linguagem estruturada.


172 UML E C++ CAP. 10

Mapeando (Traduzindo) Regras para o Conceito OO Apropriado


Nos anos 80, os sistemas de inteligência artificial ficaram altamente em evidência (moda).
O mecanismo no qual esses sistemas foram baseados para serem construídos era prima-
riamente um mecanismo de inferência. Um mecanismo de inferência processa um conjunto
de fatos e regras para fazer deduções utilizando a inferência lógica. As regras e os pro-
cessos do mecanismo de inferência são denominados regras de produção. A maioria de nós
compreende a semântica declarativa partindo dessa perspectiva. Entretanto, as regras co-
mentadas neste capítulo não serão as de produção.5 Elas são regras vinculadas ao modelo
orientado a objeto para prover um modelo proveitoso e representativo para fins de im-
plementação.
As diretrizes de mapeamento (tradução) para representar uma regra em um concei-
to OO são as seguintes:
1. Pré-condição de serviço
Uma pré-condição de serviço é mapeada (traduzida) em um serviço. Conforme
sugerido por Meyer, a pré-condição é um requisito que deverá ser garantido
pelo objeto solicitador.6
2. Pós-condição de serviço
Uma pós-condição de serviço é também mapeada (traduzida) em um serviço.
Ela constitui uma regra que deve ser verificada pelo autor deste serviço. O ser-
viço deve garantir que a pós-condição seja satisfeita.
3. Condição de controle
Uma condição de controle é mapeada (traduzida) em uma máquina de estado
finita. Ela normalmente é uma condição necessária para uma mudança de estado.

5. Um mecanismo de inferência poderá ser usado para implementar um método em uma classe;
entretanto, não recomendamos esta técnica.
6. Meyer: “Uma das principais fontes de complexidade em programas é a necessidade constante de
checar se os dados passados para um elemento de processamento (método) satisfazem os
requerimentos para se ter um processamento correto. Onde essas verificações deveriam ser
realizadas: no próprio método ou em seu cliente? A menos que os desenhistas de classe formalmente
concordem em uma distribuição precisa de responsabilidades, essas verificações acabam de fato não
sendo feitas, o que gera uma situação muito insegura ou, fora da questão de segurança, acabam
sendo feitas várias vezes. A checagem redundante talvez pareça, mas não é inofensiva. Ela
certamente restringe a eficiência; mas até mesmo mais importante é a ”poluição conceitual“ que ela
promove aos sistemas de software. A complexidade é provavelmente o inimigo de destaque mais
importante da qualidade de um software. A distribuição de checagens redundantes por todo um
sistema de software destrói a simplicidade conceitual do sistema, aumenta o risco de erros e
prejudica qualidades, tais como extensibilidade, compreensibilidade e manteneabilidade. A técnica
recomendada é a de sistematicamente utilizar pré-condições e, em seguida, possibilitar que o autor
do serviço dê como certo, quando da escrita do método, que a correspondente pré-condição seja
satisfeita. O objetivo é permitir um estilo simples de programação, favorecer a leitura, a
manutenabilidade e outras qualidades associadas.”
Essa noção aplica-se a bibliotecas e a classes dentro de uma aplicação mais do que em servidores em
um ambiente computacional distribuído. Um servidor que controla múltiplos clientes não pode se
dar ao luxo de pressupor que todos os clientes são bem-comportados. Ele deve ser codificado
defensivamente para que um cliente mal-intencionado não consiga derrubá-lo ou corromper
quaisquer dados nele armazenados.
CAP. 10 REGRAS 173

4. Disparador de ação
Um disparador de ação é mapeado (traduzido) em uma máquina de estado fi-
nita. Ele é normalmente um evento em um diagrama de transição de estado.
5. Integridade de relacionamento
Uma integridade de relacionamento é mapeada (traduzida) em um relaciona-
mento. Ela normalmente afeta o instanciamento, a eliminação e o acréscimo de
um relacionamento.
6. Integridade de dados ou disparador de dados
A integridade de dados e os disparadores de dados são mapeados (traduzidos)
em um atributo. Normalmente, eles são verificados todas as vezes que o atributo
muda de valor.
7. Derivação
Uma derivação é difícil de ser mapeada (traduzida). Ela é normalmente utilizada
como parte de um método. Todavia, existem situações em que ela é implemen-
tada como um disparador. Deve ser prestada muita atenção quando se tem uma
regra de derivação.

Documentando as Regras pela Utilização da UML


As diretrizes de documentação para as várias regras são descritas a seguir:
1. Pré-condição de serviço
Uma pré-condição de serviço é mapeada (traduzida) em um serviço. Isso precisa
ser capturado como parte dos critérios de entrada. Se houver uma especificação
de operação para o serviço, utilize a seção de pré-condição da especificação opera-
cional para documentar este fato. Se não houver, inclua-o como um comentário
na especificação do método.
2. Pós-condição de serviço
Uma pós-condição de serviço também é mapeada (traduzida) em um serviço. Se
uma especificação de operações for escrita para o serviço, utilize a seção de pós-
condições para documentar este fato. As pós-condições também deverão ser in-
cluídas na descrição do método.
3. Condição de controle
Uma condição de controle é mapeada (traduzida) em uma máquina de estado
finita. Ela normalmente é uma condição necessária para uma mudança de esta-
do. Isso é documentado como uma condição de guarda na UML.
4. Disparador de ação
Um disparador de ação é mapeado (traduzido) em uma máquina de estado fi-
nita. Ele é normalmente um evento em um diagrama de transição de estado. Isso
é documentado como um evento na UML.
5. Integridade de relacionamento
Uma integridade de relacionamento é mapeada (traduzida) em um relaciona-
mento. Ela normalmente afeta o instanciamento, a eliminação e o acréscimo de
um relacionamento. Isso é documentado como uma restrição na UML.
6. Integridade de dados ou disparador de dados
A integridade de dados e os disparadores de dados são mapeados (traduzidos)
em um atributo. Normalmente, eles são verificados todas as vezes que o atributo
muda de valor. Isso é mais bem documentado pela criação de um novo estereó-
174 UML E C++ CAP. 10

tipo, denominado disparador de ação, que é utilizado para capturar as ações as-
sociadas à(s) regra(s). Assim, associações artificiais são desenhadas entre as clas-
ses que necessitam de disparadores de dados e de uma classe disparadora de dados.
7. Derivação
Uma derivação é documentada como parte do método.

Implementando Regras
As diretrizes de mapeamento (tradução) fornecidas anteriormente revelam que as regras
de pré-condição de serviços, regras de pós-condição de serviços, regras de condição de
controle, disparadores de ações e regras de derivação mapeiam (traduzem) muito bem
para o modelo orientado a objeto clássico. Entretanto, as regras de integridade de relacio-
namentos, regras de integridade de dados e disparadores de dados não são bem supor-
tados em nosso modelo. Para tratar dessas regras, é necessário um mecanismo dirigido a
dados. Existem dois modos de prover um mecanismo dirigido a dados:
1. Utilize os disparadores no sistema de banco de dados
Utilizar disparadores no sistema de banco de dados é a maneira clássica de lidar
com regras de integridade de dados e de disparadores de dados. Toda vez que
o banco de dados reconhece uma mudança no valor dos dados, ele dispara uma
rotina escrita pelo usuário. As regras apropriadas são implementadas naquela
rotina. Isso é razoavelmente direto para regras simples dirigidas a dados, mas é
um pouco mais intrincado para regras complexas (tais como restrições de rela-
cionamentos).
2. Utilize uma linguagem que estenda C++ para a inclusão de regras
Nos laboratórios da AT&T7, pesquisadores e desenvolvedores criaram uma
nova linguagem com construções que suportam diretamente os mecanismos di-
rigidos por dados. Essa linguagem é a R++. R++ é uma extensão de C++ que re-
duz as diferenças entre a semântica procedural orientada a objeto e as regras
dirigidas a dados. As classes em C++ contém dois tipos de membros: membros
de dados e funções membros. R++ estende a construção da classe em C++ com
um novo tipo de membro, uma regra.8 Isso possibilita às aplicações orientadas
a objeto empregar computação dirigida por dados. Na condição de uma exten-
são de C++, R++ se enquadra facilmente com os conceitos e práticas de C++. As
regras em R++ são relativamente fáceis de aprender; a sintaxe é similar e o com-
portamento é muito parecido com uma função membro “reativa”.
Uma regra em R++ é sintaticamente definida como se segue:
regra nome da classe :: nome da regra {condição => ação}.
O par condição-ação comporta-se como uma asserção if-then:
if condição then ações.
A ação é automaticamente executada quando a condição é avaliada como verdadei-
ra. O sistema monitora os membros de dados que aparecem na condição da regra e, quan-

7. Deve ser notado que, muito embora a pesquisa em R++ esteja sendo realizada por pesquisadores em
Laboratórios da AT&T, a Lucent é a detentora da patente da R++.
8. O que R++ chama de regras são na verdade somente regras dirigidas a dados.
CAP. 10 REGRAS 175

do um membro de dado muda seus valores de dados, cria-se um evento disparador. O


evento disparador faz com que a regra reavalie a condição, levando em consideração da-
dos modificados/novos. Se a condição for satisfeita, a regra “dispara”. (Quando uma re-
gra dispara, a ação é executada.)
Dentro da condição, parte dos quantificadores existenciais da regra (todos e existe) e
operadores lógicos (e e ou) são suportados. Os quantificadores existenciais e os operado-
res lógicos são utilizados para formar condições compostas. Além disso a linguagem su-
porta objetos relacionados de acesso (e seus serviços associados) por meio de um conceito
denominado ligação (binding). As solicitações de serviço (procedimentos de chamada) são
também suportadas na condição.

Abordagem Recomendada
Quando aparecem asserções declarativas no documento de requisitos, são recomendadas
as seguintes etapas:
1. Separar as asserções declarativas das asserções procedurais.
2. Redefinir as asserções declarativas utilizando linguagem estruturada na condi-
ção de regras, cuidando para que elas sejam rigorosas e implementáveis.
3. Mapear as regras no mecanismo OO apropriado.
4. Se forem utilizadas regras dirigidas a dados, empregar um mecanismo dirigido
a dados para modelar essas regras. Recomendamos R++ quando da utilização de
disparadores de bancos de dados.

■■ RESUMO
Asserções declarativas, ou regras, são uma outra forma natural na qual os especialistas
em domínios e usuários finais especificam seus requerimentos. Nós, como analistas e desen-
volvedores, deveremos aceitar asserções declarativas como uma parte natural dos reque-
rimentos textuais. Segue-se que as asserções declarativas deveriam ser capturadas dentro
de um modelo. Para efetuar isso, devemos traduzir os requerimentos declarativos tex-
tuais em linguagem estruturada para assegurar que contemos com requerimentos rigoro-
sos e implementáveis. Após especificarmos todos os requerimentos declarativos em
linguagem estruturada, deveremos então mapear cada asserção declarativa em uma cate-
goria de regras. A categoria de regras permite que seja adequadamente atribuído ao me-
canismo orientado a objeto apropriado dentro do modelo.
Pelo fato de nem toda categoria de regras poder ser atribuída a um mecanismo clás-
sico orientado a objeto, introduzimos o mecanismo dirigido a dados. Esse mecanismo su-
porta o disparo de regras devido a uma mudança no valor de um atributo. Conforme
discutido, essa é uma extensão muito valiosa ao paradigma orientado a objeto. Historica-
mente, foram utilizados disparos em um sistema de banco de dados para implementar
esse mecanismo. Entretanto, documentar funções disparadoras de bancos de dados e con-
seguir pessoas para ler a documentação não foram consumados com facilidade. Um en-
foque alternativo foi desenvolvido nos Laboratórios da AT&T e da Lucent Bell.
Pesquisadores e desenvolvedores optaram por estender a linguagem C++ para prover
um mecanismo dirigido a dados como uma parte integral da linguagem. Essa solução é
altamente desejável porque podemos ver todo o código em um único local.
176 UML E C++ CAP. 10

Semelhantemente a qualquer ferramenta, as regras dirigidas a dados são boas para


algumas tarefas e não tão efetivas para outras. Nós as recomendamos para:
■ Executar invariantes
■ Manter integridade de dados
■ Manter integridade de relacionamentos
■ Detectar violações de restrições
■ Estabelecer políticas de negócio e diretrizes de engenharia
Uma regra dirigida a dados é como um daemon que, constantemente, monitora atri-
butos e reage quando apropriado. A porção de ação do código fica aparte do código pro-
cedural da rotina e é automaticamente disparada por mudanças relevantes nos objetos
que a regra monitora. Isso alivia o analista/desenvolvedor do controle explícito do dese-
nho e programação para a regra dirigida a dados.
O MODELO
O Modelo
11.O Modelo

O sublime e o ridículo, com freqüência, estão tão intimamente correlacio-


nados que se torna difícil classificá-los separadamente. Uma fase acima
do sublime configura o ridículo; e uma fase acima do ridículo faz ressurgir no-
vamente o sublime.

Tom Paine

N o final da quarta etapa, nós na realidade construímos um modelo integrado do sis-


tema. Caso tivéssemos executado cada etapa perfeitamente, nós teríamos nosso mo-
delo. Infelizmente, as diretrizes e subetapas não são adequadas para garantir esse
resultado. De fato, a deficiência em nosso método é a incapacidade de contribuir para que
um analista/desenvolvedor identifique classes abstratas.1 Na quinta etapa, nós o auxilia-
remos a encontrar essas classes. Esta é a fase de refinamento de nosso modelo.

Conceitos
Embora a maioria de nós lhe dirá que o poderio da tecnologia orientada a objeto é que
ela propicia um mecanismo para que você possa modelar a realidade, na verdade ela não
é uma abordagem que consiga fazer isso. Qualquer pessoa que tenha estudado filosofia
sabe que a realidade é o estado de espírito de cada indivíduo. Assim, a propósito, o que
faz a tecnologia orientada a objeto? Ela modela a compreensão e o processamento da rea-

1. Esta é a deficiência inerente a todos os métodos orientados a objeto. Agora, sabemos o motivo pelo
qual as pessoas dizem que é difícil encontrar objetos. Em virtude de as classes abstratas capturarem
conceitos de nossa mente, nós não prevemos o aparecimento de uma técnica no futuro próximo que
contribua para que capturemos a concepção.

177
178 UML E C++ CAP. 11

lidade capturando os conceitos que as pessoas tenham adquirido. Portanto, uma parte vi-
tal de aprender essa tecnologia está em adquirir um entendimento do que é um conceito
e de como ele é utilizado na análise orientada a objeto.
Cada conceito é uma idéia ou entendimento particular que uma pessoa tem do
mundo. A pessoa sabe que tem um conceito quando ela consegue aplicá-lo de forma
bem-sucedida às coisas/objetos que a rodeiam. Por exemplo, um carro e um telefone são
conceitos amplamente suportados e compreensíveis. Certamente, nós podemos aplicá-los
a coisas/objetos e determinar se uma coisa/objeto é uma instância de um carro, de um
telefone ou de nenhum dos dois.
A formação de conceitos coopera para que organizemos nossa realidade do mundo.
Os psicólogos acreditam que os bebês iniciam a vida em um mundo de confusão e, gra-
dualmente, adquirem conceitos para reduzir toda essa confusão. Por exemplo, um bebê
com poucos meses de vida aprende a diferenciar entre os sons de sua mãe e de seu pai.
Os seres humanos parecem possuir uma habilidade inata de perceber regularidades
e similaridades entre os diversos objetos existentes no mundo. Toda vez que reconhece-
mos essas regularidades e similaridades, criamos um conceito para organizar este fato.
Eventualmente, desenvolvemos conceitos (por exemplo, vermelho e carro) e aprendemos
a combinar conceitos para formar novos conceitos (ou seja, carro vermelho). À medida
que crescemos, desenvolvemos construções conceituais mais elaboradas que levam a um
aumento no significado semântico, na precisão e nas sutilezas.
Pelo fato de eles serem definidos, os conceitos que formamos e utilizamos são mui-
tíssimos variados. Os conceitos podem ser concretos (pessoa, carro, mesa, casa), intangíveis
(tempo, qualidade, empresa), de papéis (mãe, programador, professor, aluno), relacionais
(matrimônio, parceria, supervisão, posse), de eventos (venda, interrupção, colisão, decola-
gem), exibíveis (série, ícone, vídeo), de julgamentos (alto salário, bom exemplo, excelente
emprego), e outros (sinal, átomo).
Esses conceitos funcionam como lentes mentais com as quais tentamos compreen-
der e raciocinar sobre o porquê dos objetos em nosso mundo. Por exemplo, o conceito de
pessoa ajuda-nos a raciocinar sobre bilhões e bilhões de objetos do mundo. Novos con-
ceitos talvez nos auxiliem a (1) perceber os mesmos objetos de maneira diferente, (2) ra-
ciocinar sobre um objeto existente de maneira diferente e (3) acrescentar novos objetos ao
nosso conhecimento. Por exemplo, o conceito de funcionário ajuda-nos a raciocinar de
uma maneira diferente sobre os muitos bilhões de objetos. O conceito de “partícula atô-
mica” adiciona novos objetos ao nosso conhecimento, e o “spin” de partículas, conforme
aplicável a uma partícula atômica, faz com que raciocinemos sobre um objeto existente
de uma maneira diferente.
As pessoas podem possuir conceitos sobre coisas que existiram, que realmente exis-
tem, que podem existir e que, provavelmente, não existirão. Dois conceitos, Papai Noel e
Fada Madrinha, são considerados objetos por algumas pessoas, se bem que para outras
não o sejam. Conceitos como paz em todo o mundo e carro movido por energia solar não
se aplicam a todas as coisas hoje em dia, mas isso talvez não ocorra no futuro. É altamen-
te improvável que o conceito de movimento perpétuo se aplique a tudo hoje e no futuro.
As pessoas ainda formam conceitos para os quais não há nenhum objeto. Por exemplo,
muitas pessoas têm um conceito sobre um companheiro perfeito, ainda que não haja ne-
nhum objeto que tenha passado no teste deste conceito.
CAP. 11 O MODELO 179

Conceitos e Modelo Orientado a Objeto


Por definição, uma idéia ou compreensão particularmente mantida é denominada con-
cepção. Quando essa idéia ou esse entendimento é compartilhado com outros, ela torna-
se um conceito. Para nos comunicarmos com os outros, devemos compartilhar as
concepções individuais que sustentamos e chegar a conceitos mutuamente acordados.
Por exemplo, se a sua concepção de carro é unicamente um Lamborghini prateado 1970
e a concepção de carro de sua esposa for uma station wagon, provavelmente você dese-
jará chegar a um comum acordo antes da família sair à procura de um carro novo.
O processo da análise orientada a objeto é realmente o processo de capturar um con-
junto de conceitos compartilhados entre especialistas em domínios, usuários, gerentes e
desenvolvedores. Esses conceitos constituem os alicerces de qualquer processo organiza-
cional, definem uma realidade organizacional compartilhada e formam a base para uma
linguagem organizacional utilizada para fins de comunicação.
Para que especifiquemos estes conceitos, o paradigma orientado a objeto tem os se-
guintes mecanismos básicos: classe, associação, agregação, generalização/especialização,
polimorfismo e instanciamento. Em capítulos posteriores, acrescentaremos mecanismos
para suportar conceitos que tratem do comportamento dinâmico e de regras. Pelo fato de
termos uma tecnologia vívida, estão sendo desenvolvidos mais mecanismos para prover
um melhor suporte aos conceitos de modelagem necessários para auxiliar-nos a adminis-
trar melhor a complexidade. Nesta seção do livro revisaremos esses mecanismos básicos.

Classe
Uma classe descreve um grupo de objetos com atributos idênticos, comportamentos e re-
lacionamentos comuns (vínculo e agregação) e semântica comum. Exemplos de classes
são pessoa, funcionário, planilha de tempo, empresa e departamento. Cada objeto em
uma classe terá os mesmos atributos e padrões de comportamento. A maioria dos objetos
obtém sua individualidade tendo diferentes valores para seus atributos e diferentes obje-
tos em seus relacionamentos. Entretanto, objetos com valores de atributo idênticos e/ou
objetos relacionais idênticos são permitidos.
A chave importante para que dois objetos estejam na mesma classe é que eles com-
partilhem um propósito semântico comum no domínio de aplicação, além do fato de pos-
suírem os mesmos atributos, comportamentos e relacionamentos. Por exemplo, considere
um silo de cereais e uma vaca. Se eles fossem objetos em uma aplicação financeira, os úni-
cos dois atributos de importância poderiam ser idade e custo, e ambos, o silo e a vaca,
poderiam na realidade estar na mesma classe relativa aos bens da fazenda. Todavia, se a
aplicação fosse uma relativa à agricultura, então é improvável que o silo e a vaca estives-
sem na mesma classe. Portanto, interpretar a semântica depende da aplicação e é algo a
ser julgado pelo especialista em domínios.

Associação
Uma associação descreve um grupo de vínculos com estrutura e semântica comuns. Uma
associação é uma maneira de capturar os vínculos entre objetos em um modo represen-
tativo via suas classes (tipos de objetos). Uma associação descreve um conjunto de vín-
culos potenciais da mesma forma que uma classe descreve um conjunto de objetos
potenciais. Na modelagem orientada a objeto, todos os vínculos e, portanto, todas as as-
sociações são consideradas como bidirecionais. Uma vez que seja estabelecido um víncu-
lo entre dois objetos, cada objeto poderá requerer os serviços do outro objeto. Entretanto,
180 UML E C++ CAP. 11

o uso apropriado da associação deverá requerer que a associação (ou seja, os vínculos)
seja utilizada somente para acessar serviços consistentes com o significado semântico da
associação. Na teoria, uma associação pode ser binária (entre duas classes), ternária (entre
três classes) ou de ordem superior. Na prática, a maioria das associações é binária.

Agregação de Classe
Uma agregação de classe descreve um grupo de agregações de objetos com estrutura e se-
mântica comuns. Dessa maneira, a agregação de classe é um modo de capturar as agre-
gações de objetos entre objetos de um modo significativo por meio de suas classes (tipos
de objetos). Exemplos de agregação são pedido de compra, com seus itens de linha asso-
ciados e uma planilha de tempo com seus registros de horas associados. Embora muitas
agregações sejam implementadas como unidirecionais, elas poderiam ser consideradas
bidirecionais na etapa da análise.
Uma agregação pode ser estática ou dinâmica, e um componente de um agregado
pode também ser condicional. Uma agregação estática tem componentes fixos que não po-
dem ser mudados. Uma agregação dinâmica tem componentes que podem variar durante
o tempo. Um componente condicional pode ser ou não componente de um agregado, de-
pendendo se reter ou não uma condição específica.
A agregação tornou-se um mecanismo tão útil para a análise que foram identifica-
das sete espécies de agregação. Elas são descritas a seguir:
■ Conjunto-partes (composição componente-todo)
■ Composição material-objeto
■ Composição porção-objeto
■ Composição local-área
■ Composição conjunto-membros
■ Recipiente-conteúdo (composição membro-grupo)
■ Composição membro-parceria
Elas foram descritas no Capítulo 9.

Generalização/Especialização
A generalização é um mecanismo de abstração para o compartilhamento de similaridades
entre classes, muito embora sejam preservadas suas diferenças2. Generalização é o rela-
cionamento entre uma classe e uma ou mais versões refinadas dessa classe. A classe que
está sendo refinada é chamada superclasse3, e cada versão refinada é denominada subclas-
se4. Por exemplo, uma aeronave tem um fabricante, número de identificação, peso e um
custo. Um helicóptero, que além disso tem hélices, e um caça a jato, que tem em especial
mísseis, são versões refinadas de uma aeronave.
A generalização nos fornece a capacidade de definir as características de uma aero-
nave somente uma vez e, então, apenas acrescentar as novas propriedades para helicóp-
tero e caça a jato. Em nosso exemplo, a aeronave é a superclasse, e o helicóptero e o caça
a jato são as subclasses.

2. Na maioria das linguagens orientadas a objeto, inclusive C++, isto é implementado utilizando-se
herança.
3. Chamada de classe-base em C++.
4. Chamada de classe derivada em C++.
CAP. 11 O MODELO 181

Atributos, relacionamentos e serviços com o mesmo significado semântico são atri-


buídos para a superclasse e herdados pelas subclasses. Cada subclasse herda todos os
atributos, serviços e relacionamentos da superclasse. Por exemplo, o caça a jato herda os
atributos, o fabricante, o número de identificação, o peso e o custo da aeronave. Ele herdou
os atributos, e não os valores. O caça a jato deve determinar seus próprios valores para esses
atributos. A generalização é comumente denominada relacionamento “é_um” porque
cada instância de uma subclasse é também uma instância da superclasse.
A generalização é transitiva ao longo de qualquer número de níveis de generaliza-
ção. O termo ancestrais refere-se à generalização de classes ao longo de múltiplos níveis.
Uma instância de uma subclasse é simultaneamente uma instância de todas as suas clas-
ses ancestrais. Além do mais, a subclasse inclui valores para qualquer atributo de toda
classe ancestral e objetos relacionais para cada relacionamento em suas classes ancestrais.
E mais, todos os serviços e métodos associados de todas as classes ancestrais podem ser
aplicados à subclasse.
Cada subclasse não apenas herda todos os atributos mencionados, mas normalmen-
te acrescenta atributos específicos, relacionamentos (associações e talvez agregações) e
serviços a seus métodos associados também. Em nosso exemplo, o caça a jato acrescentou
mísseis como um atributo e, provavelmente, dispararMíssil como um serviço. Esses atri-
butos e serviços não são repartidos com uma outra aeronave.

Polimorfismo
Um dos objetivos da tecnologia OO é o de reutilizar código; a generalização é um dos
mais efetivos veículos para facilitar a reutilização de código. Entretanto, alguns métodos
talvez necessitem ser personalizados para satisfazer as demandas de negócio. Quando
essa personalização for requerida para uma subclasse, a tecnologia orientada a objeto tem
um mecanismo, denominado polimorfismo, no qual a subclasse pode ter um método (com-
portamento) que substitua o método de sua superclasse para um serviço específico. En-
tão, quando esse serviço for solicitado por uma instância da subclasse, o método da
subclasse é invocado. Entretanto, quando o serviço for solicitado por outras instâncias
(supondo-se que nenhuma outra subclasse tenha igualmente feito uma substituição para
este serviço), será invocado o método da superclasse.
Considere este exemplo simples. Contamos com uma classe Funcionário que tem
uma subclasse Executivo. Partindo-se de uma perspectiva de modelagem, um executivo
é também um funcionário. Um dos serviços que se aplica a todos os funcionários é au-
mentoDeSalário. Para todos os funcionários, o aumento de salário é o salário do funcio-
nário multiplicado pela inflação anual. Essa tem sido a política da empresa nos últimos
dez anos. Com a generalização, isso tem funcionado muito bem. A cada ano, na época do
aumento, o serviço aumentoDeSalário é invocado para todos os funcionários, inclusive os
executivos. Muito embora executivos sejam funcionários, os diretores da organização de-
cidem que os aumentos de salários dos executivos deverão ser calculados diferentemente
dos restantes dos funcionários. Fica decidido que os aumentos de salários dos executivos
será cinco vezes a taxa anual de inflação acrescida de um bônus de 15% da receita bruta.
Que mecanismo a tecnologia orientada a objeto tem para lidar com essa situação?
Nessa situação, a subclasse “executivo” pode ter um método que substitua o método de
aumentoDeSalário do funcionário todas as vezes que este serviço for solicitado para um
executivo. Embora esses métodos sejam diferentes, eles cumprem o mesmo objetivo de
negócio (têm a mesma semântica de aumentoDeSalário). Esse fenômeno é conhecido
como polimorfismo. O método que é invocado depende da classe do objeto. Dessa maneira,
182 UML E C++ CAP. 11

o exemplo do funcionário e do executivo pode ser capturado transformando-se o serviço


aumentoDeSalário em polimórfico.5

Instanciamento
Instanciamento é um mecanismo no paradigma orientado a objeto em que podemos criar
instâncias de uma classe. Essas instâncias (objetos) são os mantenedores dos dados que
farão nossa aplicação/sistema funcionar. Este mecanismo é um dos veículos que utiliza-
mos para tornar nosso modelo dinâmico.

Documentando Conceitos pelo Uso da UML


Nos capítulos anteriores, mostramos informalmente como documentar algumas das cons-
truções discutidas na seção anterior. Agora, revelaremos algumas dessas construções de
uma forma mais completa e formal.

Conceito de Classe
Na UML, existem quatro construções que podem ser utilizadas para descrever o conceito
de classe:
■ Construção da classe básica e do objeto
■ Construção da classe parametrizada e da classe associada
■ Construção da interface
Construção da Classe Básica. Na UML, o ícone para uma classe ou um objeto é um
retângulo sólido contendo o nome da classe (objeto)6. O retângulo pode ser dividido
em três7 compartimentos para identificar mais do que apenas o nome da classe. O
compartimento superior exibe o nome da classe. O compartimento central exibe os
atributos. Um atributo pode ser documentado em diversos níveis de inteireza: o
nome do atributo; o nome e tipo do atributo; e o nome, tipo e valor padrão do atri-
buto. O compartimento inferior exibe as operações. Uma operação é documentada
especificando-se sua assinatura, que abrange o nome, tipo e valor padrão de todos
os parâmetros, e seu tipo de retorno (se ela for uma função). Como uma classe pode
aparecer em muitos diagramas diferentes, não é necessário mostrar todos os atribu-
tos e operações todas as vezes que ela aparecer. Em alguns casos, provavelmente
seja sensato mostrar unicamente um subconjunto deles. Um compartimento vazio
não implica inexistência de atributos ou operações, mas só que eles não foram iden-
tificados naquele particular diagrama. Podem-se utilizar reticências (“...”) para de-
notar a existência de entradas que não foram mostradas nesse particular diagrama.
Um exemplo de representação de classes (com e sem atributos e operações) é mos-
trado na Figura 11.1.

5. Observe que o nome e a assinatura do serviço são preservados. Isto difere da sobreposição da função
C++ na qual o nome da função ou do operador é reutilizado, mas os argumentos são diferentes.
Portanto, a sobreposição de funções não é um veículo para implementar polimorfismo em C++. O
polimorfismo é implementado em C++ pela utilização de funções virtuais.
6. De modo que se distinga entre classe e objeto, os objetos têm seus nomes sublinhados.
7. Uma característica raramente utilizada da UML é a presença de um quarto compartimento para fins
de documentação de responsabilidades.
CAP. 11 O MODELO 183

FIGURA 11.1 Expressando informações de uma classe na UML.

Construção da Classe Parametrizada e da Classe Associada. A UML pode igual-


mente lidar com classes parametrizadas (templated) e classes associadas (bound). Classes
parametrizadas são aquelas que definem uma classe básica na qual os tipos de da-
dos de um ou mais dos atributos são parâmetros da definição da classe. Uma classe
associada é criada quando os parâmetros de uma classe parametrizada estão asso-
ciados a um tipo de dado. A notação é apresentada na Figura 11.2.

FIGURA 11.2 Classe parametrizada da UML e uma classe associada.

Construção da Interface. A UML ainda suporta a representação de interfaces. As in-


terfaces podem ser classes que servem como proxies ou stubs, ou podem ser classes
abstratas puras utilizadas para separar a interface da implementação. Existem duas
representações básicas, uma em que a interface é mostrada como um círculo ligado
à classe de implementação e a outra na qual a classe da interface é documentada uti-
lizando-se o ícone de classe padrão com o protótipo <<interface>> especificado. A
classe de implementação é mostrada conectada à interface com uma linha pontilha-
da terminando com um semitriângulo apontando para a interface. Os exemplos de
representação de interfaces são apresentados na Figura 11.3.

FIGURA 11.3 Diagramas da UML para a representação de interfaces.

Associação
Na UML, uma associação é documentada por uma linha traçada entre as classes que dela
participam (veja a Figura 11.4). Centralizado e acima da linha está o nome da associação.
184 UML E C++ CAP. 11

Nas extremidades da linha, os papéis que os objetos (da classe) detêm dentro da associa-
ção são identificados por nomes. Os nomes dos papéis podem aparecer acima ou abaixo
da linha. A multiplicidade do papel é identificada pelo marcador apropriado na extremi-
dade da linha. Marcadores apropriados são mostrados na Figura 11.4. A Relação 1 apre-
senta uma associação na qual a Classe 1 tem uma multiplicidade de 0 a muitos e a Classe
2 tem uma multiplicidade de 1. A Relação 2 apresenta uma associação na qual a Classe 1
tem uma multiplicidade de 1 a muitos e a Classe 2 tem uma multiplicidade de 0 a 1. A
Relação 3 apresenta uma associação na qual a Classe 1 tem uma multiplicidade de muitos
enquanto que a multiplicidade da Classe 2 não é especificada. Nesse caso, pode-se assu-
mir geralmente uma multiplicidade de 1. Uma associação qualificada tem uma caixa na
extremidade da linha identificando o qualificador. Isso é apresentado na Relação 4 na fi-
gura. Presume-se que as associações sejam bidirecionais, significando que elas podem ser
navegadas (passadas) em qualquer direção. Em alguns casos, a associação somente pode
ser navegada (passada) em uma única direção. A UML denota isso utilizando uma linha
com uma cabeça de seta indicando o sentido da passagem. Isso é apresentado pela Rela-
ção 5 na figura. A Relação 6 mostra uma relação de dependência. A UML também provê
um mecanismo para associar atributos com uma associação. Isso é conseguido ligando
uma classe de associação à linha de relação por meio de uma linha tracejada. Isso é apre-
sentado na Relação 7 na mesma figura.

FIGURA 11.4 Notações da UML para associações.

Agregação de Classe
A UML distingue entre dois tipos diferentes de agregações (agregação e composição),
conforme apresentado na Figura 11.5. O conceito da UML de agregação captura a idéia
de parte-todo. Na UML, a agregação é ilustrada como um diamante vazio na extremidade
que constitui o todo. O conceito da UML de composição é que a parte-objeto pode so-
CAP. 11 O MODELO 185

mente pertencer a um todo e que normalmente espera-se que as partes vivam ou morram
com o todo. Na UML, a composição é mostrada como um diamante cheio na extremidade
que constitui o todo. Marcadores de multiplicidade podem ser empregados nas várias ex-
tremidades da agregação. Esses marcadores são os mesmos daqueles utilizados nas
associações.

FIGURA 11.5 Notação da UML para agregação e composição.

Generalização/Especialização
A generalização/especialização de classes é documentada na UML utilizando-se uma li-
nha com um triângulo vazio apontando para a generalização, conforme mostra a Figura
11.6. No exemplo, a Classe 1 é a superclasse da Classe 2. Deve ser notado que a UML pos-
sibilita que se documente herança múltipla ao estabelecer múltiplos relacionamentos de
generalização/especialização desde uma subclasse comum ou de diferentes classes.

FIGURA 11.6 Notação da UML para generalização/especialização.

Polimorfismo
Na UML, o polimorfismo é documentado mostrando-se o serviço (operação), tanto na su-
perclasse como na subclasse.

Instanciamento
A UML permite a documentação de objetos e classes. Um objeto é denotado utilizando-se
a notação de classe com o nome do objeto sublinhado. O nome do objeto é denotado por
um rótulo separado do nome da classe por dois pontos. Exemplo disso é ilustrado na Fi-
gura 11.7. Se uma classe cria instâncias de outra classe, isto pode ser mostrado pelo uso
de uma seta tracejada indicando a dependência de instanciamento entre a classe e uma
instância.

FIGURA 11.7 Notação da UML para um objeto.


186 UML E C++ CAP. 11

Refinando o Modelo
As etapas de refinamento são as seguintes:
1. Agrupe todos os objetos com os mesmos atributos, relacionamentos similares e
comportamentos comuns. Se existir um conceito dentro do domínio de aplicação
que defina esses objetos, utilize esse nome para a classe. Em caso negativo, per-
gunte ao especialista em domínios com qual conceito isso poderia se assemelhar.
Utilize a classe recomendada pelo especialista e reagrupe seus objetos.
2. Agrupe os vínculos e agregações de objetos em associações e agregações de clas-
ses. Lembre-se de que todos os membros do grupo devem portar o mesmo do-
mínio semântico.
3. Determine se as classes são especializações de uma superclasse comum. Procure
por atributos idênticos, relacionamentos, serviços e comportamentos comuns ao
longo das classes e, com eles, tente formar uma classe. Novamente, pergunte ao
especialista em domínios se essas propriedades capturam um conceito de domí-
nio proveitoso. Lembre-se de que esses novos objetos devem operar em um do-
mínio semântico útil.
4. Procure por polimorfismo. Observe se existem serviços de objetos que sejam os
mesmos ou similares, mas difiram quanto ao comportamento (de que forma é
provido o serviço). Se esses objetos operam no mesmo domínio semântico, faça
com que o serviço seja polimorfo. Realize todas as etapas precedentes mais uma
vez até que não sejam mais encontradas novas superclasses.

Subsistemas
Ao construir aplicações ou sistemas extensos, o analista/desenvolvedor tem de lidar com
vários temas diferentes e interessantes. Por exemplo, em uma aplicação típica, temos os
seguintes tópicos: a aplicação, a interface dos sistemas externos, a interface do usuário, o
subsistema de alarmes e o subsistema de entrada no sistema (logging).8 Isso normalmente
é material demais para que possamos tratá-los como um todo. Conseqüentemente, neces-
sitamos de uma estratégia para a organização desses diferentes tópicos em subsistemas
mais facilmente gerenciáveis. A estratégia ou técnica que utilizaremos é baseada no tra-
balho de Shlaer e Mellor. A estratégia deles baseia-se no conceito de domínio.

Domínio
Um domínio é um mundo real, hipotético ou abstrato em separado, habitado por um con-
junto distinto de objetos que se comportam segundo regras e diretrizes que caracterizam
o domínio. Por exemplo, um domínio de Gerenciamento da Companhia Aérea estaria re-
lacionado com aviões, rotas de aviões, aeroportos e portões, bem como com políticas ope-
racionais e normas do Departamento Nacional de Aviação Civil governando sua
utilização. Entretanto, o domínio da Interface do Usuário estaria relacionado com janelas,
menus descendentes de opções, caixas de diálogo e ícones, juntamente com um conjunto
diferente de políticas operacionais.

8. Em desenho, acrescentamos o subsistema de telas, subsistema de banco de dados etc. O sistema


operacional, linguagens de programação, pacotes de software e meio de desenvolvimento são todos
considerados como parte do desenho.
CAP. 11 O MODELO 187

Cada domínio forma um todo separado, mas coesivo. O princípio de coesão contri-
bui para mantermos idéias intimamente correlacionadas juntas, e idéias não-correlaciona-
das, separadas. Pelo fato de um domínio representar um conjunto de objetos, regras e
diretrizes intimamente ligados, ele pode ser tratado como uma unidade (subsistema) para
fins de análise.
Para entendermos melhor o conceito de domínio, examinemos este conceito em ter-
mos de objetos:
1. Um objeto é definido em um domínio.
2. O objeto em um domínio requer a existência de outros objetos no mesmo domí-
nio.
3. Os objetos em um domínio não requerem a existência de objetos em um diferen-
te domínio.
Por exemplo, consideremos uma aplicação de gerenciamento de uma companhia aé-
rea. (1) A rota aérea deveria estar somente no domínio de Gerenciamento da Companhia
Aérea. (2) A rota aérea em seu próprio termo não tem muita utilidade para nós sem
aviões e aeroportos. (3) As rotas aéreas e os aviões podem existir sem janelas ou caixas
de diálogo. De modo inverso, as janelas e ícones podem existir sem rotas aéreas e aviões.
Ainda que um objeto em um domínio não requeira a existência de um objeto em
um outro domínio, é muito comum que um objeto em um domínio tenha uma instância
equivalente em outro domínio. Por exemplo, um avião no domínio de Gerenciamento da
Companhia Aérea poderá ter um ícone de avião equivalente no domínio da Interface do
Usuário.
Para auxiliar no reconhecimento de domínios, Shlaer e Mellor têm nos dotado de
um esquema de classificação:9
1. Domínio de aplicação. Este é o tema partindo-se da perspectiva do cliente/usuário.
Isto é o que denominamos análise de requisitos do negócio.
2. Domínio de serviço. Este domínio provê mecanismos e funções de utilidade gené-
rica para suporte do domínio de aplicação. Estes são os domínios que temos di-
ficuldade em identificar.
3. Domínio de arquitetura. Este domínio provê mecanismos e estruturas genéricos
para administração de dados e controle para o sistema como um todo.
4. Domínio de implementação. Este domínio inclui a linguagem de programação, os
sistemas operacionais, redes e bibliotecas de classes comuns.

Ponte
De acordo com Shlaer e Mellor, existe uma ponte (vínculo) entre dois domínios quando
um domínio precisa utilizar os mecanismos e/ou recursos providos pelo outro domínio.
O domínio que requer os recursos é conhecido como o cliente (client), enquanto que o do-
mínio que os provê é denominado servidor (server). Por exemplo, o domínio de Geren-
ciamento da Companhia Aérea (cliente) poderá utilizar o domínio da Interface do
Usuário para exibir as rotas aéreas ao usuário. Durante a análise, o vínculo define um
conjunto de serviços externos (partindo da perspectiva do cliente) e um conjunto de re-
quisitos (partindo da perspectiva do servidor). Por exemplo, no nosso caso do avião, o

9. Na análise, estamos preocupados somente com os Domínios de Aplicação e de Serviço. Em desenho,


os Domínios de Arquitetura e de Implementação são considerados
188 UML E C++ CAP. 11

ícone do avião poderá ser capaz de derivar sua posição desde a posição do objeto avião
no domínio do Gerenciamento da Companhia Aérea.

Organizando Subsistemas
De acordo com Rumbaugh e outros pesquisadores (1991), a decomposição de um sistema
em subsistemas poderá ser feita tanto horizontal como verticalmente. Muito embora mui-
tos de nós tenhamos numerosas formas de decompor um sistema, qualquer decomposi-
ção reduz a um ou outro tipo, ou a uma combinação entre esses dois tipos.

Camadas Horizontais
Um sistema de camadas é um conjunto de domínios semânticos (realidade virtual), com
cada um deles construído em termos dos que se encontram abaixo e provendo a base de
implementação para os que se encontram em um nível superior. Exemplos desta aborda-
gem são as camadas de protocolo de camadas OSI e TNM para sistemas de suporte ope-
racional em telecomunicações. Para a tecnologia não aplicada a telecomunicações, um
sistema de gráfico interativo é um outro exemplo. Aqui, janelas são formadas a partir de
telas, que, por sua vez, são formadas a partir de pixels atuando em algum dispositivo de
entrada e saída. As camadas são o domínio da aplicação, o domínio da janela, o domínio
da tela, o domínio do pixel e o domínio do hardware.
O objetivo é tornar cada camada a mais independente possível. Embora haja nor-
malmente alguma correspondência entre objetos em diferentes camadas, a comunicação
entre camadas é basicamente de uma via. Um subsistema sabe sobre as camadas abaixo
dele10, mas não tem qualquer conhecimento sobre as camadas acima. Portanto, existe um
relacionamento cliente-servidor entre as camadas.11
Geralmente, apenas a camada superior, que corresponde ao domínio da aplicação,
e a camada inferior, que corresponde ao domínio do hardware, são especificadas no do-
cumento de requisitos. Uma das finalidades da análise é encontrar todas as camadas in-
termediárias. Constitui boa prática dispor de pelo menos uma camada (domínio de
serviço) entre a camada da aplicação e a camada do hardware, pois isso facilitará a utili-
zação em outras plataformas de hardware/software.

Partições Verticais
As partições verticais dividem um sistema em diversos subsistemas fracamente acopla-
dos,12 cada um dos quais provê um tipo de serviço. Por exemplo, considere um sistema
computadorizado de gerenciamento de trabalho para os funcionários da manutenção. Po-
derá haver um subsistema em separado para o trabalho de rotina, diagnóstico de proble-
mas, relatórios de tempo e administração de salários. Existe somente um acoplamento

10. Em alguns paradigmas, a comunicação poderá ser somente com a camada imediatamente abaixo.
Essa restrição preservará os princípios de ocultação de informações e de encapsulamento entre
camadas e fará com que o software seja mais fácil de ser mantido, pois o desenhista somente precisa
verificar a camada abaixo dele. Entretanto, na prática, isso é por demais restritivo. Durante o
desenho, as considerações de desempenho normalmente nos forçam a possibilitar que a camada
superior acesse todos os serviços em qualquer camada de nível mais baixo.
11. As camadas superiores são os clientes das camadas inferiores.
12. Se os subsistemas são independentes entre si, então é mais efetivo considerá-los como sistemas em
separado.
CAP. 11 O MODELO 189

muito fraco entre esses subsistemas. O trabalho rotineiro é unicamente acoplado aos re-
latórios de tempo quanto ao número de horas trabalhadas. A administração de salários
utiliza os relatórios de tempo para determinar o quanto deve ser pago; entretanto, ela não
tem qualquer acoplamento com o trabalho de rotina nem com o diagnóstico de erros.

Combinação
Um sistema pode ser decomposto sucessivamente em subsistemas com o uso de partições
verticais e camadas horizontais em várias combinações. As camadas horizontais podem
ser particionadas e as partições verticais podem ser dispostas em camadas. A maioria dos
sistemas de grande porte requer este tipo de mistura.

Identificando Subsistemas
Para a identificação de subsistemas utilizaremos o fato de que deverá existir acoplamento
entre objetos no mesmo domínio e fraco acoplamento pelos domínios. Se desenharmos
um modelo que capture somente as associações e agregações de classes, descobriremos um
agrupamento (clustering) de classes. Utilizaremos cada agrupamento como um subsiste-
ma em potencial. Para auxiliar-nos a determinar se o agrupamento é um subsistema,
Shlaer e Mellor têm as seguintes sugestões:
1. Dar um nome ao domínio e preparar uma declaração de missão para ele.
2. Encontrar as pontes (vínculos) (serviços a outros subsistemas) para o domínio.
3. Verificar se esses serviços são consistentes com a declaração de missão.
4. Determinar se é possível substituir esse conjunto de objetos por um conjunto di-
ferente de objetos com a mesma missão.
Se todas essas condições forem verdadeiras, o agrupamento será um subsistema. Se
você encontrar vários relacionamentos entre subsistemas definidos entre os mesmos dois
subsistemas, um agrupamento talvez tenha sido dividido impropriamente. Examine mais
uma vez suas definições de classe para ver se é possível redefinir as classes para tornar
os agrupamentos mais comportados.

Documentando Subsistemas
Os subsistemas podem ser documentados na UML pela utilização de pacotes. Um dia-
grama de pacote identifica uma reunião de elementos de software, tipicamente uma co-
leção de classes. A UML utiliza uma pasta estilizada para representar um pacote. Um
diagrama de pacote é apresentado na Figura 11.8. Nesse diagrama, os subsistemas de
contabilidade e dos clientes comunicam-se entre si conforme indicado pela linha com seta
dupla entre eles. O pacote Banco existe em uma camada acima dos pacotes Contabilidade
e Cliente. O pacote Banco comunica-se com os pacotes abaixo dele, mas esses não podem
fazer solicitações a ele, conforme evidenciado pelas setas indicativas de um único sentido.
190 UML E C++ CAP. 11

FIGURA 11.8 Diagrama de pacote ilustrando interações entre subsistemas.

Abordagem Recomendada
Nossa abordagem recomendada consiste nas seguintes etapas:
1. Agrupe todos os objetos com os mesmos atributos, relacionamentos similares e
comportamentos comuns. Se existir um conceito dentro do domínio de aplicação
que defina esses objetos, utilize esse nome para a classe. Em caso negativo, per-
gunte ao especialista em domínios com qual conceito isso poderia se assemelhar.
Utilize a classe recomendada pelo especialista e reagrupe seus objetos.
2. Agrupe os vínculos e agregações de objetos em associações e agregações de clas-
ses. Lembre-se de que todos os membros do grupo devem portar o mesmo do-
mínio semântico.
3. Determine se as classes são especializações de uma superclasse comum. Procure
por atributos idênticos, relacionamentos, serviços e comportamentos comuns ao
longo das classes e, com eles, tente formar uma classe. Novamente, pergunte ao
especialista em domínios se essas propriedades capturam um conceito de domí-
nio proveitoso. Lembre-se de que esses novos objetos devem operar em um do-
mínio semântico útil.
4. Procure por polimorfismo. Observe se existem serviços de objetos que sejam os
mesmos ou similares, mas difiram quanto ao comportamento (de que forma é
provido o serviço). Se esses objetos operarem no mesmo domínio semântico,
faça com que o serviço seja polimorfo. Realize todas as etapas precedentes mais
uma vez até que não sejam mais encontradas novas superclasses.
5. Desenhe um modelo que capture somente as associações e agregações de classes.
6. Identifique o agrupamento de classes e suponha que eles sejam subsistemas po-
tenciais.
7. Dê a esse agrupamento um nome e uma declaração de missão.
8. Utilize a ponte (vínculo) e o teste de substituição para determinar se porventura
trata-se de um subsistema (domínio separado).

Exemplo
Retornemos ao nosso exemplo do corte de grama.
CAP. 11 O MODELO 191

Refinamento
Nesta seção, refinaremos nosso modelo em um domínio semântico: serviços domésti-
cos13. Baseado na análise, temos os seguintes objetos: John, Jane, Peter, Paul, Elizabeth,
Mary, Jack, GramadoDaFamília e CortadorDeGramaProfissional.
Iniciaremos tentando reunir os objetos. Primeiro, Paul, Elizabeth e Mary são objetos
que têm os mesmos atributos, relacionamentos similares e comportamentos comuns. Jack
e CortadorDeGramaProfissional também aparentam estar na mesma classe. De fato, Cor-
tadorDeGramaProfissional é uma classe, e Jack é uma instância desta classe.
Embora tenhamos identificado CortadorDeGramaProfissional como um objeto, ele
é também uma classe. Essa é uma das dificuldades em se ler um documento de requisitos. Em
um emprego, o nome de uma classe é utilizado para referir a ele próprio como um objeto
e, em outra utilização, esse nome é utilizado para referir-se a si próprio como um conjun-
to de instâncias de um tipo de objeto. Agora é possível entender o problema com a etapa
1; quando são listados objetos, também são listadas classes.14 Nossa primeira redução
quanto à listagem de objetos é mostrada na Figura 11.9.
Se revisarmos todas as classes relacionadas na Figura 11.8, notaremos que a classe
FilhoA e a classe FilhoB são praticamente idênticas. A diferença é que as duas têm mé-
todos diferentes para o serviço de “corte de grama”. Em virtude de o significado semân-
tico e a assinatura do serviço serem os mesmos, esta situação é mais bem capturada pelo
uso de polimorfismo. Portanto, podemos criar uma superclasse Filho para as subclasses
FilhoA e FilhoB. Esse modelo é adequado se supormos que lidamos somente com uma
geração de uma família e ignorarmos o fato de que um cortador de grama profissional
pode também estar em uma família. Este modelo mais flexível e preciso é deixado como
um exercício ao leitor. O modelo mostrado nas Figuras 11.10, 11.11 e 11.12 é adequado
para nossa aplicação limitada. As descrições de classe são apresentadas nas Figuras 11.13,
11.14, 11.15, 11.16 e 11.17.

Classe Instância(s)

Família Jones
CortadorDeGramaProfissional Jack
FilhoA Mary, Elizabeth, Paul
FilhoB Peter
Pai John
Mãe Jane
Gramado GramadoDaFamília

FIGURA 11.9 Lista de instâncias de classe iniciais para o exemplo do corte de grama.

13. As etapas de identificação de subsistemas são mostradas no estudo de caso.


14. Isto não pode estar errado, pois toda classe é também um objeto. Entretanto, o inverso não é
verdadeiro (ou seja, existem objetos que não são classes).
192 UML E C++ CAP. 11

FIGURA 11.10 Diagrama de generalização de classes para o exemplo do corte de grama.

FIGURA 11.11 Diagrama de agregação de classes para o exemplo do corte de grama.

FIGURA 11.12 Diagrama de associação de classes para o exemplo do corte de grama.


CAP. 11 O MODELO 193

NOME DA CLASSE Pai

ATRIBUTOS
programação, minhacondição

SERVIÇOS
protótipo: cortar_a_grama (sem argumentos)
{
colocar PROGRAMAÇÃO para o período noturno (19h00 — 21h00)
Next, se PROGRAMAÇÃO tiver endereço de memória aberto,
then:
colocar cortarGramadoDaFamília em endereço de memória.
return “Sim, eu cortarei a grama nesta noite”.
else:
return “Não, eu não posso cortar o gramado”.
endif:
}
protótipo: cortarGramadoDaFamília (sem argumentos)
{
Se MINHACONDIÇÃO é igual a “cansado”,
then: // obter filhos via associação pai/filho
para cada filho em Filhos Selecionados
solicitar a cada filho para “cortar a grama” por US$ 5.
if resposta é “sim”,
then:
remover “cortarGramadoDaFamília” de PROGRAMAÇÃO.
return;
else:
endif:
endfor:
executar corte da grama.
else:
executar corte da grama.
endif:
}

FIGURA 11.13 Exemplo de um cartão de classe CRC para o corte de grama.


194 UML E C++ CAP. 11

NOME DA CLASSE Família

ATRIBUTOS

nenhum

SERVIÇOS

construtor criará a família (veja capítulo posterior)

FIGURA 11.14 Exemplo de um cartão de classe CRC para o corte de grama.

NOME DA CLASSE Filho

SUBCLASSES FilhoA, FilhoB

ATRIBUTOS

programação

SERVIÇOS

protótipo: cortar_a_grama (endereço_do_gramado, quantia_em_dólares)


// não há método especificado, requereremos com isso que cada
// subclasse especifique um método para prover este serviço.

FIGURA 11.15 Exemplo de um cartão de classe CRC para o corte de grama.

Neste exemplo, introduzimos uma segunda forma de polimorfismo. Em um exem-


plo dado anteriormente, a superclasse Funcionário tinha um método especificado para o
serviço aumentoDeSalário; a subclasse Executivo então especificou seu próprio método
para aumentoDeSalário. Entretanto, nós poderíamos ter adicionado uma outra subclasse,
Supervisores, que não especifica seu próprio método para aumentoDeSalário. Em tal
evento, instâncias de Supervisores utilizarão o método especificado na superclasse Funcio-
nário. Aqui, a superclasse Filho define o protótipo para o serviço cortar_a_grama, mas
não especifica um método para a realização do serviço. Quando isso é feito, cada subclas-
se deverá especificar seu próprio método para poder manipular o serviço. Não há qual-
quer especificação padrão na superclasse. As subclasses FilhoA e FilhoB devem ter
métodos para o serviço de corteDeGrama.
É preciso também reconhecer as diferenças entre FilhoA e FilhoB, ou seja, eles têm
métodos diferentes para a implementação do serviço corteDeGrama, e que somente Fi-
lhoA tem uma associação com CortadorDeGramaProfissional. O restante das descrições
de classe é deixado como um exercício ao leitor.
CAP. 11 O MODELO 195

NOME DA CLASSE FilhoB

SUPERCLASSE Filho

ATRIBUTOS

sem atributos adicionais; lembre-se, ela herdará programação de filho

SERVIÇOS

protótipo: cortar_a_grama (endereço_do_gramado, quantia_em_dólares)

colocar PROGRAMAÇÃO para o período noturno (19h00 — 21h00)

Next, se PROGRAMAÇÃO tiver endereço de memória aberto,

then:

colocar corteDeGrama em endereço de memória.

associar ENDEREÇO_DO_GRAMADO com corteDeGrama.

return “Sim, eu cortarei a grama nesta noite”.

else:

return “Não, eu não posso cortar o gramado”.

endif:

FIGURA 11.16 Exemplo de um cartão de classe CRC para o corte de grama.

Subsistemas
Desde o Capítulo 4, e incluindo nosso trabalho de refinamento, nossa ênfase tem sido mo-
delar o domínio semântico: serviços domésticos. Dessa maneira, estamos focalizando os
objetos que suportam o sistema na realização dos casos de uso Verificar Condições de
Trabalho e Adiantar Simulação Uma Unidade de Tempo. Esses casos de uso são impeli-
dos respectivamente pelo Daemon de Eventos Assíncronos e pelo Relógio de Simulação.
O resultado é um subsistema, o Modelo do Corte de Grama, que é uma versão especiali-
zada de Serviços Domésticos.
Entretanto, nosso modelo não é completo. Primeiramente, não incorporamos a ele a
geração de eventos para o observador, que olha a simulação conforme ditado pelo caso
de uso Adiantar Simulação Uma Unidade de Tempo. Além do mais, existem dois outros
casos de uso, Registrar-se à Informação de Simulação e Definir Parâmetros de Simulação,
que são propulsionados, respectivamente, pelo observador e pelos atores diretores, dos
quais absolutamente ainda não tratamos.
196 UML E C++ CAP. 11

SUPERCLASSE Filho
NOME DA CLASSE FilhoA

ATRIBUTOS
sem atributos adicionais; lembre-se, ela herdará programação de filho

SERVIÇOS
protótipo: cortar_a_grama (endereço_do_gramado, quantia_em_dólares)
{
if QUANTIA_EM_DÓLARES for menor do que US$ 5,
then:
colocar PROGRAMAÇÃO para o período noturno (19h00 — 21h00)
Next, se PROGRAMAÇÃO tiver endereço de memória aberto,
then:
colocar corteDeGrama em endereço de memória.
associar ENDEREÇO_DO_GRAMADO com corteDeGrama.
return “Sim, eu cortarei a grama nesta noite”.
else:
return “Não, eu não posso cortar o gramado”.
endif:
else:
utilize associação de corte do gramado para obter
um cortador de grama profissional (professional lawn mower — plm)
solicite ao plm para “cortar a grama (ENDEREÇO_DO_GRAMADO,
self, ENDEREÇO)”.
se a resposta do plm for “sim”
then:
return “Sim, eu cortarei a grama nesta noite”.
else:
return “Não, eu não posso cortar o gramado”.
endif:
endif:
}

FIGURA 11.17 Exemplo de um cartão de classe CRC para o corte de grama.


CAP. 11 O MODELO 197

Desenvolver o modelo para esses dois casos de uso não é instrutivo e existem bi-
bliotecas GUI padrão (por exemplo, a MFC da Microsoft). Portanto, não vamos desenvol-
ver o modelo necessário para suportar esses outros dois casos de uso. Entretanto, eles
introduzem dois novos subsistemas: a interface do usuário e o controlador. A interface
do usuário é o subsistema pelo qual o observador e o diretor interagem com o sistema.
O modelo da interface do usuário consistirá de classes e objetos associados a janelas, bo-
tões, menus e outras classes de interface. O controlador é o subsistema que trata de assi-
naturas e definição de parâmetros de simulação. O controlador consistirá de classes e
objetos associados à manutenção do tempo, assinaturas e eventos.
Com esses dois subsistemas, nosso desenho está completo. A Figura 11.18 apresenta
como os três sistemas interagem. Nesta figura, o subsistema controlador ativa o Modelo
de Corte de Grama por meio da simulação de tempo e estabelece assinaturas para os ele-
mentos do Modelo de Corte de Grama. O subsistema interface do usuário não se comu-
nica diretamente com o subsistema Modelo de Corte de Grama. Durante a simulação, o
subsistema Modelo de Corte de Grama gerará eventos. Esses eventos serão entregues ao
subsistema apropriado baseado em assinaturas. Isso incluirá os subsistemas da interface
do usuário e do controlador.

FIGURA 11.18 Os três subsistemas compreendendo nosso Sistema de Simulação de Serviços


Domésticos.

■ ■ RESUMO
As subetapas de refinamento de modelo e de identificação de subsistemas se dão da se-
guinte maneira:
1. Agrupe todos os objetos com os mesmos atributos, relacionamentos similares e
comportamentos comuns. Se existir um conceito dentro do domínio de aplicação
que defina esses objetos, utilize esse nome para a classe. Em caso negativo, per-
gunte ao especialista em domínios com qual conceito isso poderia se assemelhar.
Utilize a classe recomendada pelo especialista e reagrupe seus objetos.
2. Agrupe os vínculos e agregações de objetos em associações e agregações de clas-
ses. Lembre-se de que todos os membros do grupo devem portar o mesmo do-
mínio semântico.
3. Determine se as classes são especializações de uma superclasse comum. Procure
por atributos idênticos, relacionamentos, serviços e comportamentos comuns ao
198 UML E C++ CAP. 11

longo das classes. Com eles, tente formar uma classe. Novamente, pergunte ao
especialista em domínios se essas propriedades capturam um conceito de domí-
nio proveitoso. Lembre-se de que esses novos objetos devem operar em um do-
mínio semântico útil.
4. Procure por polimorfismo. Observe se existem serviços de objetos que sejam os
mesmos ou similares, mas difiram quanto ao comportamento (de que forma é
provido o serviço). Se esses objetos operarem no mesmo domínio semântico,
faça com que o serviço seja polimorfo. Realize todas as etapas precedentes mais
uma vez até que não sejam mais encontradas novas superclasses.
5. Desenhe um modelo que capture somente as associações e agregações de classes.
6. Identifique o agrupamento de classes e suponha que eles sejam subsistemas po-
tenciais.
7. Dê a esse agrupamento um nome e uma declaração de missão.
8. Utilize a ponte (vínculo) e o teste de substituição para determinar se porventura
trata-se de um subsistema (domínio separado).
DESENHO
Desenho
12.DESENHO

M uitas coisas difíceis de desenhar revelam depois de um bom


desempenho.

N o final da quinta etapa, finalizamos o modelo de aplicação. Agora, estamos prontos


para considerar a tecnologia necessária para a implementação do modelo. Neste ca-
pítulo, acrescentaremos ao modelo todos os objetos (classes) dependentes da tecnologia
para tornar implementável a aplicação.
Não abordamos ou descrevemos neste livro padrões de desenho de objetos, apesar
do grande grau de interesse que esta área temática tem gerado. Justificamos essa ausência
pela nossa observação de que o uso de padrões de desenho por pessoas sem significativa
experiência em métodos orientados a objeto tem ocasionado problemas consideráveis. Pa-
drões de desenho, muito embora simples e de fácil compreensão em seus próprios ter-
mos, podem apresentar dificuldades para que pessoas inexperientes os apliquem
corretamente. Temos observado que os novatos tendem a distorcer seus modelos de ob-
jetos para os padrões de mais fácil compreensão. Em vez de abordá-los ou descrevê-los,
optamos por mapear (traduzir) um modelo de objeto em um desenho bom e proveitoso
que possa ser facilmente implementado. Estamos deixando os padrões de desenho para
algum livro de nível avançado sobre desenho.

Introdução
Depois de você ter modelado uma solução de negócio para sua aplicação/sistema, é pre-
ciso decidir sobre uma abordagem para a implementação da solução de negócio utilizan-
do a tecnologia disponível. O desenho do sistema (system design) é a estratégia de alto nível
para se implementar a solução de negócio, e o desenho detalhado (detailed design) é a estra-
tégia de baixo nível da implementação da aplicação/sistema.

199
200 UML E C++ CAP. 12

No desenho do sistema, o desenvolvedor deverá fazer o seguinte:


■ Organizar o sistema em subsistemas.
■ Identificar concorrência inerente no modelo.
■ Alocar os subsistemas aos processadores e tarefas.
■ Escolher um veículo e uma abordagem para fins de armazenamento de dados.
■ Determinar uma estratégia de controle para o acesso de recursos globais.
■ Selecionar um implemento de controle para o software.
■ Considerar estratégias de inicialização, finalização e de falhas.
Em desenho detalhado, o desenvolvedor deverá fazer o seguinte:
■ Acrescentar os objetos/classes dos domínios de arquitetura e de implementação.
■ Desenhar algoritmos eficientes para serviços complexos.
■ Otimizar o desenho da aplicação/sistema.
■ Maximizar o uso de herança.
■ Redesenhar as associações para eficiência.
■ Determinar a melhor representação das classes.
■ Condensar as classes e associações em unidades reutilizáveis.
Obviamente que o desenho detalhado será feito após o desenho do sistema.

Desenho do Sistema
Apesar de todas as questões e decisões que um desenvolvedor deva tomar no desenho
do sistema serem críticas ao sucesso do projeto, somente discutiremos a divisão do siste-
ma em um menor número de componentes e a estratégia de controle para o software.

Subsistemas
Cada componente principal do sistema é chamado de subsistema.1 Cada subsistema deve-
rá lidar com um tópico em separado, denominado domínio. Cada domínio pode ser in-
dependente do resto do sistema.2 Uma boa indicação para um domínio é que ele possui
sua própria terminologia com diferente significado semântico; ele é um mundo real, hi-
potético ou abstrato em separado, habitado por um distinto conjunto de objetos que se
comportam segundo regras e diretrizes do domínio. Um subsistema não é um objeto nem
uma função, mas um pacote de classes, associações, operações, eventos e restrições que
estão inter-relacionados e que, felizmente, apresentam um pequeno número de interfaces
razoavelmente bem-definidas com o restante do sistema.
Um subsistema é geralmente definido pelo serviço que ele provê, exatamente da
mesma forma que um objeto ou classe. O relacionamento entre o restante do sistema e o
subsistema pode ser do tipo não-hierárquico ou cliente/servidor.
Em um relacionamento não-hierárquico, qualquer lado pode ter acesso aos serviços
do outro. A comunicação não é necessariamente feita por uma solicitação seguida de uma

1. Isto corresponde ao mesmo conceito discutido no capítulo anterior. Em análise, utilizamos


subsistemas para administrar a complexidade no domínio de aplicação. Agora, em desenho,
utilizamos subsistemas para efetuar o mesmo nos domínios de arquitetura e de implementação.
2. É nossa opinião que a reutilização efetiva não é no nível do objeto/classe conforme proclamado pela
maioria dos especialistas, mas no nível do domínio. Um exemplo de reutilização de domínio
bem-sucedida é visto com muitos pacotes disponíveis de domínio de serviço de aplicação.
CAP. 12 DESENHO 201

resposta imediata, de forma que é possível terem-se ciclos de comunicação capazes de


originar erros sutis de desenho.
O relacionamento cliente/servidor, entretanto, é muito mais simples; o cliente invo-
ca o servidor, que executa algum tipo de serviço e responde com os resultados. O cliente
precisa conhecer a interface do provedor, mas o provedor não precisa, necessariamente,
conhecer a interface do cliente. Todas as interações são realizadas por meio da interface
do provedor.
Existe toda sorte de sugestões de como decompor um sistema. O sistema decompos-
to pode ser organizado em duas maneiras: por camadas horizontais ou por partições
verticais.
Um sistema disposto em camadas é um conjunto ordenado de subsistemas em que
cada subsistema individual é criado em função daqueles que se encontram abaixo, e pro-
vê os alicerces para a construção do que se encontra em uma camada superior. Os objetos
em cada camada podem ser independentes, muito embora haja uma certa correspondên-
cia entre os objetos das várias camadas. Entretanto, o conhecimento é somente um cami-
nho; um subsistema sabe sobre as camadas inferiores, mas não sobre os subsistemas
acima dele. Portanto, existe um relacionamento cliente/servidor entre camadas. Um
exemplo de um sistema disposto em camadas é o de janelas destinado a uma interface de
usuário de computadores.
Um sistema verticalmente particionado divide um sistema em diversos sistemas in-
dependentes ou fracamente acoplados, com cada um deles provendo um tipo de serviço.
Por exemplo, um sistema operacional inclui um subsistema de arquivos, controlador de
dispositivo, subsistema de gerenciamento virtual e um driver (manipulador) de interrup-
ção de eventos. Em um sistema de partição vertical, existe um relacionamento do tipo
não-hierárquico entre subsistemas.
Um sistema real pode ser decomposto com êxito em subsistemas utilizando tanto
camadas como partições em várias combinações; uma camada pode ser particionada e
uma partição pode ser dividida em camadas. A maioria dos sistemas extensos requer
uma combinação de camadas e partições.

Estruturas Arquiteturais
Na realidade, pelo fato de muitas das decisões que deveriam ser tomadas no estágio do
desenho do sistema serem conferidas aos desenvolvedores, uma grande parcela do pro-
cesso de desenho é descobrir como integrar estes dados em um sistema operativo. Um
dos mais importantes aperfeiçoamentos em desenvolvimento de software é que os forne-
cedores de software têm dotado aos desenvolvedores um subsistema que realiza serviços
específicos de forma muito boa para aplicações. Assim, os desenvolvedores deveriam se
valer de subsistemas escritos e testados quando possível. Além do mais, a maioria dos
desenvolvedores construiu algumas estruturas arquiteturais bem-apropriadas para certos
tipos de aplicações. Se você tiver aplicações com características similares, é aconselhável
utilizar essas arquiteturas correspondentes como um ponto de partida para seu desenho.
Os tipos de sistemas são os seguintes:
■ Sistemas Batch (em lote). Uma transformação de dados é feita em um conjunto in-
teiro de inputs.
■ Sistemas Contínuos. À medida que o input muda em tempo real, uma transforma-
ção de dados é realizada em tempo real.
■ Sistemas Interativos. Interações externas dominam a aplicação.
202 UML E C++ CAP. 12

■ Sistemas de Transação. A aplicação é envolvida com o armazenamento e atualiza-


ção de dados, geralmente incluindo acesso concorrente por muitos usuários e de
muitos diferentes lugares.
■ Sistemas Baseados em Regras. A aplicação é dominada pelo interesse no cumpri-
mento de regras.
■ Sistemas de Simulação. A aplicação simula o desenvolvimento de objetos do mun-
do real.
■ Sistemas em Tempo Real. A aplicação é dominada por restrições estritas de tempo.
As etapas para a realização de um desenho de sistema orientado a objeto para as
primeiras quatro estruturas arquiteturais são apresentadas a seguir:

Estrutura Arquitetural Batch


1. Divida a transformação em subtransformações de tal forma que cada subtrans-
formação realize uma parte da transformação.
2. Defina objetos temporários para os fluxos de dados entre subtransformações.
Então, cada subtransformação somente precisa saber sobre os objetos em cada
lado de si mesma (ou seja, seus inputs e outputs).
3. Expanda cada subtransformação em outras subtransformações até que as opera-
ções tenham uma fácil implementação.
4. Reestruture o encadeamento (pipeline) para otimização.
5. Utilize o novo conjunto de objetos para formar classes que se associem frouxa-
mente ao modelo de objeto original.

Estrutura Arquitetural Contínua


1. Identifique todos os objetos que precisam de atualizações contínuas.
2. Faça um diagrama de seqüência para a transformação contínua.
3. Transforme os inputs e outputs dos serviços em objetos temporários que conte-
nham os valores que mudam continuamente.
4. Refine ou defina métodos para cada objeto/classe que processará as mudanças
incrementais do objeto.
5. Adicione quaisquer objetos complementares necessários para otimização.
6. Utilize o novo conjunto de objetos para formar classes que se associem frouxa-
mente ao modelo de objeto original.

Estrutura Arquitetural Interativa


1. Separe os objetos formadores da interface dos objetos que definam a semântica
da aplicação; eles estão em dois domínios diferentes.
2. Utilize objetos predefinidos (biblioteca) que proporcionem interfaces com agen-
tes externos. Por exemplo, a maioria dos sistemas de janelas tem bibliotecas que
concedem aos desenvolvedores janelas, menus e botões para uso.
3. Use uma abordagem (callback) de decomposição baseada em eventos.
4. Separe os eventos físicos dos lógicos e destine-os para os corretos objetos. Even-
tos lógicos são parte da aplicação e eventos físicos são provavelmente parte do
domínio da interface. Tome cuidado; muitas vezes um evento lógico correspon-
de a múltiplos eventos físicos.
CAP. 12 DESENHO 203

Estrutura Arquitetural de Transação


1. Mapeie cada modelo de objeto em um banco de dados.
2. Determine os recursos que não podem ser compartilhados.
3. Determine a unidade de uma transação (os objetos devem ser acessados juntos
durante uma transação) utilizando um diagrama de seqüência.
4. Projete o controle de concorrência para as transações. Isso é suportado pela
maioria dos sistemas de bancos de dados.
Controle de Software Dentro de uma Estrutura. Existem dois tipos de controle de fluxos
dentro de um sistema de software: externo e interno. O controle externo é o fluxo de
eventos externamente visíveis entre os objetos no sistema, enquanto que o controle inter-
no é o fluxo de controle dentro de um método.
Há três modos de controlar fluxos externos: seqüenciais baseados em procedimen-
tos, seqüenciais baseados em eventos, e concorrentes. Similarmente, os três modos de
controlar fluxos internos são: chamadas (calls) de procedimento, chamadas entre tarefas
quase-concorrentes e chamadas entre tarefas concorrentes. Ambas as estratégias selecio-
nadas de controle interno e externo são altamente dependentes de recursos (linguagem,
sistema operacional etc.) disponíveis e do padrão de interações na aplicação.
Em virtude das linguagens orientadas a objeto mais importantes, como Smalltalk,
C++ e Objective C, serem linguagens procedurais, o modo mais comum de controle de
fluxo externo é o seqüencial baseado em procedimentos. Nesse estilo, o controle reside
dentro do código da aplicação. O código da aplicação emite solicitações para inputs ex-
ternos e espera a chegada destes. Quando de sua chegada, o controle é resumido dentro
do procedimento que efetuou a chamada. Apesar desse estilo ser de fácil implementação
para a maioria dos desenvolvedores, o desenvolvedor deve converter os eventos em um
fluxo seqüencial de operações (métodos) entre objetos. Isso é feito por meio de um dia-
grama de seqüência3. Esse estilo de controle é útil quando há uma regularidade de even-
tos externos. Entretanto, esse estilo não é muito apropriado para tratar com eventos
assíncronos, condições de erro, interfaces flexíveis do usuário e sistemas de controle de
processos.
No estilo seqüencial baseado em eventos, o controle reside dentro de um executor
(dispatcher) ou monitor provido pela linguagem, subsistema ou sistema operacional. Pro-
cedimentos de aplicação são juntados aos eventos e invocados pelo executor quando
ocorrem os eventos correspondentes (callback). A aplicação efetua chamadas de procedi-
mento ao executor para input/output, mas não espera por isso in-line. Eventos são tratados
pelo executor, e todos os procedimentos de aplicação retornam o controle ao executor em
lugar de esperar pela chegada do input.
O estilo de controle de eventos tem uma implementação mais difícil com linguagens
padrão de programação (Smalltalk, C++ ou Objective C). Esse estilo possibilita um pa-
drão de controle mais flexível do que o estilo procedural. Em virtude dele estimular pro-
cessos de cooperação dentro de uma tarefa multiencadeada simples, um único método
itinerante pode bloquear toda uma aplicação. Entretanto, o estilo de controle de eventos
configura um desenho mais modular e pode manusear melhor condições de erros.
No estilo concorrente, o controle reside em diversos independentes objetos, em que
cada um deles constitui uma tarefa em separado. Uma tarefa pode esperar por input, mas

3. Os diagramas de seqüência são discutidos no Capítulo 7.


204 UML E C++ CAP. 12

outras tarefas continuam a executar. Há um mecanismo de formação de filas para os


eventos, e o sistema operacional soluciona conflitos de agendamento entre as tarefas.
JAVA é uma linguagem orientada a objeto que diretamente suporta tarefas e concorrên-
cia.

Documentando Desenho do Sistema


Uma arquitetura de sistemas, o desenho do sistema, é documentada(o) na UML utilizan-
do três diagramas diferentes: um diagrama de pacote, um diagrama de componente e um
diagrama de realização. Um diagrama de pacote mostra o particionamento do software.
Um diagrama de componente descreve os relacionamentos entre os componentes do soft-
ware no sistema. Um diagrama de realização posiciona os componentes sobre platafor-
mas de hardware. Esses diagramas são tipicamente acompanhados por descrições
textuais dos componentes, conexões e hardware.
Conforme foi introduzido no capítulo anterior, um diagrama de pacote identifica
um agrupamento de elementos de software, normalmente uma coleção de classes de soft-
ware. Um diagrama de pacote simplificado é apresentado na Figura 12.1. É possível in-
cluir explicitamente no ícone do pacote todas as classes que são membros.

FIGURA 12.1 Diagrama de pacote simplificado para um sistema do tipo modelo-controlador-


visão.

O diagrama de componente identifica os componentes formadores do sistema, junto


com as conexões entre componentes individuais, conforme apresentado na Figura 12.2. A
especificação da UML identifica cinco protótipos para componentes, a saber: (1) aplica-
ção, (2) biblioteca, (3) tabela, (4) arquivo e (5) documento. De acordo com a especificação
da UML, um componente é representado por um retângulo com dois retângulos menores
sobrepostos em um de seus lados. Entretanto, a especificação da UML não sugere repre-
sentações alternativas para os últimos quatro protótipos. As dependências são represen-
tadas por setas pontilhadas. Pode-se incluir no interior do diagrama de componente
identificação explícita das classes que um componente realiza (implementa). Isso é indi-
cado por uma seta pontilhada para a representação na UML de uma classe.
CAP. 12 DESENHO 205

FIGURA 12.2 Diagrama de componente para um sistema de reservas de uma companhia aé-
rea e para um sistema de programação de vôos.

O diagrama de implantação captura relacionamentos entre componentes e o hard-


ware no qual estes estão hospedados. Componentes são capturados no diagrama de im-
plantação utilizando-se o mesmo mecanismo gráfico do diagrama de componente. Entretanto,
os componentes são colocados dentro de caixas gráficas que representam dispositivos de
hardware (nós) do sistema. Um nó não precisa ser necessariamente um computador ge-
neralizado de processamento, mas pode incluir sensores, arranjos de discos RAID (Redun-
dant Array of Independent Disks) e outros dispositivos que são parte integral de um sistema.
São utilizadas linhas sólidas para indicar a conectividade do hardware entre os nós. Um
diagrama de implantação simplificado é apresentado na Figura 12.3.

FIGURA 12.3 Diagrama de implantação representando um terminal do agente conectado a


um dispositivo impressor especializado para a impressão de bilhetes.

Desenho Detalhado
Durante a análise, determinamos os objetos/classes e suas associações e estruturas a par-
tir de uma perspectiva de aplicação. Na fase do desenho, tivemos de acrescentar os objetos
de implementação e otimizar estruturas de dados e algoritmos para fins de codificação.
Há uma mudança de rumo quanto à ênfase dos conceitos do domínio de aplicação
em relação à dos conceitos do computador. Determinadas as classes desde a aplicação, o
desenhista deve escolher entre os diferentes caminhos para implementá-las. Fatores que
podem ter uma certa importância incluem o tempo de execução, o emprego da memória,
e o acesso de entrada e saída (I/O) de disco. Entretanto, a otimização do desenho não de-
verá ser conduzida em excesso, pois é aconselhável que exista uma compensação prática
entre otimização e facilidade de implementação, possibilidade de manutenção e extensi-
bilidade do produto final.
206 UML E C++ CAP. 12

De modo geral, a melhor e mais simples abordagem é adotar as classes encontradas


na análise para a fase do desenho. O desenho, então, torna-se o processo de acrescentar
objetos de implementação, acrescentar detalhes de implementação e tomar decisões sobre
implementação. Ocasionalmente, um objeto/classe da análise não aparece no desenho,
mas é distribuído entre outros objetos/classes para eficiência computacional. Alguns atri-
butos redundantes ou um objeto/classe poderão ser adicionados para melhorar a eficiência.
Portanto, o desenho detalhado é principalmente um processo de refino e de adição
de objetos de implementação dependentes da tecnologia. Esses novos objetos e detalhes
complementares deverão contribuir para uma melhor organização da aplicação e para o
“fortalecimento” do modelo de análise.

Desenho de Classes
Durante a análise, focamos a estrutura lógica da informação necessária para construir
uma solução de negócio. Na fase do desenho, precisamos verificar o melhor modo de im-
plementar essa estrutura lógica que contribua para otimizar o desempenho da aplicação.
Muitas das estruturas efetivas de implementação de que precisamos são instâncias de
classes recipientes; exemplos são arranjos, listas, filas, pilhas, conjuntos, sacos, dicioná-
rios, associações e árvores. A maior parte das linguagens orientadas a objeto já dispõe de
bibliotecas que provêem tais classes.4
Embora tenhamos definido algoritmos de negócios para construir uma solução de
negócio durante a análise, talvez necessitemos otimizar os algoritmos para fins de imple-
mentação. Durante a otimização, podemos acrescentar novas classes para deter resulta-
dos intermediários e novos métodos de baixo nível. Essas novas classes são geralmente
de implementação não mencionadas diretamente no documento de requisitos do cliente.
Elas normalmente são classes do domínio de serviço que suportam a construção das clas-
ses de aplicação. Quando forem acrescentados novos métodos, alguns terão objetos-alvo
óbvios em seu poder. Entretanto, alguns métodos poderão ter diversos objetos-alvo em
seu poder. Repassar responsabilidade para o tipo mais recente de serviço pode ser muito
frustrante.5 Esse é o problema fundamental quando inventamos objetos de implementa-
ção; eles são de certo modo arbitrários e suas fronteiras representam mais uma questão
de conveniência do que de necessidade lógica.6
Se necessitarmos evitar recálculos para melhorar o desempenho, deveremos definir
novos objetos/classes para reter estes atributos (dados) derivados. Lembre-se de que atri-
butos derivados devem ser atualizados quando houver alterações nos valores-base. Isso
pode ser feito por:
1. Código explícito. Pelo fato de cada atributo derivado ser definido em função de
um ou mais atributos dos objetos-base, um modo de atualizá-los é inserir código
no método atualizado dos atributos do(s) objeto(s)-base. Esse novo código atua-

4. A Standard Template Library (STL) é a biblioteca C++ que provê classes recipientes. Esse tema será
abordado em um capítulo posterior.
5. Estes serviços de “implementação” podem ser tranqüilamente omitidos, pois não são inerentemente
serviços de unicamente uma classe.
6. Isto se torna muito difícil quando precisamos atribuir um serviço em uma hierarquia de herança.
Para classes de implementação, as definições de subclasses podem ser um tanto arbitrárias e fluidas.
É muito comum vermos serviços movendo-se para cima e para baixo da hierarquia durante a etapa
de desenho.
CAP. 12 DESENHO 207

lizaria explicitamente o atributo derivado dependente do atributo do objeto-


base. Isso é denominado sincronização por código explícito.
2. Recálculo periódico. Quando são modificados valores-base em um lote, é pos-
sível recalcular todos os atributos derivados periodicamente depois da modifi-
cação de todos valores-base. Isso é denominado recálculo periódico.
3. Disparadores. Um atributo ativo tem atributos dependentes. Cada atributo de-
pendente deve se registrar com o atributo ativo. Quando o atributo ativo está
sendo atualizado, é acionado um disparador que informa a todos os objetos con-
tendo os atributos dependentes que o atributo ativo tem um valor modificado.
Então, é de responsabilidade do objeto derivado atualizar seu atributo derivado.
Isso é denominado atualização por disparadores.
Às vezes o mesmo serviço é definido para diversas classes e pode ser facilmente
herdado de uma superclasse comum. Todavia, de modo geral, os serviços em classes di-
ferentes são similares mas não idênticos. Ao modificar-se ligeiramente o protótipo de ser-
viço, é possível torná-lo concordante de modo que seja tratado por um único serviço
herdado. Quando isso é feito, não é somente o nome e a assinatura do serviço que devem
concordar, mas eles deverão ter o mesmo significado semântico. Os seguintes ajustes são
comumente feitos para se aumentar o processo de herança:
1. Quando alguns serviços têm menos argumentos do que outros serviços, esses ar-
gumentos ausentes são acrescentados, mas ignorados no método.
2. Quando um serviço tem poucos argumentos porque ele representa um caso es-
pecial de um serviço mais geral, é possível implementar o serviço especial invo-
cando-se os serviços gerais com todos os seus argumentos.
3. Quando atributos em diferentes classes têm o mesmo significado semântico, se-
lecione um nome para o atributo e mova-o para uma superclasse comum.
4. Quando serviços em diferentes classes têm o mesmo significado semântico, se-
lecione um nome para o serviço e aplique os itens 1 ou 2 para se beneficiar do
polimorfismo.
5. Quando um serviço é definido em diversas classes diferentes, mas não em ou-
tras classes que semanticamente deveriam estar em um grupo, defina o serviço
na superclasse e o declare como um método “no-op” na classe que não se impor-
ta em prover este serviço.
6. Quando tiver sido reconhecido comportamento comum, poderá ser criada uma
superclasse comum para implementar o comportamento compartilhado, deixan-
do o comportamento especializado nas subclasses. Normalmente, essa nova su-
perclasse é uma classe abstrata.7
Recomenda-se bastante que não se utilize herança puramente como uma implemen-
tação técnica. Isso ocorre quando desenvolvedores encontram uma classe existente que te-
nha implementado um grande número de serviços necessários para uma classe recentemente
definida, mesmo com as duas classes sendo semanticamente diferentes. O desenvolvedor
então poderá querer utilizar herança para obter uma implementação parcial da nova clas-
se. Isso pode originar efeitos colaterais porque alguns dos métodos herdados podem pro-

7. Às vezes vale a pena abstrair uma superclasse mesmo se há apenas uma subclasse em nossa
aplicação que é herdeira dela. Se ela tiver uma semântica proveitosa, provavelmente será necessária
em extensões futuras da aplicação ou em outras aplicações.
208 UML E C++ CAP. 12

ver comportamentos indesejados. Isso também poderá promover hierarquias de herança


frágeis que são difíceis de sofrer modificações assim que o modelo de análise evoluir para
refletir requerimentos de mudanças. Uma melhor técnica é a de utilizar delegação8, que
possibilita à classe recentemente formada delegar somente os serviços apropriados.9

Desenho de Associações
Ao implementar associações, o desenhista deve considerar o padrão de acesso e as fre-
qüências relativas dos diferentes tipos de acesso. Se o número de hits de uma consulta for
baixo devido a somente uma fração dos objetos satisfazer os critérios, é aconselhável uti-
lizar um índice para melhorar o acesso a objetos que, com freqüência, são recuperados.
Entretanto, isso incorrerá em custo, pois esse procedimento utilizará mais memória e as
atualizações serão mais lentas. Em alguns casos, acrescentar uma nova associação deriva-
da da associação-base proverá acesso direto aos dados apropriados.
Se a associação for navegável somente em uma direção, poderá ser implementada
uma associação na condição de um atributo contendo uma referência do objeto. Se a mul-
tiplicidade for 1, ela será simplesmente um identificador do outro objeto. Se a multiplici-
dade for >1, conseqüentemente ela será um identificador para um conjunto de
identificadores de objetos. Se o lado dos “muitos” for ordenado, em lugar de um conjunto
utilizar-se-á uma lista. Pode ser implementada uma associação qualificada utilizando-se
um objeto dicionário.
Uma associação de duas vias pode ser implementada conforme se segue:
1. Acrescente um atributo para a classe em um lado da associação e realize uma
busca quando for requerida uma passagem reversa.
2. Acrescente um atributo em ambos os lados da associação. Use as mesmas técni-
cas de multiplicidade que são utilizadas para uma associação que é navegável
em uma direção.
3. Crie uma classe associada, independentemente de qual seja a classe. Uma classe
associada é um conjunto de pares de objetos correlacionados, armazenados em
um objeto de tamanho variável em somente uma dimensão. Para melhora da efi-
ciência, é comum implementar-se um objeto associativo como dois objetos mapas10.
Caso a associação não tenha serviços, e sim atributos, esses podem ser implementa-
dos como segue:
1. Se a associação for do tipo um-para-um, os atributos dela poderão ser armaze-
nados como atributos em qualquer uma das classes.
2. Se a associação for do tipo muitos-para-um, os seus atributos poderão ser arma-
zenados na classe no lado dos “muitos”.
3. Se a associação for do tipo muitos-para-muitos, será melhor criar uma classe as-
sociativa e destinar os atributos da associação a ela.11

8. Em delegação, cria-se uma associação entre a classe e a classe recentemente formada. Então, essa
última pode delegar o serviço a partir dela própria até o correspondente serviço da classe existente.
9. Linguagens, como é o caso da C++, permitem que uma subclasse seletivamente realize serviços públicos.
Quanto utilizada propriamente, a herança pode efetuar o equivalente à delegação para essas linguagens.
10. Os objetos mapas são explicados no Capítulo 15.
11. Esta abordagem também pode ser usada para associações muitos-para-um porque elas geralmente
podem evoluir para associações muitos-para-muitos.
CAP. 12 DESENHO 209

Generalização e Herança
A maioria das linguagens orientadas a objeto incorpora generalização na linguagem por
meio da herança entre classes. Normalmente, uma classe-filha pode herdar os atributos, servi-
ços, comportamentos e relacionamentos da(s) classe(s)-pai12. Pela utilização desse mecanismo,
o paradigma orientado a objeto dota os analistas/desenvolvedores de um mecanismo
muito poderoso que não apenas contribui para organizar objetos complexos como tam-
bém facilita o compartilhamento e a reutilização de código na implementação. As pro-
priedades da herança entre classes são como se segue:13
1. Estrutural
a. Atributos
Objetos (instâncias) da classe descendente, que é uma subclasse da classe-pai,
terão valores para todos os atributos da classe ancestral.
b. Relacionamentos de não generalização
Objetos (instâncias) da classe descendente, que é uma subclasse da classe-pai,
terão todos os relacionamentos de não generalização da classe ancestral.
2. De Interface
Todos os serviços providos pela classe ancestral deverão ser também providos
pela classe descendente. Um objeto que seja uma instância da classe descendente
é simultaneamente uma instância de sua classe ancestral.
3. Comportamental
a. Herança sem polimorfismo (bom filho)
Em herança sem polimorfismo, todos os métodos providos pela classe ances-
tral para seus serviços são também usados pela classe descendente para pro-
ver os correspondentes serviços. Isso representa compartilhamento e reutilização
de código.
b. Herança com polimorfismo (mau filho)
Em herança com polimorfismo, alguns dos métodos providos pela classe an-
cestral para seus serviços são também usados pela classe descendente para
prover os correspondentes serviços. Para os serviços restantes da classe an-
cestral, a classe descendente fornece seus próprios métodos customizados
que substituem os métodos correspondentes apropriados para utilização pe-
las instâncias da classe descendente.14
4. Matemática
a. Anti-simetria
Se a classe A é uma subclasse da classe B, então a classe B não pode ser uma
subclasse da classe A. Em outras palavras, se o objeto A é um descendente
do objeto B, então o objeto B não pode ser também um descendente do objeto A.

12. As classes-pai são denominadas superclasses; neste livro, também utilizamos classe ancestral.
13. Este não é um tópico assim tão simples como queremos levá-lo a acreditar. As hipóteses feitas acerca
dessas propriedades são muito variadas entre as linguagens orientadas a objeto. Fornecemos a você
as propriedades que são consistentes com C++ que implementarão a generalização/especialização
conforme definido na análise.
14. A classe ancestral decide que serviços podem ser redefinidos utilizando-se a palavra-chave virtual.
Isso deve ser planejado para quando haja definição da classe ancestral.
210 UML E C++ CAP. 12

b. Transitividade15
Se a classe A é uma subclasse da classe B, e a classe B é uma subclasse da
classe C, então a classe A é uma subclasse da classe C. Uma instância da clas-
se A é também uma instância da classe C e da classe B.

Delegação
O modelo orientado a objeto que construímos é baseado na noção de classe e não na no-
ção de objeto. Entretanto, existem modelos computacionais orientados a objetos que são
baseados no objeto. Esses sistemas são normalmente denominados sistemas protótipos.
Em um sistema protótipo, não existe um mecanismo como a classe. Somente existem ob-
jetos, e um objeto pode ter um relacionamento de delegação com um outro objeto. Quan-
do é solicitado um serviço de um objeto, ele procede da seguinte forma:
1. Se tiver um método para o serviço, ele executará seu próprio método.
2. Se não tiver nenhum método, ele delegará a execução daquele serviço para um
objeto com o qual mantenha um relacionamento de delegação.
3. O relacionamento de delegação é transitivo. Assim, se o objeto delegado não ti-
ver um método para o serviço, ele tentará delegar a execução do serviço para ou-
tros objetos com os quais mantenha um relacionamento de delegação.
É preciso que o leitor note que o relacionamento ocorre entre objetos, e que o rela-
cionamento de delegação é mais genérico do que o relacionamento é_um porque ele pode
ser utilizado entre quaisquer dois objetos.16 Além do mais, a delegação pode ser estabe-
lecida dinamicamente (em tempo de execução), enquanto que a herança entre classes é
fixada no período de criação.
Para um sistema protótipo, a análise é feita pensando-se em um particular objeto e,
em seguida, desenhando similaridades e/ou diferenças para outros objetos baseados nes-
se(s) particular(es) objeto(s). Qualquer objeto pode ser um objeto protótipo durante a aná-
lise. A idéia é iniciar com objetos individuais e a seguir especializá-los e generalizá-los à
medida que forem considerados casos mais complexos.17 Lieberman descreve essa abor-
dagem comparativamente ao enfoque orientado a objeto:
Os sistemas protótipos permitem inicialmente a criação de conceitos, então os gene-
ralizam anunciando que aspectos do conceito são passíveis de variação. Sistemas
orientados a conjunto (orientado a objeto) requerem a criação da descrição da abs-
tração do conjunto (classe) antes que as instâncias individuais possam ser instaladas
como membros.
De certa forma, esse método de análise se aproxima mais do modo como os seres
humanos aprendem. Nós aprendemos por generalização ou especialização em instâncias.
Partindo disso, pode-se chegar à conclusão de que a delegação é um melhor mecanismo
para a implementação de generalização/especialização. Entretanto, veremos na próxima
seção que isso não é assim tão simples.

15. A transitividade possibilita organizar os objetos (classes) de uma maneira hierárquica. Por causa
dessa propriedade, a generalização é por diagrama mostrada como uma árvore de ancestrais.
16. Além do mais, em certas linguagens, não são só as execuções dos serviços que podem ser delegadas,
mas também os atributos podem igualmente ser herdados ou compartilhados.
17. Nosso método é um enfoque modificado envolvendo protótipo para construir um sistema orientado
a objeto.
CAP. 12 DESENHO 211

Tratado de Orlando
Historicamente, tem havido muito debate sobre qual mecanismo (herança ou delegação)
é o mais poderoso para a implementação de generalização/especialização. Desde 1987,
vemos que a delegação pode modelar herança e, inversamente, a herança pode modelar
delegação. Durante a OOPSLA daquele ano, realizada em Orlando, Flórida, Lynn Stein,
Henry Lieberman e David Urger discutiram suas diferenças sobre delegação e herança e
chegaram a um consenso refletindo a necessidade de se ter os dois mecanismos. Essa re-
solução ganhou fama como o Tratado de Orlando. Em essência, o tratado reconhece dois
modelos de compartilhamento de código: compartilhamentos antecipado e não-antecipa-
do. Os sistemas de classes baseados em herança são melhores para o compartilhamento
antecipado de código e os sistemas baseados em delegação são mais apropriados para o
compartilhamento não-antecipado de código.
O tratado caracterizou três dimensões para o compartilhamento de código:
1. Estática versus dinâmica. O compartilhamento é determinado quando o objeto
é criado ou ele pode ser determinado dinamicamente (em tempo de execução)?
2. Implícita versus explícita. Há operações explícitas para indicar o compartilha-
mento de código?
3. Orientado a objeto versus orientado a grupo. O compartilhamento é definido para
grupos completos de objetos ou ele pode ser suportado por objetos individuais?
As linguagens orientadas a objeto tradicionais (ou seja, C++, Smalltalk e Simula) uti-
lizam estratégias estáticas, implícitas e orientadas a grupo no desenho de suas lingua-
gens. Em oposição, as linguagens baseadas em delegação utilizam estratégias dinâmicas,
explícitas e orientadas a objeto no desenho de suas linguagens.
O tratado de Orlando reconhece ainda que:
não pode ser obtida uma resposta definitiva sobre qual é o melhor conjunto dessas
opções. E mais, que diferentes situações de programação pedem por diferentes com-
binações destas características: para ambientes de programação experimentais, ex-
ploratórios, talvez seja desejável possibilitar a flexibilidade de compartilhamento
dinâmico, explícito e orientado a objeto, ao passo que para produção de software
em ritmo extenso e relativamente rotineiro talvez seja mais apropriado restringir às
opções complementares disponíveis — ou seja, uma estratégia estritamente estática,
implícita e orientada a grupo.
Aqui há uma compensação entre as duas estratégias. A delegação requer menos es-
paço, mas a execução é mais lenta por causa da ligação em tempo de execução. Em con-
trapartida, a herança entre classes tem uma execução mais rápida porém requer mais
espaço. Se o sistema de classe for altamente baseado em tipos especiais predefinidos
(strongly typed)18, haverá uma compensação extra entre segurança e flexibilidade. Partin-
do-se da discussão anterior, é possível concluir que o mecanismo de delegação é ótimo
para construir sistemas protótipos; entretanto, sistemas de alto desempenho e de nível de
qualidade serão melhores se utilizarem uma linguagem baseada em classe, como é o caso
da C++.

18. C++ é uma linguagem altamente baseada em tipos especiais predefinidos (strongly typed). Uma das
principais características solicitadas do desenvolvimento foi essa propriedade, de modo que possa
ser colocado no mercado um software mais confiável e suscetível a testes.
212 UML E C++ CAP. 12

Herança Múltipla
Nós, intencionalmente, demos exemplos que apenas utilizam a herança simples (cada
subclasse tem uma e apenas uma superclasse imediata). No entanto, existem situações
reais que são efetivamente modeladas ao possibilitarem que uma subclasse herde de mais
de uma superclasse imediata. Por exemplo, em nosso caso da classe Pessoa, fizemos a su-
posição de que todos os estudantes não são funcionários. Entretanto, talvez tivéssemos
um melhor modelo se considerássemos que uma pessoa é tanto um estudante como um
funcionário. Pela utilização da herança simples, não seríamos capazes de representar di-
retamente esse múltiplo relacionamento parente. Outros exemplos seriam o da montado-
ra alemã de carros, JanelaDeTextosComBordas e brinquedos transformer. Uma montadora
alemã de carros possui propriedades decorrentes por ser uma montadora de carros.
A classe JanelaDeTextosComBordas possui propriedades de uma janela configurada
com bordas e de uma janela que contém texto. Finalmente um brinquedo transformer
pode atuar como um robô, um carro, um avião e um navio. O mecanismo que nos pos-
sibilita modelar essas situações é denominado herança múltipla.
Com a herança múltipla, podemos combinar diversas classes existentes (classes-pai)
para formar uma nova subclasse de todas elas. Ela poderá acessar todos os métodos e
conter todos os atributos e relacionamentos de todas as classes-pai. Por exemplo, permi-
ta-nos usar a árvore de herança de nosso exemplo original que incluía o Ornitorrinco.
Nessa árvore de herança, a classe Ornitorrinco herda atributos tanto da classe Mamífero
como da classe Ameaçado de Extinção. Esse caso corresponde a um exemplo de herança
múltipla e é mostrado na Figura 12.4.

FIGURA 12.4 Herança múltipla para o exemplo do ornitorrinco.

Um exemplo mais abrangente poderia ser o seguinte: suponhamos que Carol,


Frank, Mary, Susan e Karen sejam funcionários de uma empresa. Carol trabalha na área
administrativa e o gerente dela é Frank. Susan é engenheira e sua gerente é Mary. Karen
trabalha sob o regime de meio período e é também uma estudante. Adam também é es-
tudante, mas ele não trabalha na empresa. A árvore de herança para esse exemplo é mos-
trada na Figura 12.5.
CAP. 12 DESENHO 213

FIGURA 12.5 Herança múltipla para o exemplo da estudante/funcionária Karen.

Documentando Desenho Detalhado


Um desenho detalhado é documentado utilizando-se muitos dos mesmos diagramas que
aparecem na documentação de um modelo de análise. Em particular, o desenho detalha-
do empregará um diagrama de generalização/especialização de classe, diagramas de es-
tado e diagramas de colaboração. Diagramas que ilustram associações e agregações não
serão mostrados em um desenho porque aqueles elementos do modelo de análise terão
sido incorporados como novas classes e/ou como atributos das classes anteriormente
identificadas. E mais, um desenho detalhado incorporará diagramas de componentes. Es-
ses tipos de diagramas identificarão explicitamente as classes conformadas pelos
componentes.

■ ■ RESUMO
Neste capítulo nós abordamos rapidamente o desenho. Apesar da brevidade desse tópi-
co, temos algumas recomendações:
1. Em desenho de sistemas, acreditamos que um subsistema deva ser uma coleção
de objetos (classes) em um único domínio semântico.
2. Em desenho de sistemas, acreditamos na utilização do paradigma cliente/servi-
dor para estabelecer relacionamentos de comunicações entre subsistemas. Te-
mos certeza de que isso acarretará um software mais robusto e mais fácil de ser
mantido.
3. Para controle do software, acreditamos que tanto a estratégia baseada em pro-
cedimento como a baseada em evento são aplicáveis, dependendo da aplica-
ção/sistema que deva ser construída(o). Na realidade, temos utilizado ambas
simultaneamente em um sistema.
214 UML E C++ CAP. 12

4. Recomendamos utilizar o diagrama de implementação da UML para capturar a


topologia física na qual a aplicação/sistema de software residirá.
5. Recomendamos a utilização da classe Utilities da UML para capturar funções
de suporte adjacentes do sistema operacional e bibliotecas não baseadas em
objetos.19
6. No desenho detalhado de classes, é mais conveniente adotar as classes encontra-
das na análise e mantê-las no desenho. A seguir, deveríamos acrescentar classes
de implementação.20
7. No desenho detalhado de associações, recomendamos implementar todas as as-
sociações com seu(s) atributo(s) como classes a menos que existam questões re-
lativas a desempenho. Uma implementação de associação de classes com
atributo(s) precisamente modela a aplicação/sistema e, conseqüentemente, con-
tribui para que o software se torne mais manutenível e flexível para aplicações
futuras.
8. No desenho detalhado, utilize delegação em vez de herança caso você espere ter
a subclasse e a superclasse implementadas em diferentes processadores.
9. No desenho detalhado, é recomendável a utilização de herança múltipla somen-
te se todas as classes-pai residirem no mesmo processador. Se a(s) classe(s)-pai
residir(em) em processadores diferentes, utilize a delegação para implementar
herança múltipla.
10. Consulte um especialista em OO para descobrir oportunidades de utilizar (1)
modelos, (2) padrões, (3) estereótipos e (4) recursos mistos da UML. Ainda peça
a esse especialista que revise todos os seus diagramas. Certifique-se de que ele
tenha verificado minuciosamente (1) herança múltipla, (2) agregação, (3) associ-
ações não-binárias, (4) classes de categoria, (5) tradução de regras em constru-
ções e (6) manuseio de exceções. Ajuste seus diagramas para refletir as
oportunidades recomendadas pelo especialista.
11. Recomendamos a utilização do diagrama de componente da UML para capturar
seu desenho físico detalhado dos componentes.

19. Uma classe “utilities”na UML é uma classe utilizada para capturar variáveis e procedimentos
globais. Ela é muito mais um artefato de modelagem do que um artefato de implementação. As
variáveis e procedimentos globais não são acessíveis por meio de uma instância dessa classe.
20. O processo de acrescentar classes de implementação segue o mesmo método que descrevemos neste
livro, exceto que, agora, o domínio é a tecnologia apropriada.
FUNDAMENTOS DE C++
Fundamentos de C++
13.Fundamentos de C++

A idéia é que para ter utilidade não basta que uma propriedade de lingua-
gem seja elegante; ela também deve ser acessível no contexto de um pro-
grama real.

Bjarne Stroustup (The C++ Programming Language)

A pós a finalização das etapas de desenho, construímos um modelo que, por ora, está
preparado para ser implementado em C++. Entretanto, antes de mostrarmos como
esse modelo mapeia (traduz) facilmente em C++, precisamos abordar os fundamentos
desta linguagem. Os leitores que conhecem C++ podem pular este capítulo.
O objetivo deste e dos próximos cinco capítulos não é o de ensinar ao leitor como
programar em C++. A intenção é mostrar como um desenho criado segundo os métodos
descritos neste livro é traduzido para o código em C++. Isso é consistente com nosso ob-
jetivo de prover um guia prático para o desenvolvimento de aplicações orientadas a ob-
jeto utilizando-se C++. Recomendamos aos leitores não-familiarizados com a linguagem
C++ para adquirir um bom livro sobre ela e um outro sobre programação em C++ para
seus ambientes de desenvolvimento.

História
A linguagem de programação C foi desenvolvida por Kernighan e Ritchie na Bell Labo-
ratories em 1978. O propósito era desenvolver uma linguagem de programação portável,
pequena, simples e rápida que não impedisse o programador de fazer o que era necessá-

215
216 UML E C++ CAP. 13

rio ser feito. Ela basicamente depositava confiança plena no trabalho do programador.1 Em
1988, o Comitê X3J11 da ANSI aprovou uma norma ANSI para essa linguagem.
O desenvolvimento de C++ teve início em 1983, e foi inspirado pelo SIMULA67.
Bjarne Stroustrup, também do quadro da Bell Laboratories, desenvolveu-a como uma
evolução da linguagem C. Em um período de seis meses após seu lançamento inicial, em
1985, existiam portas comerciais de C++ disponíveis em mais de 24 plataformas; cobrindo
de PCs a mainframes. Em 1989, foi criado o comitê X3J162 da ANSI para a padronização
de C++. A ISO (International Standards Organization) associou-se mais tarde a esse tra-
balho de padronização. Em 1997, o FDIS (Final Draft International Standard) foi aprova-
do por unanimidade pelo Comitê da ANSI. Em 1998, contendo apenas pequenas
mudanças, o FDIS foi aprovado pela ISO e tornou-se um padrão internacional.3
C++ é a linguagem de programação mais amplamente utilizada voltada à progra-
mação orientada a objeto. Em virtude de a maioria de seus aperfeiçoamentos da lingua-
gem C suportar a criação de classes com a mesma riqueza encontrada nos tipos de dados
incorporados, a linguagem C++ suporta os conceitos e mecanismos discutidos neste livro.
Além disso, C++ proporcionou uma maior e mais robusta verificação de tipos do que a
maioria das linguagens para que se evitassem erros de conversão e inicialização ilegal de
estruturas de dados. Esse e outros recursos fizeram dela a linguagem ideal para desen-
volvimento orientado a objeto de sistemas de software extensos. Entretanto, ela não re-
quer nem reforça o emprego do paradigma orientado a objeto para se escrever
programas. C++ é em primeiro lugar, e essencialmente, uma linguagem de multiparadig-
mas; ela possibilita a escrita de código no paradigma clássico procedural dos métodos es-
truturados e no paradigma orientado a objeto dos métodos orientados a objeto.

Elementos de Programação
Um programa é composto de elementos denominados tokens, os quais são uma coleção
de caracteres (alfabéticos, numéricos e especiais) que constituem o vocabulário básico re-
conhecido pelo compilador. O compilador traduz esses tokens em instruções que o com-
putador possa entender. Os tokens são separados por espaço em branco e/ou texto de
comentário inserido para fins de legibilidade e documentação.
Em C, um comentário é escrito da seguinte forma:

/* possivelmente muitas linhas */

C++ acrescentou um comentário de resto de linha escrito da seguinte maneira:

// este é um novo comentário em C++

1. Isto significa que a linguagem C possibilitava aos programadores acesso a operações de nível muito
baixo e acesso direto à memória. Ela confia que o programador fará o correto trabalho.
2. O nome oficial do comitê X3J16 da ANSI foi depois mudado para J16.
3. A Norma ANSI/ISSO para a linguagem C++ está disponível para download no site da ANSI:
http://www.ansi.org.
CAP. 13 FUNDAMENTOS DE C++ 217

Esse comentário estende somente até o final da linha, ao passo que o comentário em C
poderá conter os comentários em C++. Por exemplo:

/* início do comentário em C
x = x + y; // este é o novo comentário em C++
final do comentário em C */

Existem cinco tipos de tokens:


■ Palavras-chave
■ Identificadores
■ Literais
■ Operadores
■ Pontuadores
Todos eles, individualmente, são abordados nos próximos parágrafos.

Palavras-Chave
Palavras-chave são palavras explicitamente reservadas que têm um significado predefi-
nido em C++. Elas incluem palavras para declaração de tipos de dados, formulação de
declaração, controle de acesso e novas representações de operadores e pontuadores. A
Tabela 13.1 lista as palavras-chave.4 As palavras sublinhadas fazem parte da norma ANSI
C. As palavras em negrito são necessárias para o suporte de programação orientada a ob-
jeto. As palavras-chave em itálico são interessantes por razões de implementação. Pala-
vras associadas com operadores lógicos são igualmente incluídas nessa tabela.

Identificadores
Um identificador é uma seqüência de caracteres alfanuméricos juntamente com o carac-
tere grifado. O identificador não poderá iniciar com um dígito. Embora permitido, não é
aconselhável utilizar um grifo como o primeiro caractere, pois os compiladores e o código
de biblioteca oculto utilizam o grifo como o primeiro caractere. Letras maiúsculas e mi-
núsculas são tratadas como caracteres distintos. Teoricamente, um identificador pode ser
arbitrariamente longo, porém, devido às limitações em alguns compiladores, 31 caracte-
res é um bom limite.
Segundo a convenção de atribuição de nomes da Standard Library da ANSI C, todas
as letras maiúsculas deverão ser utilizadas para constantes, macros e estruturas. Letras
maiúsculas combinadas com minúsculas são utilizadas para funções e nomes de variáveis.

Literais
Literais são valores constantes (por exemplo, 1 e 3,14159). É possível que todos os tipos
de dados nativos em C++ (veja a seção “Tipos de Dados Nativos” à frente) tenham literais.

4. Para mais detalhes sobre cada palavra reservada, consulte um livro introdutório à programação em
C++.
218 UML E C++ CAP. 13

Operadores
Operadores são caracteres e/ou seqüências de caracteres com um significado espe-
cial. A maioria dos operadores é utilizada para executar operações aritméticas ou ló-
gicas. A Tabela 13.2 lista os operadores aritméticos e seus operadores de atribuição
associados 5. Os operadores de igualdade, relacionais e lógicos são mostrados na Ta-
bela 13.3 juntamente com seus operadores de atribuição associados e suas equivalen-
tes palavras-chave reservadas.

Tabela 13.1 Palavras Reservadas em C++


and and_eq asm auto bitand
bitor bool break case catch
char class compl const const_cast
continue default delete do double
dynamic_cast else enum explicit export
extern false float for friend
goto if inline int long
mutable namespace new not not_eq
operator or or_eq private protected
public register reinterpret_cast return short
signed sizeof static static_cast struct
switch template this throw true
try typedef typeid typename union
unsigned using virtual void volatile
wchar_t while xor xor_eq

TABELA 13.2 Operadores Aritméticos em C++


Operador Matemático Descrição da Função Operador de Atribuição

% Módulo %=
+ Adição +=
++ Incremento
- Subtração -=
-- Decremento
* Multiplicação *=
/ Divisão /=
Atribuição =

Pontuadores
Os pontuadores incluem parênteses, chaves, vírgulas e dois-pontos. Eles são utilizados
para conferir estrutura a outros tokens da linguagem.6

5. Um operador de atribuição associado com um operador é uma notação no estilo reduzido para
realizar a operação e, em seguida, atribuir o resultado à variável contida no lado esquerdo do
operador. Por exemplo, x+ = 3 é o mesmo que x = x +3.
6. Para mais detalhes, consulte um livro introdutório de programação em C++.
CAP. 13 FUNDAMENTOS DE C++ 219

TABELA 13.3
Operador Palavra Descrição da Função Operador Palavra
Reservada de Reservada
Atribuição

! not Lógico NÃO


< Menor que
<= Menor ou igual a
> Maior que
>= Maior ou igual a
== Igual
!= Desigualdade (desigual)
&& and Lógico E &= and_eq
|| or Lógico OU |= or_eq
& bitand Bitwise E &=
| Bitwise inclusive OU
^ xor Bitwise exclusive OU ^= xor_eq
? Operador condicional
~ compl Conformidade

Tipos de Dados Nativos


C++ provê um conjunto predefinido de tipos de dados e operadores para manipular estes
tipos de dados.7 São predefinidos os seguintes tipos de dados:
■ Tipos de dados básicos
■ Valores constantes
■ Variáveis simbólicas
■ Tipos ponteiros
■ Tipos constantes
■ Tipos de referência
■ Tipos de enumeração
■ Tipos de matriz
■ Nomes typedef
Esses tipos de dados são descritos nos parágrafos seguintes.

Tipos de Dados Básicos


Os seguintes tipos de dados básicos8 são predefinidos:
■ Número inteiro (int, short, long)
(por exemplo, 1, 111, 1050)

7. C++ talvez defina vários tipos de dados, mas não especifica seus tamanhos. O tamanho depende do
sistema operacional e da plataforma. Isso é algo que torna C++ uma linguagem de difícil
portabilidade quando transportada de uma máquina para outra.
8. O nome é seguido pelas palavras-chave entre parênteses. As palavras-chave são utilizadas na
linguagem para identificá-los.
220 UML E C++ CAP. 13

■ Ponto decimal flutuante (float, double, long double)


(por exemplo, 1,1; 123,3456)
■ Caractere (char)
(por exemplo, ’a’, ’d’, ’2’)
■ Cadeia (String) de caracteres9 (char*)
(por exemplo, “o gato preto grande”, “número de placa x123”)
■ Booleana (bool)
■ (por exemplo, verdadeiro, falso)
Os tipos integrais10 são char, short, int e long. Eles podem ser assinados (signed) ou
não-assinados (unsigned). As aspas simples e duplas são necessárias, respectivamente, nos
exemplos dos caracteres e de suas strings.

Valores Constantes
Qualquer e toda constante literal tem um tipo de dado básico associado e é não-endere-
çável. Os diferentes tipos são:
1. Constante de número inteiro literal (decimal, octal, hex)
(por exemplo, 1, 024, 0x1A, 0X1B2)
2. Signed/unsigned e long11
(por exemplo, 1L, 8LU, 27lu)
3. Constante em ponto flutuante literal (científica, decimal)
(por exemplo, 1,23e-3, 2,14E3, 3,14159)
4. Precisão única (F/f) e precisão dupla (L/l)
(por exemplo, 1,23e-3F, 3,14159L)
5. Constante de caractere literal
(por exemplo, ’a,’ ’d,’ ’2,’ “)
6. Caracteres não-imprimíveis incluindo ’ e “ que utilizam uma barra invertida
(por exemplo, \n, \’, \“, \?, \ \, \7 (bell))
7. Constante de string literal12
(por exemplo, “ ”, “a”, “\uma string? {}[]”)

Variáveis Simbólicas
Na norma ANSI C e, em conseqüência, em C++, uma variável simbólica é identificada
por um nome provido pelo usuário. Cada variável é formada por um tipo de dado espe-
cífico e é endereçável. Dois valores são associados com uma variável simbólica:
■ rvalue: seu valor de dado
■ lvalue: seu valor de lugar (lugar na memória)
Por exemplo,

9. No capítulo que trata da Standard Template Library (STL), é introduzida uma classe string para
substituir o uso explícito de strings de caracteres.
10. O termo integral significa que todos estes tipos podem ser tratados como números inteiros.
11. O emprego de 27lu é legal, mas um número long signed não constitui uma boa prática.
12. O compilador insere um caractere nulo no final da string.
CAP. 13 FUNDAMENTOS DE C++ 221

char c ;
c = c - ’ 0 ’ ;

A primeira linha é uma definição de como a armazenagem é alocada; poderíamos


ter feito dela uma declaração utilizando a palavra-chave extern. A segunda linha subtrairá
o rvalue da constante ’0’ do rvalue da variável simbólica c. O lvalue de ’0’ é determinado
pelo compilador, não sendo diretamente acessível ao programador.
O exemplo seguinte ainda inclui a inicialização do rvalue das variáveis simbólicas:

int y = 40 ;
char c (’ d ’);

Na primeira linha, a variável simbólica y é definida e seu rvalue é inicializado como


40. Na segunda linha, a variável simbólica c é definida e é inicializada como o caractere d.

Tipos Ponteiros
Na norma ANSI C e, em conseqüência, em C++, uma variável indicadora detém um en-
dereço como seu valor na memória. Isso proporciona referência indireta e, pelo fato de
C++ ser uma linguagem altamente baseada em tipos especiais predefinidos, cada pontei-
ro tem um tipo de dado associado. Exemplos são:

int* ptr1; /* legal, porém má prática */


unsigned char *ptr2;
int *ptr3, num; // estilo preferido
int xx, *ptr4;

Nas linhas 1 e 2, mostramos que o símbolo ’*’ pode ser colocado ao lado do tipo
de dado ou próximo da variável. Nas linhas 3 e 4, verificamos que a colocação do símbolo
’*’ próximo da variável corresponde a uma melhor prática. Assim, o código fica mais fácil
de ser interpretado.
Um ponteiro pode ser inicializado com um lvalue do elemento de dado do mesmo
tipo. Por exemplo:

int j;
int *ptr = &j;

Na linha 2, a variável indicadora ptr será inicializada pelo lvalue de j.13


Em C++, todas as manipulações de strings de caracteres são feitas utilizando-se
ponteiros de caracteres (char*). Cada constante de string de caracteres é do tipo char*, e a
variável n do tipo char* pode ser inicializada em uma string de caracteres. Por exemplo:

char *strptr = "This is it\n"; // ("Isto é it\n")

Essa linha igualará o rvalue de strptr a ’This is it’ com um novo caractere de controle
de linha e um caractere nulo acompanhando essa string literal.

13. Neste contexto, o símbolo & é o operador de endereços, e não o operador and lógico.
222 UML E C++ CAP. 13

Tipos Constantes
O modificador const de uma variável simbólica transforma essa variável em uma cons-
tante simbólica. Ele é uma variável somente para leitura, de forma que deve ser iniciali-
zado. Por exemplo:

const int bufSize = 1024;

Seus endereços talvez não sejam atribuídos a um ponteiro, porém, é admitida a pre-
sença de um ponteiro para um dado const.14 Por exemplo:

const int *ptr_to_const;

É igualmente admitido um ponteiro const. Por exemplo:

int *const const_ptr;


const int * const const_ptr_to_const;

Na última linha, tanto o ponteiro como aquele para quem o ponteiro aponta devem
ser especificados quando instanciados e, possivelmente, nunca mudarão.

Tipos de Referência
Um tipo de referência é definido ao colocar-se o operador de endereço (&) em seguida ao
qualificador de tipo. Essa variável, conhecida também como tipo de referência, deve ser
inicializada. Ela tem um nome proposto e não pode ser construída para cunhar o nome
de uma outra variável, de forma que ela é uma referência const. Todas as operações sobre
a referência ou variável atuam na variável (objeto) para a(o) qual referenciam. Por exemplo:

int& y;

No exemplo, int é o qualificador de tipo, & o operador de endereço e y é a referência


ou variável.
Um segundo exemplo é:

double y = 11,12345;
double &refY = y;
refY += 2,54321; // y = 13,66666

Ele é normalmente utilizado em argumentos e como o tipo de retorno (return type)


de uma função.

Tipos de Enumeração
Uma enumeração é declarada com a palavra-chave enum e uma lista de enumeradores se-
parados por vírgulas e incluídos entre chaves. Por exemplo:

14. As atribuições de uma variável const ou de uma variável non-const a um ponteiro constante são
permitidas. Entretanto, em ambos os casos, os dados não podem ser modificados pelo uso do
ponteiro constante.
CAP. 13 FUNDAMENTOS DE C++ 223

enum {sim, nao, talvez};

Ela declara um conjunto de constantes integrais simbólicas, e não há qualquer ar-


mazenamento endereçável associado a cada enumerador. Um valor pode ser explicita-
mente atribuído a um enumerador, e um nome de etiqueta pode ser atribuído e utilizado
como um qualificador de tipo. Por exemplo:

enum Opiniao2 {sim, nao = 30, talvez};


Opiniao2 resposta = nao; // legal
Opiniao2 resposta = 1; // ilegal

A linha 3 não é legal porque resposta é uma instância do tipo de dado ’Opinião2’ e
somente pode ser atribuído um valor partindo do conjunto de constantes integrais sim-
bólicas. As únicas constantes integrais simbólicas definidas para ’Opinião2’ são sim, não
e talvez.

Tipos de Matriz
Uma definição de matriz (array) consiste de um qualificador de tipo, um identificador e
uma dimensão. Por exemplo:

float matriz_flutuante[100];

O valor da dimensão é calculado no tempo de compilação (compile time) e deve ser


uma expressão constante.15
Uma matriz é uma coleção de dados de um tipo de dado individual. Acesso e atri-
buição são estabelecidos pela posição na matriz. Por exemplo:

float yy = matriz_flutuante[10];

Os elementos da matriz são numerados iniciando com o 0. A linguagem não provê


qualquer verificação dos índices em tempo de compilação ou em tempo de execução.
Matrizes multidimensionais são declaradas especificando-se dimensões comple-
mentares. Por exemplo:

float yy[5][10];

Isso declara uma matriz bidimensional estática, com uma graduação 5x10. Estrita-
mente falando, yy é uma matriz de cinco itens, os quais por sua vez são matrizes de dez
itens. Qualquer uma das seguintes expressões, yy, yy[i], yy[i][j], pode surgir em uma ex-
pressão.

Nomes Typedef
Uma definição contendo typedef começa com a palavra-chave typedef seguida pelo tipo de
dado e pelo identificador (nome typedef). Por exemplo:

15. O capítulo que trata da Standard Template Library introduz a classe vetor, que pode ser utilizada
para substituir uma matriz.
224 UML E C++ CAP. 13

typedef int Comprimento;

Nesse exemplo, Comprimento pode ser usado em qualquer lugar em que int é per-
mitido na linguagem. Ele provê sinônimos mnemônicos para tipos de dados existentes
predefinidos, derivados e definidos pelos usuários. O identificador não é um novo tipo
de dado, mas um sinônimo para o tipo de dado existente. E mais, o identificador pode
ser usado em qualquer lugar que porventura possa surgir um nome de tipo. Typedef é uti-
lizado para facilitar a leitura dos programas e para encapsular aspectos dependentes de
máquina do programa.

O Que É uma Declaração?


Uma declaração é a menor unidade executável dentro de um programa C++. Há uma
grande variedade de tipos de declaração, mas todas elas são finalizadas por um ponto-e-
vírgula. A declaração mais simples de todas é a declaração em branco, ou null statement,
que assume a seguinte forma:

; // a declaração nula é semelhante a no-op

Uma declaração nula é proveitosa quando a sintaxe da linguagem requer uma de-
claração, mas a lógica da aplicação não.
Uma das declarações vistas com maior freqüência é a declaração de atribuição (assign-
ment statement). Por exemplo:

x = y + 1;

O lado direito dessa declaração é avaliado e convertido para um valor compatível


com a variável do lado esquerdo (y + 1 é atribuído como x). C++ provê operadores de
atribuição que combinam uma atribuição e alguns outros operadores (eles são identifica-
dos na Tabela 6.2). Por exemplo:

x += y ; // isto é o mesmo que x = x + y ;


x *= y ; // isto é o mesmo que x = x * y ;

C++ provê operadores de auto-incremento e autodecremento nas formas prefix e


postfix. Por exemplo:

++k ; // isto é o mesmo que k = k +1 ;

1 = --k ; // isto é o mesmo que k = k – 1; 1 = k ;

m = k++ ; // isto é o mesmo que m = k; k = k + 1 ;

Expressões
Uma expressão é composta de uma ou mais operações. Operações são capturadas em
C++ por operadores. Por exemplo, a operação de adição é capturada pelo operador +. Os
CAP. 13 FUNDAMENTOS DE C++ 225

argumentos de uma operação são referenciados como operandos. Por exemplo, a opera-
ção de adição requer dois operandos.
Praticamente todas as operações são unárias (requerendo somente um operando) ou
binárias (requerendo dois operandos). Operadores binários têm um operando direito e
um esquerdo. Deve-se ter cuidado, pois alguns operadores representam tanto operações
unárias como binárias. Por exemplo, o operador “*” é usado para capturar a operação de
“dereferência” quando for utilizado como um operador unário. Entretanto, na condição
de operador binário, ele é utilizado para capturar a operação de multiplicação.
Uma avaliação de expressão executa todas as operações capturadas na expressão e
gera um resultado. Normalmente, o resultado é um rvalue de um tipo de dado determi-
nado pelos tipos de dados do(s) operando(s). Embora o processo de ordenação seja bas-
tante natural, é aconselhável ao leitor consultar um livro introdutório à linguagem C++
para ter uma idéia do real ordenamento.

Declarações Compostas
Uma declaração composta corresponde a uma série de declarações envoltas pelas chaves
’{’ e ’}’ e é usada principalmente para reunir declarações em uma unidade executável. Por
exemplo, uma função C++ é uma declaração composta. Uma declaração composta é tam-
bém utilizada quando a sintaxe da linguagem permite que apenas uma única declaração
seja especificada e a lógica da aplicação requer que duas ou mais declarações sejam exe-
cutadas.16

Controle de Fluxo de Declarações


O controle de fluxo padrão é seqüencial em C++, de modo que todo programa em C++
inicia com a primeira declaração main(). Cada instrução é executada sucessivamente.
Quando a última instrução é executada, o programa finaliza. Entretanto, a execução se-
qüencial das instruções é tipicamente inadequada; a não ser para os programas mais sim-
ples. Nas seções seguintes, examinaremos algumas das instruções para controle
disponíveis em C++.17

Instrução If
A instrução if testa uma condição particular. A forma de uma instrução if é a seguinte:

if (expressão) instrução;

Sempre que a expressão é avaliada como verdadeira (diferente de zero), uma ins-
trução (ou uma instrução composta) é executada. De outro modo, a instrução é cancelada.
Qualquer que seja o evento, a próxima instrução é executada somente depois que a ins-
trução if tiver sido finalizada.
Muito parecido com o comando if é o comando if-else. Ele tem a seguinte forma:

16. Ainda que seja possível inserir uma declaração composta onde se põe uma instrução, uma
declaração composta não é terminada por ponto-e-vírgula.
17. Consulte um livro introdutório à linguagem C++ para mais detalhes e para uma série completa de
instruções de controle
226 UML E C++ CAP. 13

if (expressão) instrução-1;
else instrução-2;

Se a expressão é diferente de zero, então a instrução-1 é executada e a instrução-2 é


cancelada. Entretanto, se a expressão é zero, então a instrução-1 é cancelada e a instru-
ção-2 é executada. Da mesma maneira como acontece com a instrução if, depois de a ins-
trução if-else ter sido finalizada, é executada a próxima instrução.
Aqui temos um exemplo de utilização da instrução if-else para se obter o mínimo de
dois números:

if (y < x)
min = y;

else
min = x;
/*Abaixo se encontra a "instrução seguinte" */

cout << "mínimo é" << min;

Se y < x é avaliado como verdadeiro, então ao valor de y é atribuído o mínimo; se


y < x é avaliado como falso, então ao valor de x é atribuído o mínimo. Para os dois casos,
“min” é impresso ou é a saída.

Instrução For
A instrução for é uma instrução iterativa normalmente utilizada com uma variável que é
incrementada ou decrementada. Ela é na maior parte das vezes utilizada para passar atra-
vés de uma estrutura de dados de comprimento fixo, como é o caso de um matriz. A for-
ma sintática de uma instrução for é:

for ( instrução inicial; expressão-1; expressão-2)


instrução de execução;

A instrução inicial pode ser tanto uma declaração como uma expressão. Ela é nor-
malmente utilizada para inicializar uma variável; todavia, ela pode ser nula. A expressão-
1 serve como o controle de loop. As iterações são realizadas contanto que a expressão-1
seja avaliada como verdadeira. Em cada interação, é executada a instrução. A instrução de
execução pode ser tanto uma instrução simples como composta. Se a primeira avaliação
da expressão-1 for falsa, a instrução de execução jamais será executada. A expressão-2 é
avaliada depois de cada interação do loop. Ela é normalmente utilizada para modificar a
variável inicializada na instrução inicial. Se a primeira avaliação da expressão-1 for falsa,
a expressão-2 nunca será avaliada.
A seguir temos um exemplo simples de utilização de uma instrução for para inicia-
lizar uma matriz:

const int Max = 50;


float matriz_flutuante [Max];
for (int i=0; i <Max; i++)
CAP. 13 FUNDAMENTOS DE C++ 227

{
matriz_flutuante [i] = i;
}
Nesse exemplo, cada membro da matriz é inicializado como seu próprio valor-índi-
ce como um número em ponto flutuante. Por exemplo, matriz_flutuante [0] = 0,0, e ma-
triz_flutuante[15] = 15,0.

O Que É uma Função?


Quer estejamos utilizando métodos estruturados ou métodos orientados a objeto, há um
processo de refinamento a passos largos que envolve a decomposição de um processo
(um serviço em tecnologia orientada a objeto) em subprocessos de menor porte. As cons-
truções intituladas funções são utilizadas para capturar os processos e subprocessos. O
programa “main” em C++ é uma seqüência de “chamadas à função” que pode invocar
outras funções.
C++ proporciona os mecanismos de funções para realizar algumas tarefas, e a bi-
blioteca de C++ provê mecanismos de funções complementares. O próximo capítulo in-
troduz a mais significativa dessas bibliotecas. Até o momento, um exemplo importante
de novos mecanismos de funções supridos pelas bibliotecas é o do referente a input/out-
put, que programadores experimentados em C++ não dão o merecido valor. De fato, o
mecanismo input/output não é parte direta da linguagem. O leitor deve estar ciente de que
existem diversas bibliotecas padrão para input/output empregadas em C++. Há a biblio-
teca modelo ANSI C, stdio.h; a biblioteca inicial de fluxo (stream) C++, stream.h; e a mais
recente biblioteca de stream em C++, iostream.h. Neste livro, utilizaremos esta última bi-
blioteca e apresentaremos só seus fundamentos18.
A biblioteca iostream.h define e declara três fluxos padrão para o programador. Esses
fluxos são apresentados na Tabela 13.4. A biblioteca iostream.h sobrecarrega os operadores
shift de dois bits para obter inputs e enviar outputs, conforme descrito na Tabela 13.5.
Além de todas as funções da biblioteca, os serviços definidos pelo usuário para uma clas-
se de objetos são também funções.

TABELA 13.4 Objetos Padrão para Input e Output em C++


Fluxo Descrição

cout Saída padrão, normalmente a tela


cin Entrada padrão, normalmente o teclado
cerr Erro padrão, normalmente também a tela

TABELA 13.5 Operador Isostream


Operador Descrição

<< “Put to” fluxo de saída


>> “Get from” fluxo de entrada

18. Consulte um livro introdutório de programação em C++ para maiores detalhes.


228 UML E C++ CAP. 13

Invocação de Funções
Um programa C++ é composto de uma ou mais funções, e uma delas é a função main().
Quando um programa C++ está executando e encontra um nome de função, a função é
chamada19 e o controle é passado a essa função. Depois de a função realizar seu trabalho,
o controle é transferido de volta ao meio “solicitador” que pode, então, continuar seu pro-
cessamento. Um exemplo simples de um programa desse tipo é o seguinte:

#include <iostream.h>
#include <string.h>

main ( )
{
char * s;
cout << "\nAlô a todos!" << endl;
cout << "\nQueira digitar seu nome" << endl;
cin >> s;
if (strlen (s) > 20)
cerr << "Erro, nome é" << strlen (s) -20 <<
"excesso de caracteres" << endl;
}

Esse programa utiliza as bibliotecas string e iostream. A primeira instrução output


(saída) posiciona uma string “Alô a todos!” na tela. O símbolo “\n” garante uma nova
linha, e o endl é um identificador especial que “esvazia” o fluxo e insere uma nova linha.
A segunda instrução output é similar à primeira instrução input (entrada), que aguarda
por uma série de caracteres seguida pela chave Enter (Return). A instrução que aparece
em seguida utiliza a função strlen para obter o comprimento da série de entrada. Se esta
última for maior do que 20 caracteres, ela informa ao usuário que a string está excedendo
o número de caracteres de um valor x.

Definição da Função
O código em C++ que descreve o que uma função executa é denominado definição da fun-
ção. Ele assume a seguinte forma:

cabeçalho da função (function-header)


{
instruções
}

Tudo o que estiver antes da chave esquerda é parte do cabeçalho (header) da defini-
ção de função, e tudo o que estiver compreendido entre as chaves é o corpo da definição
de função. O cabeçalho da função assume a seguinte forma:

tipo_de_retorno nome_da_função (assinatura)

19. Em muitos livros didáticos, isto é denominado invocação (ou seja, a função é invocada).
CAP. 13 FUNDAMENTOS DE C++ 229

O tipo de retorno (return type) que precede o nome da função (function-name) deter-
mina o tipo de dado do valor retornado pela função. O mecanismo de retorno será expli-
cado mais adiante. O nome da função é auto-explicativo e a assinatura é uma lista de
parâmetros (argumentos) que a função espera serem providos pelo solicitador da função.
C++ permite a diversas funções terem o mesmo nome, porém, requer que a combinação
entre o nome de uma função e sua assinatura seja exclusiva.
Parâmetros são sintaticamente identificadores e, como tal, podem ser utilizados no cor-
po da função. Tecnicamente, eles constituem parâmetros formais porque são placeholders de
valores reais passados à função quando esta é chamada. Mediante a invocação da função, os
valores do argumento correspondente ao parâmetro formal são utilizados no corpo da fun-
ção quando ela for executada. A seguir, temos uma definição de função para a função min:

int min (const int x, const int y)


{
if (y < x)
return (y);
else
return (x);
}

A instrução return tem dois propósitos. Primeiro, quando é executada uma ins-
trução de retorno, o controle é passado imediatamente de volta ao solicitador. Segun-
do, se uma expressão vem em seguida à palavra-chave return, o valor da expressão é
devolvido ao solicitador. Quando existir uma expressão, ela deverá ser conversível
quanto à atribuição ao tipo de retorno do cabeçalho da definição de função. Note que
quando não há qualquer expressão, o tipo de retorno da função deve ser do tipo void.
Isso é empregado quando o solicitador não espera retorno de valor(es).

Protótipo da Função
Em C++, uma função pode ser declarada antes de ser definida. Essa capacidade é utiliza-
da em programação orientada a objeto para preservar o encapsulamento. Uma declaração
desse tipo é denominada protótipo da função (function prototype) e tem a seguinte forma:

tipo nome ( lista de declaração de argumentos );

Nesse caso, tipo é o tipo de retorno da função, que pode ser um tipo de dado defi-
nido pelo usuário ou um tipo void. O nome é o nome da função, e a lista de declaração
de argumentos é uma relação de tipos de dados separados por vírgulas. Essa lista define
os tipos de dados dos valores que o solicitador deve prover à função. É também bastante
comum termos identificadores de argumentos na lista; dessa maneira, o protótipo pode
ser idêntico ao cabeçalho da função.

Inlining
Em C++, quando a palavra-chave inline precede uma declaração da função, o compilador
tentará substituir a “chamada à função” pelo código. O compilador analisará a função e
proverá o equivalente semântico de uma versão non-inline da função. O compilador não
230 UML E C++ CAP. 13

permitirá que funções complexas sejam executadas (inlined). Esta palavra-chave basica-
mente substitui a expansão Macro.20

Classe de Armazenamento
Toda variável e função na linguagem núcleo C++ contam com dois atributos: tipo e classe
de armazenamento. Temos abordado tipos de dados nativos e, por ora, precisamos dis-
cutir sobre classes de armazenamento. Existem cinco tipos de classes de armazenamento:
automática, externa, registro, estática e volátil. Suas palavras-chave correspondentes são:
auto, extern, register, static e volatile.

Auto
Variáveis declaradas dentro de um corpo de função são, por padrão, automáticas, que ge-
ralmente correspondem à classe de armazenamento de uso mais geral. Se uma instrução
composta contiver declarações variáveis, então as variáveis somente poderão ser atuadas
quando submetidas ao escopo da instrução composta que as envolve. Declarações de va-
riáveis dentro de blocos são, implicitamente, da classe automática de armazenamento. A
palavra-chave auto é utilizada apenas para especificar explicitamente a classe automática
de armazenamento.
Quando um bloco é acessado, o sistema aloca memória (normalmente do espaço da
pilha) para as variáveis automáticas. Dentro do bloco, estas variáveis são definidas e con-
sideradas locais ao bloco. Quando um bloco deixa o programa, este libera a memória re-
servada para as variáveis automáticas. Os valores para estas variáveis não estão mais
disponíveis. Se o bloco é acessado novamente, o sistema mais uma vez alocará memória
para as variáveis automáticas, porém, os valores anteriores serão perdidos.

Extern
Quando uma variável é declarada fora de uma função, o armazenamento é atribuído per-
manentemente a ela e sua classe de armazenamento é extern. Uma variável desse tipo é
considerada global a todas as funções declaradas após ela. Além do mais, mediante as
saídas do bloco ou função, a variável externa permanece “viva”. As variáveis externas
nunca desaparecem; elas existem ao longo da vida útil do programa. Elas são utilizadas
para transmitir valores pelas funções. Entretanto, isso pode ser perigoso porque a variá-
vel pode ficar oculta se o identificador for redefinido. A palavra-chave extern é utilizada
para informar ao compilador que procure em qualquer outro lugar a definição desta va-
riável. A variável pode estar nesse ou em algum outro arquivo.
Em virtude de as funções poderem também obter informações por meio do mecanismo
de passagem de parâmetros, os especialistas orientados a objeto podem recomendar o uso
esporádico de variáveis externas porque elas violam o princípio de encapsulamento.

Register
A classe de armazenamento de registro informa ao compilador que as variáveis associa-
das deverão ser armazenadas em registros de memória de alta velocidade se isto for física

20. A maioria dos guias de estilos recomenda que se evite a utilização de constantes macro e macros
semelhantes às funções porque elas são inseguras, difíceis de depurar e podem facilmente “inchar”o
tamaho do arquivo executável. C++ provê alternativas muito melhores.
CAP. 13 FUNDAMENTOS DE C++ 231

e semanticamente possível. Pelo fato de as restrições semânticas e de recursos talvez não


possibilitarem essa solicitação, as variáveis adotarão o padrão automatic quando elas não
puderem passar para o modo register.
Isso deverá ser utilizado somente quando o programador estiver preocupado com ve-
locidade. Então selecione algumas variáveis que são mais freqüentemente acessadas e decla-
re-as como da classe de armazenamento de registro. Entretanto, esteja ciente de que os
compiladores de otimização contemporâneos são de modo geral mais astutos do que o pro-
gramador. A maioria de nós considera esta classe de armazenamento de utilidade limitada.

Static
Declarações estáticas permitem que uma variável local retenha seu valor anterior quando o
bloco ou função é acessado novamente. Isso é totalmente oposto a uma variável automática,
que perde seu valor na saída e precisa ser reinicializada na reentrada. Um exemplo desse uso
de retenção de valor de declarações estáticas é mostrado pela inserção de códigos à função
min para manter uma contagem do número de vezes em que ela é invocada:

int min (const inst x, const int y)


{
static int contagem_invocada = 0 ;
contagem_invocada++ ;
if ( y < x )
return (y);
else
return (x);
}

A classe de armazenamento estática (static) foi utilizada de forma depreciativa em


declarações externas a fim de prover um mecanismo de privacidade para a manutenção
da modularidade de programas.21 O termo privacidade refere-se à visibilidade ou às res-
trições de escopo no tocante à acessibilidade de variáveis e funções. Declarações externas
estáticas são visíveis somente dentro do arquivo no qual elas são definidas. Assim, dife-
rentemente das variáveis externas que podem ser acessadas por outros arquivos, uma de-
claração estática apenas fica disponível em todo o seu próprio arquivo.
Em C++, as variáveis externas (extern) e estáticas são inicializadas com zero se elas
não forem explicitamente inicializadas pelo programador. Em contrapartida, variáveis
automáticas (auto) e de registro (register) não são inicializadas pelo sistema e podem ini-
ciar com valores “garbage”. Para programação orientada a objeto, a forma estática deve-
ria ser utilizada unicamente como mecanismo de privacidade.

Volatile
Volatile é uma classe de armazenamento que instrui o compilador a ler o valor da variável
de sua fonte toda vez que ela for acessada, em vez de armazená-la em um registro mais
veloz de processador. Este tipo de classe de armazenamento possibilita a um programa
utilizar valores que podem mudar devido a circunstâncias fora do controle explícito do

21. Em vez de utilizar a variável static para promover privacidade, um namespace sem nome poderia ser
utilizado para tal propósito.
232 UML E C++ CAP. 13

programador ou dentro de aplicações de multiencadeamento. Por exemplo, um progra-


ma pode ler o valor da taxa atual de bits de um modem diretamente de uma porta. En-
tretanto, o valor pode se alterar durante a execução do programa devido a condições
variáveis na linha. O compilador poderia erroneamente assumir que o valor fosse inalte-
rável e armazenar o valor em um registro, perdendo com isso uma mudança no valor.

Conversão de Tipos
C++ é uma linguagem de programação altamente baseada em tipos especiais predefi-
nidos (strongly typed). Isso é bom e ruim, dependendo de sua perspectiva. É bom no
sentido de que força as pessoas a claramente identificarem tipos, evita acessos incor-
retos a dados e garante que as operações sobre valores sejam semanticamente válidas.
Por outro lado, uma utilização excessiva de tipos especiais predefinidos dificulta a
ocorrência de transição no tratamento de um valor em um nível de abstração (por
exemplo, como um objeto formato) em uma parte do programa e tratá-lo em um di-
ferente nível de abstração (por exemplo, como um objeto retângulo, círculo ou linha)
em outra parte do programa.
Para dotar o programador com a habilidade para poder modificar tipos e classes
de armazenamento, a linguagem C++ provê diversos operadores de conversão de ti-
pos. Estes são: static_cast, const_cast, dynamic_cast e reinterpret_cast. A utilização de
operadores de conversão de tipos possibilita ao programador utilizar valores de um
modo apropriado para o domínio ao mesmo tempo que permite que o compilador re-
force restrições de tipos.

static_cast
C++ provê o operador static_cast para conversão entre tipos com a verificação destes sen-
do realizada em tempo de compilação. Este operador é utilizado na maioria das conver-
sões entre tipos de dados fundamentais, como é o caso de int, double, float e assim por
diante. Um exemplo de utilização de static_cast é dado a seguir:

float t = 3,14;
int x = static _cast <int> (t); // x tem um valor de 3

A primeira linha declara uma variável float, t, e atribui a ela o valor de 3,14. A se-
gunda linha declara uma variável int, x, e lhe atribui o resultado de converter 3,14 a um
valor inteiro.

const_cast
O operador const_cast é provido para colocar de lado valores const ou volatile. O operador
const_cast não muda tipos. Ele afeta apenas a “const-ness” de um valor. Ele é geralmente
utilizado dentro de um método de classe que tenha sido declarado const para violar aque-
la declaração.22 O emprego de const_cast é um tópico de C++ muito avançado e deverá
ser realizado unicamente sob circunstâncias muito especiais. É recomendável ao leitor

22. Não constitui boa prática violar promessas feitas na declaração de um método. Um dos piores casos
é prometer que não haverá mudanças de valores e, em seguida, forçá-los a mudar.
CAP. 13 FUNDAMENTOS DE C++ 233

que esteja muito interessado em mudar a “const-ness” de algum atributo que recorra a um
livro avançado sobre C++.

dynamic_cast
O operador dynamic_cast está incorporado à linguagem C++ para proporcionar o tipo de
operação cast provida por C.23 O operador dynamic_cast é normalmente utilizado para
fins de downcasting. Downcasting corresponde a casting partindo de um ponteiro de clas-
se-base até um ponteiro de classe derivada. Isso pode ser perigoso, mas o dynamic_cast
utiliza informações em tempo de execução para garantir que o cast seja permitido. Se o
cast não for permitido, o resultado é 0 (zero). Segue um exemplo de utilização de um ope-
rador dynamic_cast:

Formato *f = static_cast<Formato *> (new Quadrado ( ));


// Quadrado é um tipo de Formato
Quadrado *meuQuadrado = dynamic_cast<Quadrado *> (f); // meuQuadrado aponta para f
Circulo *oops! = dynamic_case<Circulo *> (f) ; // oops! aponta para 0

A primeira linha nesse exemplo declara uma variável, f, que é um ponteiro para
uma instância da classe Formato. Formato é uma classe virtual. Uma instância do tipo Quadra-
do é criada utilizando-se o construtor padrão Formato(), e é forçada a ser do tipo Formato
por designação do ponteiro f.24 A segunda linha utiliza o operador dynamic_cast para pos-
sibilitar ao ponteiro de um Formato converter-se em um ponteiro de um Quadrado. A
terceira linha utiliza novamente dynamic_cast em uma tentativa de converter o ponteiro
de um Quadrado para um ponteiro de um Círculo. A tentativa fracassa porque o Forma-
to não é na realidade um Círculo, e sim um Quadrado.

reinterpret_cast
O operador reinterpret_cast é principalmente provido para permitir conversões fora do pa-
drão, mas pode ser utilizado para conversões padrão. Um exemplo de utilização de rein-
terpret_cast é dado a seguir:

main ()
{
int i = 1, *intPtr;
void *voidPtr = &i;
intPtr = reinterprete_cast<int *> (voidPtr);
}

Esse exemplo mostra como um ponteiro do tipo void pode ser convertido em um
ponteiro do tipo int. É preciso observar que a utilização de reinterpret_cast pode ocasionar
sérios erros em tempo de execução. Além do mais, essa conversão pode exibir diferentes
comportamentos em computadores distintos.

23. C++ suportou o mecanismo casting de C, mas o padrão introduziu quatro diferentes operadores cast,
que são o mecanismo de casting preferido.
24. Construtores padrão são discutidos mais adiante neste livro. Tudo que é importante, por ora, é que
criamos uma instância de um quadrado.
234 UML E C++ CAP. 13

Namespace
Esta seção aborda um recurso provido pela linguagem C++ que é muito valioso para sis-
temas muito extensos e de pouco valor para programas pequenos. Ele é denominado na-
mespace. Namespace é um mecanismo que promove a compreensão, facilita a manutenção
e suporta desenvolvimento de sistemas muito grandes. Ele provê um meio de decidir so-
bre nomes utilizados em programas. Este recurso aceita que diversas equipes de desen-
volvimento utilizem os mesmos nomes para classes, variáveis e funções, sem conflitar
com a atribuição de nomes no nível do código-fonte.
Um namespace provê um escopo em que identificadores e variáveis globais são po-
sicionados. Para definir um namespace, utiliza-se uma palavra-chave e um nome para na-
mespace, e colocam-se todos os elementos de código associados a ele dentro de chaves. O
programa a seguir ilustra a definição de um namespace contendo um conjunto de variáveis
globais:

namespace minhasConstantes {
const int i=34;
const float pi=3,1415;
}
namespace suasConstantes {
const int i=29;
}

Esses namespaces serão utilizados nos exemplos de programas descritos a seguir.


Para utilizar um membro de um namespace, pode-se identificar um escopo com a
instrução using antes de o nome ser utilizado ou o nome do membro pode ser qualificado
com o nome do namespace e do operador de escopo, ::. Isso fica demonstrado no seguinte
trecho de código:

using namespace suasConstantes;


int j = i;
int k = minhasConstantes::i

Nesse trecho de código será atribuído a j um valor de 29 porque ele utilizará o valor
de i definido em um namespace denominado suasConstantes. Será atribuído à variável k
um valor de 34 porque o operador de escopo identifica o i ora utilizado como pertencente
ao namespace denominado minhasConstantes.
A palavra-chave empregada também pode ser utilizada para identificar somente
elementos especiais dentro de um namespace. Isto é conseguido identificando-se o mem-
bro do namespace que está prestes a ser utilizado. Um exemplo é o seguinte:

using namespace minhasConstantes::pi;

Isso propicia ao compilador saber que, nos locais em que cada pi aparece, ele deve utilizar
o membro em minhasConstantes.
CAP. 13 FUNDAMENTOS DE C++ 235

Enfoque Recomendado
Este capítulo tratou de alguns dos conceitos básicos de C++. Os leitores não-familiariza-
dos com essa linguagem devem:
1. Adquirir um livro de programação em C++
2. Adquirir um livro didático que aborde a Norma C++

■ ■ RESUMO
Neste capítulo, aprendemos sobre:
1. Tokens
2. Palavras-chave importantes
3. Tipos de dados nativos
4. Instruções
5. Fluxo duplo de instruções (if e for)
6. Funções
7. Invocar uma função
8. Classes de armazenamento
9. Operadores cast
10. Namespace
236 UML E C++ CAP. 13
Implementando Classes
IMPLEMENTANDO CLASSES
14.Implementando Classes

S aber o que pensamos, sermos donos de nosso próprio significado, formará


uma fundação sólida para idéias grandiosas e de valor.

C. S. Peirce, How to Make Our Ideas Clear

O mecanismo de classe em C++ possibilita aos desenvolvedores definir seus próprios


tipos de dados além daqueles nativos à linguagem. Na implementação inicial da
aplicação/sistema, desenvolvedores utilizam este recurso para implementar as classes
descobertas no modelo. Então, em futuras versões da aplicação, os desenvolvedores cons-
tatarão a utilidade das classes quando: (1) necessitarem agregar funcionalidade a um tipo
de dado existente (quer ele seja nativo ou definido pelo usuário), e (2) necessitarem in-
troduzir uma nova abstração que não possa ser mapeada (traduzida) em um dos tipos de
dados definidos ou ser dele derivada.

Componentes de uma Classe


Uma classe C++ é composta de quatro partes principais:
1. Coleção de membros de dados. Na tecnologia orientada a objeto, esta coleção
corresponde ao conjunto de atributos. Poderá haver nenhum ou um número
maior de membros de dados de qualquer tipo de dado nesta coleção.
2. Coleção de declaração de funções membros. Esta coleção é o conjunto de pro-
tótipos de funções que podem ser aplicados aos objetos dessa classe. Na tecno-
logia orientada a objeto, esta coleção corresponde aos serviços. Poderá haver
nenhum ou um número maior de protótipos de funções nesta coleção.
3. Nível de visibilidade. Cada membro (dado ou função) pode ser especificado
como possuindo o seguinte nível de acesso (visibilidade): private (privada), pro-

237
238 UML E C++ CAP. 14

tected (protegida) ou public (pública). Na tecnologia orientada a objeto, todos os


membros de dados devem ser privados e todos os serviços devem ser públicos.1
4. Nome de etiqueta associado. Este nome serve como um qualificador de tipo
para a classe definida pelo usuário. Conseqüentemente, o nome poderá ser uti-
lizado no programa em que possa surgir o tipo de dado nativo.
As funções membros públicas são referidas como a interface da classe. Uma classe
com membros de dados privados e funções membros públicas é chamada de tipo de dado
abstrato.2 Uma classe em C++ suporta os princípios da ocultação de informações e encap-
sulamento. Ela ainda agrupa uma série de membros de dados a um conjunto de funções,
e define as características de todas as instâncias criadas por aquela classe. Em resumo, ela
provê a unidade básica de reutilização.

Definição de Classe
Uma definição de classe é composta de duas partes: cabeçalho da classe (class header) e
corpo da classe (class body). O cabeçalho da classe é composto da palavra-chave class se-
guida pelo nome de etiqueta da classe. O corpo da classe é incluído dentro de um par de
chaves; a chave de fechamento deve ser seguida por um ponto-e-vírgula ou por uma lista
de declarações. Por exemplo:

class Pessoa
{
private:
char nome[40];
char sexo;
int idade;
};
class Cachorro
{
private:
char nome[40];
int idade ;
} meuCachorro, Lassie;

Note que no exemplo da classe Pessoa não há nenhuma lista de declarações. Entre-
tanto, na classe Cachorro, são declarados dois objetos: meuCachorro e Lassie.

Corpo da Classe
Dentro do corpo da classe, são especificados membros de dados, funções membros e seus
níveis associados de visibilidade.

1. Isso corresponde à implementação dos princípios de ocultação de informações e de encapsulamento.


2. Agora você sabe por que algumas pessoas dizem que foi a análise orientada a objeto quem descobriu
os tipos abstratos de dados.
CAP. 14 IMPLEMENTANDO CLASSES 239

Visibilidade
Cada membro da classe tem um nível de visibilidade. Existem três níveis de visibilidade
(pública, privada e protegida) possíveis a um membro. Caso um nível de visibilidade não
seja explicitamente especificado para um membro, é utilizada a visibilidade padrão, do
tipo privada. As regras de utilização de níveis de visibilidade dentro do corpo da classe são:
■ Todas as declarações de membros subseqüentes às palavras-chave public: são aces-
síveis por outras classes (objetos).
■ Todas as declarações de membros subseqüentes às palavras-chave private: são aces-
síveis somente pela própria classe.
■ Todas as declarações de membros subseqüentes às palavras-chave protected: são
acessíveis unicamente pela classe e suas subclasses.
■ O uso posterior das palavras public:, protected: ou private: irá ter prioridade sobre de-
finições anteriores somente para os membros subseqüentes às palavras-chave pu-
blic:, protected: ou private:.
A ordem preferida para organização dos membros da classe é pública, protegida e
privada. Quando um objeto é instanciado, todos os membros (dados e funções) são aces-
síveis às suas funções membros. O nível de visibilidade aplica-se unicamente a funções
de um outro objeto, quer ele esteja na mesma classe ou em classe diferente. Quando uma
função tem acesso aos dados privados de todos os objetos de sua classe, ela tem escopo
de classe (class scope). A maioria das funções membro unicamente tem acesso aos dados
privados do objeto para o qual ela foi invocada; isto é o escopo de objeto (object scope).

Membros de Dados
A declaração de membros de dados é idêntica às declarações de variáveis na linguagem,
com a exceção de que não é permitido um inicializador explícito. Por exemplo, o código
a seguir não funcionará:

class Pessoa
{
int altura = 0; int peso; char * nome; /* Exemplo Ilegal */
}

A inicialização é executada no construtor para a classe (veja em capítulo mais adian-


te). De maneira similar às declarações de variáveis, é válido agrupar a declaração int de
múltiplos membros de dados em uma declaração. Por exemplo:

class Pessoa
{
int altura, peso; char * nome;
}

Quando possível, declare membros de dados em uma proporção crescente de arma-


zenamento para otimizar o alinhamento de armazenamento em todas as máquinas. En-
tretanto, os membros de dados também podem ser dos tipos definidos pelo usuário. Um
objeto de classe pode ser declarado como um membro de dado somente se a definição de
classe já tiver sido vista pelo compilador antes de seu emprego como um membro de
240 UML E C++ CAP. 14

dado. Entretanto, quando uma declaração de um membro de dado é um ponteiro ou re-


ferência para uma classe, poderá ser utilizada uma declaração antecipada. Por exemplo,
a seguir temos uma definição de um objeto Mulher utilizando uma declaração antecipada
para um objeto Homem:

class Homem; // declaração antecipada


class Mulher;
{
private:
char nome[40];
Homem * marido; // ponteiro para objeto Homem, que é marido
}

Uma classe não é considerada definida até que a chave de fechamento do corpo da
classe seja vista pelo compilador; entretanto, a classe é considerada como declarada após
o surgimento da chave inicial. Isto possibilita a uma classe definir ponteiros e referências
a si própria na qualidade de membros de dados. Considere uma relação de vínculos de
pessoas:

class PessoaDoVinculo
{
private:
Pessoa eu ;
PessoaDoVinculo *next;
PessoaDoVinculo *prev;
}

Funções Membros
As funções membros de uma classe são declaradas dentro do corpo da classe. Uma de-
claração consiste do protótipo da função. O protótipo da função é composto de um tipo
de retorno e de um nome seguido por uma assinatura fechada por parênteses. A assina-
tura consiste de uma lista de tipos de argumentos separados por vírgulas. Cada qualifi-
cador de tipo poderá ser seguido por um tipo de argumento. Por exemplo:

class Pessoa
{
public: // funções membro
char* obterNome ( );
char obterSexo ( );
int obterIdade ( );
void definirNome (char *)
void definirSexo (char s);
void definirIdade ( int a);
private: // membros de dados
char nome[40];
char sexo;
int idade, altura, peso;
}
CAP. 14 IMPLEMENTANDO CLASSES 241

A lista de argumentos é referida como a assinatura de uma função porque ela faz a
distinção entre duas funções com o mesmo nome. O nome em si não necessariamente
identifica especificamente uma função. Entretanto, o nome e sua assinatura irão, especifi-
camente, identificar uma função. Por exemplo:

class Pessoa
{
public: // funções membros
char obterSexo ();
void definirSexo (char );
void definirSexo (int );
...
private: // membros de dados
char nome[40];
char sexo;
int idade, altura, peso;
}

Podemos empregar a função definirSexo() utilizando um número inteiro como um


argumento e char como um argumento. Talvez isso seja necessário porque em uma apli-
cação o sexo poderá ser capturado como um número inteiro (por exemplo, 1 para femi-
nino, 0 para masculino), enquanto em outra aplicação o sexo poderá ser capturado como
um caractere (por exemplo, f para feminino, m para masculino).
As funções membros distinguem-se de outras funções devido às seguintes caracte-
rísticas:
■ Funções membros são favorecidas por privilégios de acesso pleno aos membros pri-
vados, protegidos e públicos da classe, ao passo que outras funções somente têm
acesso ao membro público da classe.
■ Funções membros de uma classe não têm privilégios de acesso a membros de uma
outra classe. Entretanto, quando uma classe tem um relacionamento com outra clas-
se, ela tem acesso aos membros públicos da outra classe.
■ Funções membros são definidas somente dentro do escopo de classe, ao passo