14 de dezembro de 2008

Getters e Setters em Python como em Java


Na série sobre acesso a atributos em Orientação a Objetos (OO), o foco é o encapsulamento, que é uma das premissas da OO. Dentro deste contexto, foi mostrado como Getters e Setters podem ser usados para ajudar a garantir o encapsulamento e como indicar um atributo privado, dentro de uma classe em Python. Como apresentado, Python não permite que um atributo seja 100% privado, mas possui convenções para nomes de atributos, que dizem ao usuário da classe que aquele atributo é privado e não deveria ser acessado diretamente. Contudo, se o projetista da classe define um atributo privado, que o usuário precisa acessar, ele deve criar getters e/ou setters para acesso àquele atributo.

A forma mais trivial de criar getters e setters em Python é criar um método get e um set para cada atributo que precisa ser acessado fora da classe. Em Java, como apresentado no primeiro texto da série, o padrão é criar métodos como getAttribute() e setAttribute(value). Contudo, como o PEP8 orienta, os nomes compostos de métodos devem usar um underscore para separar as palavras e todas as letras devem ser minúsculas, salvo abrevituras. Assim, esses métodos mudam de nome para get_attribute() e set_attribute(value), em Python. Tomemos o exemplo abaixo.
class Clock:
    def __init__(self):
        self._hour = 0
        self._minute = 0
        self._second = 0

    def get_hour(self):
        return self._hour

    def get_minute(self):
        return self._minute

    def get_second(self):
        return self._second

    def set_hour(self, value):
        if 0 <= value <= 23:
            self._hour = value

        else:
            raise ValueError, "Invalid %s value: %s" % ("hour", value)

    def set_minute(self, value):
        if 0 <= value <= 59:
            self._minute = value

        else:
            raise ValueError, "Invalid %s value: %s" % ("minute", value)

    def set_second(self, value):
        if 0 <= value <= 59:
            self._second = value

        else:
            raise ValueError, "Invalid %s value: %s" % ("second", value)

# Main

clock = Clock()

# Uses getters to retrieve object's attributes.
print "Actual state of clock object is: %d:%d:%d." % (clock.get_hour(),
                                                      clock.get_minute(),
                                                      clock.get_second())

# Uses setters to change attributes' values.
clock.set_hour(15)
clock.set_minute(17)
clock.set_second(39)

# Uses getters to retrieve object's attributes.
print "Actual state of clock object is: %d:%d:%d." % (clock.get_hour(),
                                                      clock.get_minute(),
                                                      clock.get_second())

# Change attributes' values without setters.
# In this case, we don't have error checking, so avoid doing it!
clock._hour = 134
clock._minute = 6099
clock._second = 2560

# Access object's attributes directly. Don't do it!
print "Actual state of clock object is: %d:%d:%d." % (clock._hour,
                                                      clock._minute,
                                                      clock._second)
Listagem 1. Implementação de Getters e Setters em Python, similar à Java.

Da linha 1 à 35, temos a definição da classe Clock. Dentro dela, entre as linhas 7 e 14, temos a definição dos getters e entre as linhas 16 e 35, temos a definição dos setters. Reparem que os getters simplesmente retornam o atributo apropriado e os setters recebem o novo valor para o atributo e realizam a checagem do mesmo antes de realizar a atribuição, assim, caso um valor errado seja passado para o atributo, uma excessão é gerada, evitando que o objeto seja colocado em um estado de inconsistência.

No corpo principal do programa, na linha 39, um objeto do tipo Clock é instanciado. Logo a seguir, nas linhas 42, 43 e 44, os atributos do objeto recém criado são impressos, com o uso dos getters, para acessá-los. Nas linhas entre 47 e 49, os setters são usados corretamente para se alterar os valores de cada atributo do objeto. Caso um dos valores passados para o objeto estivesse errado, o programa terminaria com uma excessão sendo gerada, evitando que o objeto se tornasse inconsistente. Entre as linhas 52 e 54 os atributos do objeto são reimpressos, mostrando que realmente foram alterados. A seguir, uma sequência de erros, mostrando a liberdade (responsabilidade) que o programador Python possui. Entre as linhas 58 e 60 os atributos do objeto são novamente alterados, desta vez sem o uso dos setters. Como não há checagem de erros neste acesso direto, valores errados podem ser passados para os atributos, sem que qualquer erro seja reportado. No caso, valores inválidos são passados para os atributos e nas linhas 63, 64 e 65 os mesmos são impressos (sem o uso dos getters), provando que o objeto já está inconsistente.

Como apresentado no texto anterior, caso um atributo de uma classe tenha um ou mais underscores no início do seu nome, o usuário da classe deve ter cuidado ao usá-lo, consultando a documentação da classe para isso (esta prática deveria ser hábito em qualquer situação). O mal uso dos atributos de uma classe, pode prejudicar o programa como um todo e desenvolvedor do mesmo, por consequência. Apesar de Python dar a liberdade de se acessar atributos que deveriam ser privados, o programador não deveria abusar deste direito, usando-o o mínimo possível e com muita cautela e consciência, caso o utilize. Via de regra, sempre que um atributo possuir um getter ou setter, utilize-o. Se não possui-lo e o mesmo for privado, verifique se realmente é necessário acessá-lo diretamente, uma vez que o projetista da classe pode não querer que este acesso seja feito.

Série Orientação a Objetos: Acessos a Atributos

Leia Também

Nenhum comentário:

Postar um comentário