Programação
Orientada aos Objectos
Set/2005
Uma Introdução
Usando C++
Paulo Marques
Departamento de Eng. Informática
Universidade de Coimbra
[email protected]
Sobre o que é que vamos falar?

Primeira parte...





Linguagens de programação
Conceitos de programação orientadas aos objectos
Representação visual dos mesmos (UML)
Como é que se exprimem em C++
Segunda parte...

“Network Simulator”, uma aplicação OOP
2
» If you think C++ is not overly
complicated, just what is a protected
abstract virtual base pure virtual private
destructor and when was the last time
you needed one? «
Tom Cargill
» C makes it easy to shoot yourself in
the foot; C++ makes it harder, but when
you do, it blows away your whole leg. «
Bjarne Stroustrup
3
Paradigmas de Programação

Actualmente existem quatro paradigmas de linguagens de
programação em uso comum:




Imperativas (e.g. C, Pascal, Fortran)
Funcionais (e.g. LISP, Scheme)
Lógicas/Declarativas (e.g. Prolog)
Orientadas-aos-Objectos (e.g. Java, C++, C#, Smalltalk)
Hoje em dia a indústria é dominada pelos paradigmas
Imperativo e Orientado-aos-Objectos
4
Linguagens Imperativas

Para programar um computador diz-se que…
 PROGRAMA =
ESTRUTURAS DADOS + ALGORITMOS

No programa existem variáveis que representam os dados
Existe um conjunto de instruções que sucessivamente, a
cada instrução, altera o valor das variáveis, manipulando
os dados
Segue de forma bastante próxima o modelo básico de
funcionamento do processador
Exemplos: C, Pascal, Fortran



5
Programação Orientada aos Objectos

Os grandes problema da programação imperativa,
estruturada:


Grande Acoplamento!
Baixa Coesão!
f()
g()
h()
f()f() f()f() m()
f()
j()
f()p()
l()
f() f()
k()
Estruturas de Dados
Temos os dados, e o programa é constituído por milhares de funções que…
-- Ou manipulam directamente esses dados
-- Ou trocam imensos valores por parâmetro
6
Programação Orientada aos Objectos

Em OOP (Object-Oriented Programming), as funções
estão encapsuladas juntamente com os dados a que
podem (e devem aceder)
l()
f()
Dados
Dados
m()
g()
q()
h()
f()
Dados
g()
h()
7
Programação Orientada aos Objectos

A principal ideia das objectos é que:





Programação Imperativa Procedimental:


Apenas as funções relacionadas com os dados lhes podem aceder
Reduzir o acoplamento e aumentar a coesão
Isto é, permitir a construção de software em projectos de larga
escala, de forma consistente e fácil de gerir
Para além disso, é muito mais natural pensar em termos de
objectos e suas relações do que em termos de dados e algoritmos
PROGRAMA = DADOS + ALGORITMOS
Programação Orientada aos Objectos

PROGRAMA = OBJECTOS + RELAÇÕES
8
Programação OOP e o C++






Ole-Johan Dahl e Kristen Nygaard, Noruegueses,
inventam a linguagem Simula-67, para simulação.
A motivação foi que ao realizarem simulações sobre o
mundo real, com centenas de entidades, tornava-se
inviável especificar explicitamente todas as
interacções possíveis.
Introduziu o conceito de classe/encapsulamento,
objecto e uma forma de herança
Alan Kay, Americano, inventa a linguagem SmallTalk
(circa 1972), considerada a primeira verdadeira
linguagem OOP
Tudo são objectos; Os objectos comunicam trocando
mensagens. Fortemente associada ao GUI!
...inventou o conceito de computador pessoal, GUI e
Portátil, numa altura em que tal era... RADICAL!
“There is no reason anyone would want a
computer in their home." (Ken Olsen, Digital
Equipment Corp, 1977)
9
Programação OOP e o C++


Bell Labs, 1979, Bjarne Stroustrup queria ter
classes e objectos na linguagem C
Motivação: análise do Kernel do UNIX para
possibilitar computação distribuída!
... OOP facilitaria o processo

Criou um pré-processador que compilava a sua
linguagem “C with Classes” para C



Primeira linguagem com suporte OOP
largamente utilizada na indústria


