Problema
Estou trabalhando em um projeto Python cuja classe principal é um pouco " God Object ". Não são tão friggin muitos atributos e métodos!
Eu quero refatorar a classe.
Tão longe…
Para o primeiro passo, quero fazer algo relativamente simples; mas quando tentei a abordagem mais direta, ela quebrou alguns testes e exemplos existentes.
Basicamente, a classe tem uma lista loooonga de atributos - mas eu posso examiná-los claramente e pensar: "Esses 5 atributos estão relacionados ... Esses 8 também estão relacionados ... e depois há o resto".
getattr
Basicamente, eu só queria agrupar os atributos relacionados em uma classe auxiliar do tipo ditado. Tive a sensação de __getattr__
que seria ideal para o trabalho. Então, mudei os atributos para uma classe separada e, com certeza, __getattr__
funcionou perfeitamente bem sua mágica…
No primeiro .
Mas então tentei executar um dos exemplos. A subclasse de exemplo tenta definir um desses atributos diretamente (no nível da classe ). Mas como o atributo não estava mais "fisicamente localizado" na classe pai, recebi um erro dizendo que o atributo não existia.
@propriedade
Eu então li sobre o @property
decorador. Mas também li que isso cria problemas para as subclasses que desejam fazer self.x = blah
quando x
é uma propriedade da classe pai.
Desejado
- Faça com que todo o código do cliente continue trabalhando
self.whatever
, mesmo que awhatever
propriedade do pai não esteja "fisicamente localizada" na própria classe (ou instância). - Agrupe atributos relacionados em contêineres semelhantes a dict.
- Reduza o barulho extremo do código na classe principal.
Por exemplo, eu não quero simplesmente mudar isso:
larry = 2
curly = 'abcd'
moe = self.doh()
Nisso:
larry = something_else('larry')
curly = something_else('curly')
moe = yet_another_thing.moe()
... porque ainda é barulhento. Embora isso faça com que um atributo simplesmente seja algo que possa gerenciar os dados, o original tinha 3 variáveis e a versão aprimorada ainda possui 3 variáveis.
No entanto, eu ficaria bem com algo assim:
stooges = Stooges()
E se uma pesquisa self.larry
falhar, algo verificará stooges
se larry
há. (Mas também deve funcionar se uma subclasse tentar fazer isso larry = 'blah'
no nível da classe.)
Sumário
- Deseja substituir grupos de atributos relacionados em uma classe pai por um único atributo que armazena todos os dados em outro local
- Deseja trabalhar com o código do cliente existente que usa (por exemplo)
larry = 'blah'
no nível da classe - Deseja continuar permitindo que as subclasses estendam, substituam e modifiquem esses atributos refatorados sem saber que algo mudou
Isso é possível? Ou estou latindo na árvore errada?
fonte
Respostas:
Depois de escrever e refatorar um python "objeto de Deus", eu simpatizo. O que fiz foi dividir o objeto original em subseções com base em métodos. Por exemplo, o original se parecia com este pseudo-código:
O método stuff é uma "unidade" de trabalho independente. Eu a migrei para uma nova classe que o original instancia. Isso retirou as propriedades necessárias também. Alguns eram usados apenas pela subclasse e podiam atravessar diretamente. Outros foram compartilhados e foram transferidos para uma classe compartilhada.
O "objeto Deus" cria uma nova cópia da classe compartilhada na inicialização e cada uma das novas subclasses aceita um ponteiro como parte do método init. Por exemplo, aqui está uma versão simplificada da mala direta:
Ele é criado uma vez e compartilhado entre as diferentes classes que precisam de recursos de correspondência.
Então, para você, crie uma classe
larry
com as propriedades e métodos que você precisa. Em todos os lugares que o cliente solicitar,larry = blah
substitua-olarryObj.larry = blah
. Isso migra as coisas para os subprojetos sem interromper a interface atual.A única outra coisa a fazer é procurar por "unidades de trabalho". Se você iria transformar parte do "Objeto de Deus" em seu próprio método, faça-o . Mas, coloque o método fora dele. Isso força você a criar uma interface entre os componentes.
Estabelecer esse fundamento permite que todo o resto o siga. Por exemplo, uma parte do objeto auxiliar que demonstra como ele interage com a mala direta:
Concentre-se na menor unidade individual de trabalho possível e mova-a para fora. Isso é mais fácil de fazer e permite que você jogue com a configuração rapidamente. Não olhe as propriedades para mover coisas, elas são auxiliares das tarefas que estão sendo feitas com eles na maioria dos casos. O que sobrar depois que você lida com os métodos provavelmente deve permanecer no objeto original, pois faz parte do estado compartilhado.
Porém , os novos objetos agora devem aceitar as propriedades de que precisam como variáveis de inicialização, sem tocar na propriedade dos objetos de chamada. Em seguida, eles retornam quaisquer valores necessários, que o chamador pode usar para atualizar as propriedades compartilhadas conforme necessário. Isso ajuda a dissociar os objetos e cria um sistema mais robusto.
fonte