Upload
internet
View
123
Download
5
Embed Size (px)
Citation preview
ProgramaçãoOrientada aos Objectos
Paulo MarquesDepartamento de Eng. InformáticaUniversidade de [email protected] S
et/2
005
Uma Introdução Usando C++
2
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
3
» 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
» 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
4
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 paradigmasImperativo e Orientado-aos-Objectos
5
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
6
Programação Orientada aos Objectos
Os grandes problema da programação imperativa, estruturada: Grande Acoplamento! Baixa Coesão!
Estruturas de Dados
f() g() h() f()
f()
f() m()f()f()j()
l() f() f()f()
k()p()
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
7
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)
Dadosf()
g()h()
Dadosf()
g()h()
Dadosl()
m()q()
8
Programação Orientada aos Objectos
A principal ideia das objectos é que: 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ção Imperativa Procedimental: PROGRAMA = DADOS + ALGORITMOS
Programação Orientada aos Objectos PROGRAMA = OBJECTOS + RELAÇÕES
9
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)
10
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 1983, a linguagem é renomeada C++. 1985: primeira versão comercial
Primeira linguagem com suporte OOP largamente utilizada na indústria Dominou os anos 90.
Última versão do standard: 2003 Devido à complexidade da linguagem,
praticamente nenhum compilador implementa o standard completamente...
11
Fundamentos da OOP
Noção de Classe e Objecto
Encapsulamento
Herança
Polimorfismo
12
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 principalint 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;}
13
O resultado...
14
Noção de Classe e Objecto
Uma classe representa um grupo (ou tipo) de coisas 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
Um objecto (ou instância) representa uma coisa em particular de um grupo. Exemplo: “Paulo Marques”, “43-23-XM”
Toda a programação OOP baseia-se em encontrar classes e relações entre classes!
15
Mais alguns exemplos
Uma REDE possui NODOS. Cada NODO pode ser um COMPUTADOR ou um REPETIDOR Rede, Nodo, Computador e Repetidor são classes!
Numa rede pode fazer-se um Broadcast Broadcast é um método!
Um nodo pode enviar e receber PACOTEs 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!
16
Classe Pessoa
class Pessoa {private: string _nome; int _idade;
public: Pessoa(string nome, int idade); void imprime();};
Pessoa.h
+Pessoa(in nome, in idade)+imprime()
-_nome-_idade
Pessoa
#include "Pessoa.h“
int main() { Pessoa aluno("Carlos Manuel", 30); Pessoa professor("Guilherme To", 23);
aluno.imprime(); professor.imprime();
return 0;}
Basics.cpp
17
Classe Pessoa
Pessoa aluno("Carlos Manuel", 30); Pessoa professor("Guilherme To", 23);
aluno.imprime(); professor.imprime();
18
Classe Pessoa
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!
Pessoa aluno("Carlos Manuel", 30); Pessoa professor("Guilherme To", 23);
aluno.imprime(); professor.imprime();
classe objectos (instâncias)
19
A implementação de Pessoa...
Pessoa::Pessoa(string nome, int idade){ _nome = nome; _idade = idade;}
void Pessoa::imprime(){ cout << "[" << _nome << "/" << _idade << "]" << endl;}
Pessoa.cpp
20
Herança
É possível definir especializações de uma classe base. Chama-se a isso uma “classe derivada” A classe derivada contém tudo o que a base contém, mas
com informação/métodos adicionais
Um COMPUTADOR é um NODO de rede. Um REPETIDOR é um NODO de rede. 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!
21
Classes Patrão e Empregado
class Empregado : public Pessoa{public: Empregado(string nome, int idade); void abreBalcao();};
Empregado.h
class Patrao : public Pessoa{private: string _codigoEmpresa;public: Patrao(string nome, int idade, string codigo); void abreCofre();};
Patrao.h
+Pessoa(in nome, in idade)+imprime()
-_nome-_idade
Pessoa
+Patrao(in nome, in idade, in codigo)+abreCofre()+imprime()
-_codigoEmpresa
Patrao
+Empregado(in nome, in idade)+abreBalcao()+imprime()
Empregado
22
Classes Patrão e Empregado (2)
Empregado::Empregado(string nome, int idade) : Pessoa(nome, idade){}
void Empregado::abreBalcao(){ cout << "Puff.. a abrir o balcao." << endl;}
Empregado.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;}
Patrao.cpp
23
E agora, a execução...
Patrao patrao("Carlos Manuel", 30, "1234F");Empregado empregado("Guilherme To", 23);patrao.imprime();patrao.abreCofre(); empregado.imprime();empregado.abreBalcao();
24
Interlúdio...
Antes de podermos examinar o que é polimorfismo, temos de lidar com alguns detalhes...
25
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.
26
Passagem por Valor (C++)
#include <iostream>using namespace std;
void leNome(string nome){ nome = “Critical”; cout << nome << endl; }
void main(){ string nome = “XSoft”;
leNome(nome); cout << nome << endl;}
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
27
Passagem por Referência (C++)
#include <iostream>using namespace std;
void leNome(string& nome){ nome = “Critical”; cout << nome << endl; }
void main(){ string nome = “XSoft”;
leNome(nome); cout << nome << endl;}
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 nafunção reflectem-se na variável original!
Imprime… Critical Critical
28
Ponteiros (C++)
Em C++ moderno, a principal utilidade do uso de ponteiros é permitir guardar uma referência para um certo objecto.
Notação...
// Uma stringstring nome = “Critical”;
// Um ponteiro para uma string...string* ptrNome = 0;// Uma atribuição...ptrNome = &nome;
// Acesso à string original...cout << *ptrNome << endl;
“Critical”
nome (0x5490)
0
ptrNome (0x6000)
0x5490
ptrNome (0x6000)
29
Passagem de Ponteiros (C++)
Em C++ moderno, a principal utilidade do uso de ponteiros é permitir guardar uma referência para um certo objecto, para ser utilizado mais tarde.
string* ptrNome = 0;
void guardaNome(string* nome){ ptrNome = nome; }
void imprimeNome(){ cout << *ptrNome << endl;}
void main(){ string nome = “XSoft”;
guardaNome(&nome); imprimeNome();
nome = “Critical”;
imprimeNome();}
Imprime… XSoft Critical
30
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;
umaPessoa = &patrao;umaPessoa->imprime();
umaPessoa = &empregado;umaPessoa->imprime();
Imprime… PATRAO[Sofia / 30] EMPREGADO[Carlos / 23]
Comportamento diferente apesarde ser o mesmo código!
31
A classe Pessoa tem de ser modificada...
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
class Pessoa {private: string _nome; int _idade;
public: Pessoa(string nome, int idade); virtual void imprime();};
Pessoa.h
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 gravesmemory leaks!
32
Redefinição dos métodos imprime()
void Empregado::imprime(){ cout << “EMPREGADO”; Pessoa::Imprime();}
Empregado.cpp
void Patrao::imprime(){ cout << “PATRAO”; Pessoa::Imprime();}
Patrao.cpp
class Empregado : public Pessoa{public: Empregado(string nome, int idade);
void abreBalcao(); virtual void imprime();};
Empregado.h
class Patrao : public Pessoa{private: string _codigoEmpresa;
public: Patrao(string nome, int idade, string codigo);
void abreCofre(); virtual void imprime();};
Patrao.h
33
A execução...
34
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(in nome, in idade)+imprime()
-_nome-_idade
Pessoa
+Patrao(in nome, in idade, in codigo)+abreCofre()+imprime()
-_codigoEmpresa
Patrao
+Empregado(in nome, in idade)+abreBalcao()+imprime()
Empregado
35
» 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?
36
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
ProgramaçãoOrientada aos Objectos
Paulo MarquesDepartamento de Eng. InformáticaUniversidade de [email protected] S
et/2
005
“Network Simulator”Um pequeno caso de estudo
38
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
39
Simple Network
A
B
C
D
E
F
40
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
41
Diagrama UML
+getName()+sendPacket()+receivePacket()+addToSubnet()
-_name
Node
+getOrigin()+getDestination()+getMessage()+print()
-_from-_to-_msg
Packet
+addNode()+broadcast()
-_nodes
Subnet
«uses»
1 *
+sendPacket()+receivePacket()+addToSubnet()
-subnet
Computer
+sendPacket()+receivePacket()+addToSubnet()
-subnets
Bridge1
1
1
*
42
Comportamento do Sistema
COMPUTADOR: 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
REPETIDOR: 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
43
Implementação – Programa Principal (Network.cpp)int main() { Computer computerA("Computer_A"); Computer computerB("Computer_B"); Computer computerC("Computer_C");
Computer computerD("Computer_D"); Computer computerE("Computer_E"); Computer computerF("Computer_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);}
A
B
CD
E
F
44
O Resultado da Execução...
45
Como implementar uma relação 1 para 1?
+addNode()+broadcast()
-_nodes
Subnet
+sendPacket()+receivePacket()+addToSubnet()
-subnet
Computer
1 1
class Computer : public Node {private: Subnet* _subnet;
public: void addToSubnet(Subnet& s);};
void Computer::addToSubnet(Subnet& net){ _subnet = &net;}
46
Como implementar uma relação 1 para N?
class Bridge : public Node {private: vector<Subnet*> _subnets;
public: void addToSubnet(Subnet& s);};
void Bridge::addToSubnet(Subnet& net){ _subnets.push_back(&net);}
+addNode()+broadcast()
-_nodes
Subnet
+sendPacket()+receivePacket()+addToSubnet()
-subnets
Bridge
1 *
47
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;};
48
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);};
49
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();};
50
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;}
51
Implementação – Node.cpp
Node::Node(string name) : _name(name){}
string Node::getName(){ return _name;}
52
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(); }}
53
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); }}
54
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);}
55
That’s It!
A
B
CD
E
F
56
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
57
Para saber mais...
C++ How to Program, 4th Editionby Harvey M. Deitel, Paul J. DeitelPrentice Hall, Aug. 2002
Uma introdução “leve” ao C++
C++ Primer, 4th Editionby Stanley B. Lippman et. al.Addison-Wesley Professional, Feb. 2005
Para aprender “ao pormenor” tudo o que há a saber sobre C++
58
Para saber mais...
Object-Oriented Analysis and Design withApplications, 2nd Editionby Grady BoochAddison-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 JacobsonAddison-Wesley Professional, May 2005
Um “tutorial” extremamente claro sobre como fazer modelação usando a linguagem UML.
59
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]