1983, a linguagem é renomeada C++.
1985: primeira versão comercial
Dominou os anos 90.
Última versão do standard: 2003

Devido à complexidade da linguagem,
praticamente nenhum compilador implementa o
standard completamente...
10
Fundamentos da OOP

Noção de Classe e Objecto

Encapsulamento

Herança

Polimorfismo
11
Preliminares... (Temos de garantir os básicos!)
// Importa biblioteca e passa a usar o espaço de nomes “standard”
#include <iostream>
#include <vector>
using namespace std;
// Programa principal
int main()
{
// Declara uma tabela de inteiros de tamanho variável
vector<int> myTable;
// Adiciona-lhe 10 números
for (unsigned i=0; i<10; i++)
myTable.push_back(i);
// Imprime o seu conteúdo
for (unsigned i=0; i<myTable.size(); i++)
cout << myTable[i] << endl;
return 0;
}
12
O resultado...
13
Noção de Classe e Objecto

Uma classe representa um grupo (ou tipo) de coisas





Um objecto (ou instância) representa uma coisa em
particular de um grupo.


Exemplo: Pessoa, Automóvel
É sempre um NOME
Uma classe tem operações associadas: métodos
Os métodos representam acções sobre uma entidade, logo são
VERBOS
Exemplo: “Paulo Marques”, “43-23-XM”
Toda a programação OOP baseia-se em encontrar classes
e relações entre classes!
14
Mais alguns exemplos

Uma REDE possui NODOS. Cada NODO pode ser um
COMPUTADOR ou um REPETIDOR


Numa rede pode fazer-se um Broadcast


Broadcast é um método!
Um nodo pode enviar e receber PACOTEs



Rede, Nodo, Computador e Repetidor são classes!
Enviar e Receber são métodos!
PACOTE é uma classe!
Um LEITOR_DE_MP3 toca MUSICA!


LEITOR e MUSICA são classes!
Toca é um método!
15
Classe Pessoa
Pessoa.h
class Pessoa
{
private:
string _nome;
int _idade;
Pessoa
public:
Pessoa(string nome, int idade);
void imprime();
};
-_nome
-_idade
+Pessoa(in nome, in idade)
+imprime()
Basics.cpp
#include "Pessoa.h“
int main() {
Pessoa aluno("Carlos Manuel", 30);
Pessoa professor("Guilherme To", 23);
aluno.imprime();
professor.imprime();
return 0;
}
16
Classe Pessoa
Pessoa aluno("Carlos Manuel", 30);
Pessoa professor("Guilherme To", 23);
aluno.imprime();
professor.imprime();
17
Classe Pessoa
Pessoa aluno("Carlos Manuel", 30);
Pessoa professor("Guilherme To", 23);
aluno.imprime();
professor.imprime();
classe


objectos (instâncias)
A existência de “classes de coisas” e as suas “instâncias”,
permitem-me manipular e simular o mundo de forma
abstracta!
A noção de encapsulamento – os dados estarem
escondidos – é fundamental!
18
A implementação de Pessoa...
Pessoa.cpp
Pessoa::Pessoa(string nome, int idade)
{
_nome = nome;
_idade = idade;
}
void Pessoa::imprime()
{
cout << "[" << _nome << "/" << _idade << "]" << endl;
}
19
Herança

É possível definir especializações de uma classe base.
Chama-se a isso uma “classe derivada”


Um COMPUTADOR é um NODO de rede. Um REPETIDOR
é um NODO de rede.


A classe derivada contém tudo o que a base contém, mas com
informação/métodos adicionais
Computador e Repetidor são tudo o que um Nodo de rede é, mas
tendo funcionalidade específica! São classes derivadas de nodo!
Um PATRÃO é uma PESSOA. Um EMPREGADO é uma
PESSOA.

