18.9 - Encapsulamento de dados
Os capítulos anteriores demonstram um plano de desenvolvimento que poderíamos chamar de “projeto orientado a objeto”. Identificamos os objetos de que precisamos – como Point, Rectangle e Time – e definimos classes para representá-los. Em cada caso há uma correspondência óbvia entre o objeto e alguma entidade no mundo real (ou, pelo menos, no mundo matemático).
Mas, às vezes, é menos óbvio quais objetos você precisa e como eles devem interagir. Nesse caso é necessário um plano de desenvolvimento diferente. Da mesma forma em que descobrimos interfaces de função por encapsulamento e generalização, podemos descobrir interfaces de classe por encapsulamento de dados.
A análise de Markov, de “Análise de Markov”, na página 200, apresenta um bom exemplo. Se baixar o meu código em http://thinkpython2.com/code/markov.py, você vai ver que ele usa duas variáveis globais – suffix_map e prefix – que são lidas e escritas a partir de várias funções.
suffix_map = {}
prefix = ()
Como essas variáveis são globais, só podemos executar uma análise de cada vez. Se lermos dois textos, seus prefixos e sufixos seriam acrescentados às mesmas estruturas de dados (o que geraria textos interessantes).
Para executar análises múltiplas e guardá-las separadamente, podemos encapsular o estado de cada análise em um objeto. É assim que fica:
class Markov:
def __init__(self):
self.suffix_map = {}
self.prefix = ()
Em seguida, transformamos as funções em métodos. Por exemplo, aqui está process_word:
def process_word(self, word, order=2):
if len(self.prefix) < order:
self.prefix += (word)
return
try:
self.suffix_map[self.prefix].append(word)
except KeyError:
# se não houver entradas deste prefixo, crie uma.
self.suffix_map[self.prefix] = [word]
self.prefix = shift(self.prefix, word)
Transformar um programa como esse – alterando o projeto sem mudar o comportamento – é outro exemplo de refatoração (veja “Refatoração”, na página 70).
Este exemplo sugere um plano de desenvolvimento para projetar objetos e métodos:
-
Comece escrevendo funções que leiam e criem variáveis globais (quando necessário).
-
Uma vez que o programa esteja funcionando, procure associações entre variáveis globais e funções que as usem.
-
Encapsule variáveis relacionadas como atributos de objeto.
-
Transforme as funções associadas em métodos da nova classe.
Como exercício, baixe o meu código de Markov de http://thinkpython2.com/code/markov.py e siga os passos descritos acima para encapsular as variáveis globais como atributos de uma nova classe chamada Markov.
Solução: http://thinkpython2.com/code/Markov.py (observe o M maiúsculo).