Programação Orientada aos Objectos Paulo Marques Departamento de Eng. Informática Universidade de...

Preview:

Citation preview

ProgramaçãoOrientada aos Objectos

Paulo MarquesDepartamento de Eng. InformáticaUniversidade de Coimbrapmarques@dei.uc.pt 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 Coimbrapmarques@dei.uc.pt 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, pmarques@dei.uc.pt

Recommended