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.