laboratório de programação orientada a objetos para engenharia
TRANSCRIPT
Escola Politécnica da Universidade de São Paulo
Laboratório de Programação Orientada a
Objetos para Engenharia Elétrica
Aula 5: Encapsulamento e Tipo Abstrato de
Dados
PCS 3111
Agenda
Questões Típicas
Modularização do Software
Princípios de Modularização do Software
Encapsulamento
Tipo Abstrato de Dados (TAD)
Contrato e Implementação de TAD
Classe como TAD
Classe e Arquivos
2
Questões Típicas
O que caracteriza um bom projeto de software? • Uma boa organização do software
Qual é a estratégia para essa organização? • Modularização do software
Existem princípios que ajudam na modularização do software? • Abstração
• Refinamento passo a passo
• Ocultação de informações
Como deve ser um software? • Reusável
• Extensível
3
Questões Típicas
Quais são os benefícios desses princípios na
modularização do software?
• Facilidade de construção do software
• Facilidade de teste e depuração do software
• Facilidade de modificação do software
• Legibilidade do software
• Facilidade de comunicação entre os desenvolvedores
• Menor time-to-market
4
Modularização do Software
Quebra a solução de software em módulos
Cada módulo pode ser implementado e
compilado independentemente
Os módulos podem ser ligados (linked)
formando a solução completa
Permite isolar os erros cometidos e eliminar
redundâncias
Torna o desenvolvimento do software
gerenciável
5
Princípios de Modularização
Abstração
Refinamento Passo a Passo
Ocultação de Informações
6
Abstração
Torna visíveis somente detalhes do módulo que
interessam
7
Placa vista com Circuitos
Integrados, Resistores,
Capacitores
PC
PC visto com Placa Mãe, Placas de
Vídeo, Placa de Rede, Placa de
Som, Placa de Memória
Abstração
Uma solução modular pode ter muitos níveis de
abstração
• Abstração procedimental: a ação de “ligar” o
equipamento de ar condicionado pode ter uma
sequência de passos procedimentais. Por exemplo:
Pegue o controle remoto
Acione o botão de ligar
Escolha a temperatura
Ative a temperatura escolhida
8
Abstração
Uma solução modular pode ter muitos níveis de
abstração
• Abstração de dados: “equipamento” pode ser
caracterizado por outros dados: Por exemplo:
Fabricante
No. de série
Modelo
Ano
9
Refinamento Passo a Passo
Uma solução modular pode ter muitos níveis de
refinamento
A medida que se desenvolve o software, mais
detalhes são incorporados aos módulos
10
Refinamento Passo a Passo
Exemplo 1:
• “Escolha a temperatura”, um procedimento da
abstração “ligar”, pode ser definido como:
Acione o botão “temp+” para aumentar a temperatura ou
Acione o botão “temp-” para diminuir a temperatura
Exemplo 2:
• “Fabricante”, um dado da abstração “equipamento”,
pode ser definido como:
string de até 10 caracteres. Ex. “Brastemp”
11
Ocultação de Informações
Uma solução modular esconde detalhes do
módulo de outros módulos
Especifica um módulo do ponto de vista externo
Separa o propósito de um módulo de sua
implementação
Restringe o acesso de outros módulos a
detalhes de implementação e a qualquer
estrutura de dados do módulo
12
Encapsulamento
Uma solução modular pode ser obtida usando
princípios na modularização de software
apresentadas anteriormente, mantendo a
integridade dos dados
O encapsulamento esconde detalhes do módulo
que não essenciais para compreender suas
características
O encapsulamento permite o acesso aos dados
do módulo somente através de interfaces
definidas
13
Encapsulamento
O que há dentro dos objetos?
14
Variáveis
(atributos)
Métodos (operações)
Tipo de Dados
Conjunto de valores que uma variável pode
assumir, em uma dada linguagem de
programação. Por exemplo, em C++:
• int i = 1234;
• char a = ‘A’;
• bool flag = true;
15
Tipos de Dados
Exemplo: Inteiros (Integer)
• Operadores:
Aritméticos Unários (+; -)
• Ex: +5 resulta 5
• Ex: -8 resulta -8
Aritméticos Binários (+; -; *; /; %)
• Ex: 2 + 3 resulta 5
• Ex: 8 % 3 resulta 2 (resto)
Relacionais (==; !=; <; <=; >; >=)
• Ex: a != b resulta True se a é diferente de b, caso
contrário, é False
• Ex: a > b resulta True se a é maior do que b, caso
contrário, é False
16
Operadores sobre
Inteiros encapsulam
números inteiros
Tipo Abstrato de Dados (TAD)
É um conceito central de design de programa,
sobretudo na abordagem OO
É independente de linguagem específica de
programação
Incorpora princípios de modularização de
software
Encapsula uma coleção de dados e um conjunto
de operações sobre esses dados
Pode ter várias implementações diferentes
17
Tipo Abstrato de Dados (TAD)
18
Min, Max
Ventilador
Aquecedor
Refrigerador
Ar Condicionado
Residencial
Painel de
Controle
Contrato (Especificação) de um TAD
TAD nome_TAD é
Dados
[Descreve a estrutura de dados]
Operações Construtor
Valores iniciais: [Dados usados para iniciar o objeto]
Processo: [Inicia o objeto]
Operação 1
Entrada: [Dados vindos do cliente]
Pré-condições: [Estado (condição) necessário(a) do sistema antes de executar a operação]
Processo: [Ações executadas com os dados]
Saída: [Dados retornados para o cliente]
Pós-condições: [Estado do sistema depois de executar a operação]
Operação 2 ...
Operação n ...
Destrutor
Processo: [Destrói o objeto]
Fim TAD nome_TAD
19
( Adaptado de Ford e Topp, 1996)
Importante:
O Contrato não
especifica a
representação
dos dados e nem
os algoritmos que
implementam as
operações
Exemplo: Contrato do TAD Sensor
20
TAD Sensor é
Dados
Um número real não negativo que especifica o código do sensor.
Um valor booleano que indica o estado do sensor.
Operações Construtor
Valores iniciais: O código default do sensor.
Processo: Atribuir um valor inicial ao sensor.
Ativação
Entrada: Código do sensor.
Pré-condições: Nenhuma.
Processo: Ativar sensor.
Saída: Nenhuma.
Pós-condições: Nenhuma.
Leitura
Entrada: Código do sensor.
Pré-condições: Nenhuma.
Processo: Ler estado do sensor.
Saída: Retorna estado do sensor.
Pós-condições: Nenhuma.
Destrutor
Processo: Destruir sensor.
Fim TAD Sensor
Classe como TAD
Uma Classe definida pelo usuário implementa
um TAD em C++
21
Privado
Dados membros: valor 1, valor 2...
Operações internas
Público
Construtor
Operação 1
Operação 2
Destrutor
Classe
(Adaptado de Ford e Topp, 1996)
Encapsulamento
Interface de Acesso
Exemplo em C++
22
Privado
Estado
Público
Construtor
Ativação
Leitura
Destrutor
Classe Sensor
class Sensor {
private:
bool estado;
public:
Sensor()
{
estado=false;
}
void Sensor::ativaSensor()
{
estado = true;
}
bool leSensor() {
return estado;
}
~Sensor()
{}
};
Stack
23
Push Pop
Clear Peek
Contrato do TAD Stack
24
TAD Stack é
Dados
Uma lista de itens com uma posição “topo” que indica o topo da Stack.
Operações Construtor
Valores iniciais: Nenhum.
Processo: Inicia o topo da Stack.
StackEmpty
Entrada: Nenhuma.
Pré-condições: Nenhuma.
Processo: Verificar se a Stack está vazia.
Saída: Retorna True se estiver vazia e False, caso contrário.
Pós-condições: Nenhuma.
Pop
Entrada: Nenhuma.
Pré-condições: Stack não está vazia.
Processo: Remover o item do topo da Stack.
Saída: Retorna o elemento do topo da Stack.
Pós-condições: Elemento do topo da Stack é removido. (Ford e Topp, 1996)
Contrato do TAD Stack
25
Push
Entrada: Um item da Stack.
Pré-condições: Nenhuma.
Processo: Armazenar o item no topo da Stack.
Saída: Nenhuma.
Pós-condições: A Stack tem um novo elemento no topo.
Peek
Entrada: Nenhuma.
Pré-condições: Stack não está vazia.
Processo: Recuperar o valor do item no topo da Stack.
Saída: Retorna o valor do item do topo da Stack.
Pós-condições: A Stack está inalterada.
ClearStack
Entrada: Nenhuma.
Pré-condições: Nenhuma.
Processo: Deletar todos os itens da Stack e resetar o topo da Stack.
Saída: Nenhuma.
Pós-condições: A Stack está resetada para a sua condição inicial.
Destrutor
Processo: [Destrói a Stack]
Fim TAD Stack (Ford e Topp, 1996)
Implementação do TAD Stack
Dado um contrato de um TAD, escolher a
melhor forma de implementação
Uma TAD Stack pode ser implementada usando
diferentes estruturas de dados, como por
exemplo, um vetor (Array) ou uma lista ligada
26
Classe Stack
(Implementação usando Vetor)
27
#include <iostream>
using namespace std;
int maxStackSize = 10;
class Stack {
private:
int stackElement[10];
int top;
public:
bool stackEmpty();
int pop();
void push(int x);
int peek();
void clearStack();
};
bool Stack::stackEmpty(){
if (top == -1)
return true;
else
return false;
}
int Stack::pop(){
int x;
if (stackEmpty()) {
cout << "pilha vazia!!! ";
return -1;
}
else {
x = stackElement[top];
top--;
return x;
}
}
void Stack::push(int x) {
if (top < maxStackSize-1){
top++;
stackElement[top] = x;
} else
cout << "pilha cheia" << endl;
}
Classe Stack
(Implementação usando Vetor)
28
Saída
pilha vazia!!!-1
30
20
10
pilha vazia!!! -1
1300
pilha vazia!!! -1
int Stack::peek(){
int x;
if (stackEmpty()){
cout << "pilha vazia!!! ";
return -1;
}
else {
x = stackElement[top];
return x;
}
}
void Stack::clearStack() {
for (int x = 0; x < maxStackSize; x++) {
stackElement[x] = 0;
top = -1;
}
}
int main() {
Stack s;
s.clearStack();
cout << s.peek() << endl;
s.push(10);
s.push(20);
s.push(30);
cout << s.pop() << endl;
cout << s.pop() << endl;
cout << s.pop() << endl;
cout << s.pop() << endl;
s.push(1100);
s.push(1200);
s.push(1300);
cout << s.pop() << endl;
s.clearStack();
cout << s.peek() << endl;
return 0;
}
Classe Stack
(Implementação usando Vetor)
29
#include <iostream>
using namespace std;
class Stack {
private:
static const int maxSize = 10;
int elementos[maxSize];
int top;
public:
bool empty();
int pop();
void push(int x);
int peek();
void clear();
Stack();
};
bool Stack::empty() {
if (top == -1)
return true;
else
return false;
}
int Stack::pop() {
if (empty()) {
cout << "Pop da pilha vazia.\n";
exit(1);
} else {
int x = elementos[top];
top--;
return x;
}
}
void Stack::push(int x) {
if (top < maxSize-1){
top++;
elementos[top] = x;
} else {
cout << "Push na pilha cheia.\n";
exit(1);
}
}
Classe Stack
(Implementação usando Vetor)
30
Saída:
Peek na pilha vazia!-2147483648
30
20
10
1300
Peek na pilha vazia!-2147483648
int Stack::peek() {
if (empty()) {
cout << "Peek na pilha vazia!";
return INT_MIN;
} else {
return elementos[top];
}
}
void Stack::clear() {
top = -1;
}
Stack::Stack() {
clear();
}
int main() {
Stack s;
cout << s.peek() << endl;
s.push(10);
s.push(20);
s.push(30);
cout << s.pop() << endl;
cout << s.pop() << endl;
cout << s.pop() << endl;
s.push(1100);
s.push(1200);
s.push(1300);
cout << s.pop() << endl;
s.clear();
cout << s.peek() << endl;
}
Classes e Arquivos
31
Classes e Arquivos
C++ permite definir e implementar mais de uma
classe em um mesmo arquivo
• Exemplo: sistema bancário
32
class ContaCorrente { ... }; int main(int argc, char** argv) { ... } class Agencia { ... }; void ContaCorrente::depositar(double valor) { ... } void Agencia::adicionar(ContaCorrente c) { ... } class Correntista { ... }; bool ContaCorrente::retirar(double valor) { ... } ...
Classes e Arquivos
Como organizar as classes em um arquivo?
Quantas classes devem ser colocadas em cada
arquivo?
Boa prática
• Crie um arquivo separado para cada definição e
implementação
• Em projetos grandes, não é possível colocar uma
definição e uma implementação por arquivo. Imagine
um projeto com 2000 classes.
33
Definição
Arquivo de cabeçalho: ".h"
• Exemplo: ContaCorrente.h
34
class ContaCorrente { public: double saldo; void depositar(double valor); bool retirar(double valor); };
Implementação
Arquivo .cpp
• Necessário fazer um #include do ".h"
• Exemplo: ContaCorrente.cpp
35
#include "ContaCorrente.h" void ContaCorrente::depositar(double valor) { saldo += valor; } bool ContaCorrente::retirar(double valor) { if (saldo < valor) return false; saldo -= valor; return true; }
Incluindo a definição
Implementação
Arquivo main.cpp
Para compilar e linkar manualmente:
36
#include "ContaCorrente.h" #include <iostream> using namespace std; int main() { ContaCorrente conta={0}; conta.depositar(1000); cout << conta.saldo << endl; }
g++ -c -std=c++11 contacorrente.cpp -o contacorrente.o g++ -c -std=c++11 main.cpp -o main.o g++ contacorrente.o main.o -o main.exe
Inclusão
Indica para o compilador onde o arquivo está
• (O arquivo pode estar em pastas diferentes)
• Quando se inclui cabeçalhos da biblioteca padrão usa-
se o "<" e ">"
O compilador procura o arquivo onde as bibliotecas ficam
Arquivos de cabeçalho também podem fazer
inclusões
• Exemplo: Agencia.h inclui a ContaCorrente.h
37
#include "ContaCorrente.h" #include <iostream>
Do projeto
Da biblioteca padrão
Diretiva ifndef
Problema: múltipla inclusão de um cabeçalho
• Exemplo: tanto a Agência quanto o Correntista
incluem a ContaCorrente
• Gera um erro de compilação!
(Compiladores de outras linguagens evitam esse erro)
Solução: diretiva #ifndef
38
#ifndef CONTACORRENTE_H #define CONTACORRENTE_H class ContaCorrente { ... }; #endif
Se CONTACORRENTE_H não estiver
definido, define-o
Fim do ifndef
Classes e Arquivos
Exemplo: arquivos de um sistema bancário
• ContaCorrente.h e ContaCorrente.cpp
• Agencia.h e Agencia.cpp
• Correntista.h e Correntista.cpp
• main.cpp
No Netbeans
• Novo Arquivo → C++ → Classe C++
• Cria tanto o cabeçalho quanto a implementação
Coloca já o #ifndef no arquivo cabeçalho
Cria alguns métodos (veremos na aula que vem o que eles
significam)
39
Exercício
Dado um equipamento de Ar Condicionado que
pode ser operado com os seguintes comandos:
Ligar, Desligar, Aumentar Ventilação, Diminuir
Ventilação, Aumentar Temperatura e Diminuir
Temperatura, escreva um código que permita
armazenar previamente uma sequência de
tarefas, que seja executada após a
programação do equipamento
Baixe o enunciado do exercício Aula 5 Ar
Condicionado Enunciado v1.pdf e os esqueletos
de códigos do Moodle
40
Bibliografia
FORD, W.; TOPP, W. Data Structures with C++.
Prentice Hall. 1996. 895p.
41