Em qualquer caso, é possível encarar um “Patrão” ou um
“Empregado” como sendo pessoas! Têm é “mais funcionalidade”.
São classes derivadas!
20
Classes Patrão e Empregado
Patrao.h
class Patrao : public Pessoa
{
private:
string _codigoEmpresa;
public:
Patrao(string nome, int idade, string codigo);
void abreCofre();
};
Empregado.h
class Empregado : public Pessoa
{
public:
Empregado(string nome, int idade);
void abreBalcao();
};
Pessoa
-_nome
-_idade
+Pessoa(in nome, in idade)
+imprime()
Patrao
Empregado
-_codigoEmpresa
+Patrao(in nome, in idade, in codigo)
+abreCofre()
+imprime()
+Empregado(in nome, in idade)
+abreBalcao()
+imprime()
21
Classes Patrão e Empregado (2)
Patrao.cpp
Patrao::Patrao(string nome, int idade, string codigo) :
Pessoa(nome, idade), _codigoEmpresa(codigo)
{
}
void Patrao::abreCofre()
{
cout << "Eheh, a abrir o cofre. Codigo: " << _codigoEmpresa << endl;
}
Empregado.cpp
Empregado::Empregado(string nome, int idade) :
Pessoa(nome, idade)
{
}
void Empregado::abreBalcao()
{
cout << "Puff.. a abrir o balcao." << endl;
}
22
E agora, a execução...
Patrao patrao("Carlos Manuel", 30, "1234F");
Empregado empregado("Guilherme To", 23);
patrao.imprime();
patrao.abreCofre();
empregado.imprime();
empregado.abreBalcao();
23
Interlúdio...

Antes de podermos examinar o que é polimorfismo, temos
de lidar com alguns detalhes...
24
Passagem por Valor, Referência e por Ponteiro

Em C++, os parâmetros das funções podem ser passados
por valor, por referência ou por ponteiro.

Na passagem por valor, apenas uma cópia do valor é
passado. Dentro da rotina, alterações na variável não
afectam a variável original.

Na passagem por referência, a variável que se encontra no
parâmetro representa a variável original. Alterações na
variável são reflectidas na variável original.

Na passagem por ponteiro, é passado o endereço de
memória onde se encontra o dado original. É possível
guardar informação sobre onde se encontram esse dado.
25
Passagem por Valor (C++)
#include <iostream>
using namespace std;
void leNome(string nome)
{
nome = “Critical”;
cout << nome << endl;
}
void main()
{
string nome = “XSoft”;
Ao chamar-se leNome(), o valor de
“nome” é copiado para dentro da função.
Não existe nenhuma relação entre a
variável “nome” de leNome() e a
variável “nome” do programa principal,
excepto o seu valor inicial.
Imprime…
Critical
XSoft
leNome(nome);
cout << nome << endl;
}
26
Passagem por Referência (C++)
#include <iostream>
using namespace std;
void leNome(string& nome)
{
nome = “Critical”;
cout << nome << endl;
}
void main()
{
string nome = “XSoft”;
Ao chamar-se leNome(), “nome” em
leNome() representa a variável original
com a qual se chama o programa
(note-se que não têm de ter o mesmo
nome). Alterações feitas sobre variável na
função reflectem-se na variável original!
Imprime…
Critical
Critical
leNome(nome);
cout << nome << endl;
}
27
Ponteiros (C++)


Em C++ moderno, a principal utilidade do uso de
ponteiros é permitir guardar uma referência para
um certo objecto.
Notação...
// Uma string
string nome = “Critical”;
// Um ponteiro para uma string...
string* ptrNome = 0;
// Uma atribuição...
ptrNome = &nome;
nome (0x5490)
“Critical”
ptrNome (0x6000)
0
ptrNome (0x6000)
0x5490
// Acesso à string original...
cout << *ptrNome << endl;
28
Passagem de Ponteiros (C++)
string* ptrNome = 0;
void guardaNome(string* nome)
{
ptrNome = nome;
}
void imprimeNome()
{
cout << *ptrNome << endl;
}

Em C++ moderno, a principal
utilidade do uso de ponteiros é
permitir guardar uma referência
para um certo objecto, para ser
utilizado mais tarde.
Imprime…
XSoft
Critical
void main()
{
string nome = “XSoft”;
guardaNome(&nome);
imprimeNome();
nome = “Critical”;
imprimeNome();
}
29
Polimorfismo

Capacidade de objectos diferentes se comportarem de forma diferente
quando recebem a mesma mensagem.




