A programação com objectos facilita a reutilização de componentes em diferentes programas e, em particular, permite-nos especializar essas componentes. Veremos de seguida como criar especializações de classes através da definição de subclasses e herança.
Herança
Consideremos uma agência de viagens onde são oferecidos vários produtos. Cada produto é caracterizado por um preço e uma designação. A um produto podemos também associar uma descrição. Dois produtos são iguais se tiverem o mesmo nome. Neste caso vamos definir a classe av_produto da seguinte forma:
class av_produto:
def __init__(self, nome, preco):
if not(isinstance(nome, str) and isinstance(preco, (int, float))):
raise ValueError('av_produto: argumentos invalidos')
self.__nome = nome
self.__preco = preco
self.__descricao = None
def get_nome(self):
return self.__nome
def set_nome(self, nome):
if not isinstance(nome, str):
raise ValueError('av_produto: set_nome: argumento invalido')
self.__nome = nome
def get_preco(self):
return self.__preco
def set_preco(self, preco):
if not isinstance(preco, (int, float)):
raise ValueError('av_produto: set_preco: argumento invalido')
self.__preco = preco
def get_descricao(self):
return self.__descricao
def set_descricao(self, descricao):
if not isinstance(descricao, str):
raise ValueError('av_produto: set_descricao: argumento invalido')
self.__descricao = descricao
def __repr__(self):
return self.get_nome() + ' [preco: ' + str(self.get_preco()) + ']'
def __eq__(self, produto):
if not isinstance(produto, av_produto):
raise ValueError('av_produto: __eq__: argumento invalido')
return self.get_nome() == produto.get_nome()No entanto nem todos os produtos nesta agência são iguais. Temos viagens (que têm um local de origem e destino), hotéis (que ficam num local e têm categorias), e circuitos (que podem incluir vários dos outros produtos). Para representarmos estes diferentes produtos poderíamos ser tentados a replicar o código acima e fazer as operações necessárias, no entanto, como já vimos, devemos utilizar abstracções e reutilizar código sempre que possível. Vejamos como utilizar herança e subclasses para representarmos estes produtos, redefinindo os métodos as superclasse quando necessário.
class av_viagem(av_produto):
def __init__(self, nome, preco, origem, destino):
super().__init__(nome, preco)
if not(isinstance(origem, str) and isinstance(destino, str)):
raise ValueError('av_viagem: argumentos invalidos')
self.__origem = origem
self.__destino = destino
def get_origem(self):
return self.__origem
def set_origem(self, origem):
if not isinstance(origem, str):
raise ValueError('av_viagem: set_origem: argumentos invalidos')
self.__origem = origem
def get_destino(self):
return self.__destino
def set_destino(self, destino):
if not isinstance(destino, str):
raise ValueError('av_viagem: set_destino: argumentos invalidos')
self.__destino = destino
def __repr__(self):
return super().__repr__() + ' ' + self.get_origem() + ' -> ' + self.get_destino()
class av_hotel(av_produto):
# EXERCICIO
class av_circuito(av_produto):
def __init__(self, nome):
super().__init__(nome, 0)
self.__produtos = []
def get_produtos(self):
return tuple(self.__produtos)
def add_produto(self, produto):
if not isinstance(produto, av_produto):
raise ValueError('av_circuito: add_produto: argumento invalido')
self.__produtos.append(produto)
def set_preco(self, preco):
pass
def get_preco(self):
total = 0
for x in self.get_produtos():
total += x.get_preco()
return total
def __repr__(self):
res = super().__repr__() + ' circuito:'
for x in self.get_produtos():
res += '\n\t' + str(x)
return res Note-se o uso de super para podermos aceder a métodos da classe pai que entretanto estejam a ser, ou tenham sido, redefinidos. Na implementação da classe av_circuito utilizámos o padrão de desenho composite que será objecto de estudo na disciplina de programação com objectos, entre outros padrões.