HomePage RecentChanges

Lesson23

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.