Permite tratar da mesma forma todos os objectos.
(uma mensagem é uma invocação de um método...)
Um PATRÃO quando “calcula o ordenado”, calcula-o como sendo uma
percentagem dos lucros da empresa. Um EMPREGADO, calcula-o
como sendo um valor constante.
Quando se chama imprime() em PATRÃO, deve dizer que “é um
patrão” antes de dar os dados. Em empregado, deverá dizer que é um
empregado.
Patrao patrao(“Sofia", 30, "1234F");
Empregado empregado(“Carlos", 23);
Pessoa* umaPessoa;
Imprime…
PATRAO[Sofia / 30]
EMPREGADO[Carlos / 23]
umaPessoa = &patrao;
umaPessoa->imprime();
umaPessoa = &empregado;
umaPessoa->imprime();
Comportamento diferente apesar
de ser o mesmo código!
30
A classe Pessoa tem de ser modificada...
Pessoa.h
class Pessoa
{
private:
string _nome;
int _idade;
public:
Pessoa(string nome, int idade);
virtual void imprime();
};


A palavra chave “virtual” indica
que o método foi pensado para
ser modificado numa classe
derivada.
Quando se usa um ponteiro do
tipo Pessoa*, o ambiente de
execução tem de procurar
saber qual é a verdadeira
classe do objecto e chamar o
método correcto
Sempre que se declara um método virtual, deve criar-se um
“destrutor” virtual. Nós não iremos abordar esses tópicos neste crash
course. Mas, no mundo real, se isto não for feito por levar a graves
memory leaks!
31
Redefinição dos métodos imprime()
Patrao.h
Patrao.cpp
class Patrao : public Pessoa
{
private:
string _codigoEmpresa;
void Patrao::imprime()
{
cout << “PATRAO”;
Pessoa::Imprime();
}
public:
Patrao(string nome, int idade,
string codigo);
void abreCofre();
virtual void imprime();
};
Empregado.h
Empregado.cpp
class Empregado : public Pessoa
{
public:
Empregado(string nome, int idade);
void Empregado::imprime()
{
cout << “EMPREGADO”;
Pessoa::Imprime();
}
void abreBalcao();
virtual void imprime();
};
32
A execução...
33
Herança, Polimorfismo e Redefinição

Um aspecto muito importante do uso da herança é que
todas as partes comuns, mesmo em termos de
algoritmos, devem ser agrupadas na classe base.


As classes abaixo são especializações
Quando se redefine um método, pode sempre invocar-se a
funcionalidade que já se encontrava disponível. E.g.
Pessoa::imprime()
Pessoa
-_nome
-_idade
+Pessoa(in nome, in idade)
+imprime()
Patrao
Empregado
-_codigoEmpresa
+Patrao(in nome, in idade, in codigo)
+abreCofre()
+imprime()
+Empregado(in nome, in idade)
+abreBalcao()
+imprime()
34
» Now this is not the end. It is
not even the beginning of the
end. But it is, perhaps, the end
of the beginning «
Winston Churchill
Questions?
35
Moth found trapped between points at Relay # 70, Panel F, of the Mark II Aiken Relay
Calculator while it was being tested at Harvard University, 9 September 1945. The
operators affixed the moth to the computer log, with the entry: “First actual case of bug
being found”. They put out the word that they had “debugged” the machine, thus
introducing the term “debugging a computer program”. In 1988, the log, with the moth
still taped by the entry, was in the Naval Surface Warfare Center Computer Museum at
Dahlgren, Virginia.
U.S. Naval Historical Center Photograph.
http://www.history.navy.mil/index.html
36
Programação
Orientada aos Objectos
Set/2005
“Network Simulator”
Um pequeno caso de estudo
Paulo Marques
Departamento de Eng. Informática
Universidade de Coimbra
[email protected]
Aviso!

O exemplo que se segue é limitado e serve unicamente
para ilustrar os pontos anteriormente explorados

Em C++ real, existem algumas coisas que são feitas neste
código que não devem ser feitas



Eventuais problemas com memory leaks
Performance... péssima
Porquê é que o fazemos? Infelizmente, nesta
“short session”, não é possível estudar a
linguagem a fundo...
» It has been discovered that C++ provides a
remarkable facility for concealing the trival details
of a program – such as where its bugs are «
David Keppel
38
Simple Network
B
E
A
C
D
F
39
Simple Network (2)

Temos NODOS



Cada NODO pode ser um COMPUTADOR ou um REPETIDOR
Os nodos enviam() e recebem() PACTOREs
Os NODOS pertencem a uma SUBREDE



Uma SUBREDE pode ter vários NODOS
Um COMPUTADOR apenas pertence a uma SUBREDE
Um REPETIDOR pode pertencer a várias SUBREDEs
40
Diagrama UML
Subnet
*
1
+addNode()
+broadcast()
+getName()
+sendPacket()
+receivePacket()
+addToSubnet()
1
1
Computer
Packet
-_from
-_to
-_msg
+getOrigin()
+getDestination()
+getMessage()
+print()
-_name
-_nodes
*
«uses»
Node
Bridge
-subnet
-subnets
+sendPacket()
+receivePacket()
+addToSubnet()
+sendPacket()
+receivePacket()
+addToSubnet()
1
41
Comportamento do Sistema

COMPUTADOR:



REPETIDOR:


Se eu quero enviar um pacote 
faço broadcast para a minha sub-rede
Se eu recebo um pacote 
caso seja para mim, imprimo-o
caso contrário, descarto-o
Se eu recebo um pacote 
caso seja para mim, imprimo-o
caso contrário, envio-o para todas as redes a que estou ligado
SUB-REDE

Caso me peçam para fazer um broadcast de um pacote 
envio para todos os nodos excepto o de origem
42
Implementação – Programa Principal (Network.cpp)
int main()
Computer
Computer
Computer
{
computerA("Computer_A");
computerB("Computer_B");
computerC("Computer_C");
Computer computerD("Computer_D");
Computer computerE("Computer_E");
Computer computerF("Computer_F");
B
A
E
C
D
F
Bridge bridge("MyBridge");
Subnet subnet1;
subnet1.addNode(computerA);
subnet1.addNode(computerB);
subnet1.addNode(computerC);
subnet1.addNode(bridge);
Subnet subnet2;
subnet2.addNode(computerD);
subnet2.addNode(computerE);
subnet2.addNode(computerF);
subnet2.addNode(bridge);
Packet p1("Computer_A", "Computer_F", "Hello F!");
computerA.sendPacket(p1);
}
43
O Resultado da Execução...
44
Como implementar uma relação 1 para 1?
Computer
Subnet
-subnet
+sendPacket()
+receivePacket()
+addToSubnet()
1
1
-_nodes
+addNode()
+broadcast()
class Computer : public Node
{
private:
Subnet* _subnet;
public:
void addToSubnet(Subnet& s);
};
void Computer::addToSubnet(Subnet& net)
{
_subnet = &net;
}
45
Como implementar uma relação 1 para N?
Bridge
Subnet
-subnets
+sendPacket()
+receivePacket()
+addToSubnet()
1
*
-_nodes
+addNode()
+broadcast()
class Bridge : public Node
{
private:
vector<Subnet*> _subnets;
public:
void addToSubnet(Subnet& s);
};
void Bridge::addToSubnet(Subnet& net)
{
_subnets.push_back(&net);
}
46
Interface – Node.h
class Node
{
private:
string _name;
public:
Node(string name);
string getName();
virtual void sendPacket(Packet p) = 0;
virtual void receivePacket(Packet p) = 0;
virtual void addToSubnet(Subnet& net) = 0;
};
47
Interface – Computer.h e Bridge.h
class Computer : public Node
{
private:
Subnet* _subnet;
public:
Computer(string name);
virtual void addToSubnet(Subnet& net);
virtual void sendPacket(Packet p);
virtual void receivePacket(Packet p);
};
class Bridge : public Node
{
private:
vector<Subnet*> _subnets;
public:
Bridge(string name);
virtual void addToSubnet(Subnet& net);
virtual void sendPacket(Packet p);
virtual void receivePacket(Packet p);
};
48
Interface – Subnet.h e Packet.h
class Subnet
{
private:
vector<Node*> _nodes;
public:
Subnet();
void addNode(Node& node);
void broadcast(Node& sender, Packet p);
};
class Packet
{
private:
string _from;
string _to;
string _msg;
public:
Packet(string from, string to, string msg);
string getOrigin();
string getDestination();
string getMessage();
void print();
};
49
Implementação – Packet.cpp
Packet::Packet(string from, string to, string msg) :
_from(from), _to(to), _msg(msg) {
}
string Packet::getOrigin() {
return _from;
}
string Packet::getDestination() {
return _to;
}
string Packet::getMessage() {
return _msg;
}
void Packet::print()
{
cout << "[FROM: " << _from << ", TO: " << _to << ", MSG: \""
<< _msg << "\"]" << endl;
}
50
Implementação – Node.cpp
Node::Node(string name) :
_name(name)
{
}
string Node::getName()
{
return _name;
}
51
Implementação – Computer.cpp
Computer::Computer(string name) :
Node(name), _subnet(0)
{
}
void Computer::addToSubnet(Subnet& net) {
_subnet = &net;
}
void Computer::sendPacket(Packet p) {
_subnet->broadcast(*this, p);
}
void Computer::receivePacket(Packet p) {
if (p.getDestination() == getName()) {
cout << "[NODE " << getName() << "] RECEIVED MESSAGE: ";
p.print();
}
else {
cout << "{Node " << getName() << "} Discarting packet: ";
p.print();
}
}
52
Implementação – Bridge.cpp
Bridge::Bridge(string name) :
Node(name) {
}
void Bridge::addToSubnet(Subnet& net) {
_subnets.push_back(&net);
}
void Bridge::sendPacket(Packet p) {
for (unsigned i=0; i<_subnets.size(); i++)
_subnets[i]->broadcast(*this, p);
}
void Bridge::receivePacket(Packet p) {
if (p.getDestination() == getName()) {
cout << "[BRIDGE " << getName() << "] RECEIVED MESSAGE: ";
p.print();
}
else {
cout << "*Bridge " << getName() << "* Echoing packet:
";
p.print();
sendPacket(p);
}
}
53
Implementação – Subnet.cpp
Subnet::Subnet()
{
}
void Subnet::addNode(Node& node)
{
node.addToSubnet(*this);
_nodes.push_back(&node);
}
void Subnet::broadcast(Node& sender, Packet p)
{
for (unsigned i=0; i<_nodes.size(); i++)
if (&sender != _nodes[i])
_nodes[i]->receivePacket(p);
}
54
That’s It!
B
E
A
C
D
F
55
Alguns exercícios...

O que é que acontece se forem colocadas dois repetidores
na rede? Como é que resolve o problema?

Considere uma rede com fios em que existem
encaminhadores (routers). Modele e implemente um
sistema que permita enviar pacotes de qualquer endereço
para qualquer endereço.

Considere um jogo de cartas (Poker, Sueca, ...). Modele o
sistema e simule o decorrer de um jogo:



Pessoa contra Pessoa
Pessoa contra Computador
Computador contra Computador
56
Para saber mais...
C++ How to Program, 4th Edition
by Harvey M. Deitel, Paul J. Deitel
Prentice Hall, Aug. 2002

Uma introdução “leve” ao C++
C++ Primer, 4th Edition
by Stanley B. Lippman et. al.
Addison-Wesley Professional, Feb. 2005

Para aprender “ao pormenor” tudo o que há a
saber sobre C++
57
Para saber mais...
Object-Oriented Analysis and Design with
Applications, 2nd Edition
by Grady Booch
Addison-Wesley Professional, Sep. 1993


Como fazer design orientado aos objectos!
Notação desactualizada, mas uma referência;
Muito educativo!
The Unified Modeling Language User Guide, 2nd Ed.
by Grady Booch, James Rumbaugh, Ivar Jacobson
Addison-Wesley Professional, May 2005

Um “tutorial” extremamente claro sobre como fazer
modelação usando a linguagem UML.
58
YOU ARE FREE TO USE THIS MATERIAL FOR YOUR
PERSONAL LERNING OR REFERENCE, DISTRIBUTE IT
AMONG COLLEGUES OR EVEN USE IT FOR TEACHING
CLASSES. YOU MAY EVEN MODIFY IT, INCLUDING MORE
INFORMATION OR CORRECTING STANDING ERRORS.
THIS RIGHT IS GIVEN TO YOU AS LONG AS YOU KEEP
THIS NOTICE AND GIVE PROPER CREDIT TO THE
AUTHOR. YOU CANNOT REMOVE THE REFERENCES TO
THE AUTHOR OR TO THE INFORMATICS ENGINEERING
DEPARTMENT OF THE UNIVERSITY OF COIMBRA.
(c) 2005 – Paulo Marques, [email protected]
59
Download

Programação Orientada aos Objectos