Princípios de POO e nomes de métodos

22
class Boxer:

    def punch(self, punching_bag, strength):
        punching_bag.punch(strength)


class PunchingBag:

    def punch(self, strength):
        print "Punching bag punched with strength", strength

boxer = Boxer()
punching_bag = PunchingBag()

boxer.punch(punching_bag, 2)

Sem dúvida, esse punché um bom nome de método no caso de um boxeador. Mas o nome punchtambém é bom para o método de saco de pancadas? Nos dois casos, quero dizer soco como um comando (ou seja, soco).

clima
fonte

Respostas:

23

Uma boa regra geral é que os nomes dos métodos devem ser verbos ou predicados, de modo que o objeto no qual você os chama ( selfna convenção padrão do Python, thisna maioria das outras linguagens) se torne o assunto.

Por essa regra, file.closeé meio errado, a menos que você siga o modelo mental de que o arquivo se fecha ou que o fileobjeto não representa o arquivo, mas um identificador de arquivo ou algum tipo de objeto proxy.

Um saco de pancadas nunca se perfura, portanto, punchingBag.punch()está errado de qualquer maneira. be_punched()é tecnicamente correto, mas feio. receive_punch()pode funcionar ou handle_punch(). Outra abordagem, bastante popular no JavaScript, é tratar chamadas de método como eventos, e a convenção deve incluir o nome do evento, prefixado com 'on', para que seja on_punched()ou on_hit(). Como alternativa, você pode adotar a convenção de que particípios passados ​​indicam voz passiva e, por essa convenção, o nome do método seria justo punched().

Outro aspecto a considerar é se o saco de pancadas realmente sabe o que o atingiu: faz diferença se você o perfura, bate com um graveto ou se depara com um caminhão? Se sim, qual é a diferença? Você pode resumir a diferença em um argumento ou precisa de métodos diferentes para diferentes tipos de punição recebida? Um único método com um parâmetro genérico é provavelmente a solução mais elegante, porque mantém o grau de acoplamento baixo, e esse método não deve ser chamado punched()ou handle_punch(), mas algo mais genérico receive_hit(). Com esse método, você pode implementar todos os tipos de atores que podem atingir os sacos de pancadas, sem alterar o próprio saco de pancadas.

tdammers
fonte
4
@ Artur: sim e não. Os arquivos podem (conceitualmente falando) fechar-se quando solicitados; matrizes podem se classificar; mas os sacos de pancadas não se perfuram.
Tdammers
2
Tudo bem, se nosso saco de pancadas atinge a parede em velocidade insana, é a parede que o perfurou, ou foi o saco de pancadas que sofreu um golpe em si e na verdade por si mesmo?
1
@ Tdammers: Sua sugestão de generalizar também pode levar a uma interface chamada Hitable.
Jens Piegsa #
2
@ Artur: Eu acho que é aqui que a OOP pressupõe que cada frase tem um assunto natural e que essa idéia é aplicável à programação.
Tdammers
1
Então a questão principal é. Se os arquivos podem se fechar, as matrizes podem se classificar etc., por que os sacos de pancada não podem se perfurar? Existe alguma diferença real ou é apenas que, no primeiro caso, estamos acostumados a isso e no segundo, não estamos?
clime
6

Eu acho que é uma questão conceitual (como pensamos sobre o mundo). Tudo bem dizer:

  • Olha, a porta está se fechando. door.close()
  • Uau, o papel está dobrando por conta própria. paper.fold()
  • Que diabos?! O arquivo na mesa acabou de fechar, ninguém está por perto. file.close()

É estranho dizer:

  • Aquele saco de pancadas na academia acaba de se dar um soco. bag.punch()

Precisaria ter algo para se socar em primeiro lugar (por exemplo, braços). Você provavelmente diria:

  • O saco de pancadas começou a se mover sozinho, como se alguém o tivesse dado um soco. punching_bag.move()

Tudo bem para objetos programáticos fazerem coisas que normalmente outras pessoas fazem com / com eles (no "mundo real"). Mas acho que deve sempre fazer pelo menos algum sentido que a coisa esteja fazendo isso com ela mesma . Você deve poder imaginá-lo facilmente sem ficar obscuro (como no caso do punching_bag).

clima
fonte
2

É uma questão de gosto, eu acho. Punching bagO punch()método de é pelo menos consistente com file.close()ou frame.move()no sentido de experimentar ação em si mesmo. Maior pergunta seria: por Boxerque tem punch(something)método?


fonte
Eu gosto do seu ponto sobre file.close (). Isso era algo que eu estava tentando entender. Talvez o boxeador tenha um método de soco, porque também há um treinador treinando o boxeador. Bem, na verdade, eu estava apenas tentando dar um exemplo de uma ação (mensagem) sendo passada através de vários objetos, sendo o último "objeto de uma ação". Eu tenho um pequeno problema com list.append (4), account.deposit (50), file.close (), paper.fold () vs. boxer.punch (), dog.bark (), logger.log () etc .
clime
Ao passar por vários objetos, existem 2 casos: você usa o contexto vinculado (próprio) e não. Se você fizer isso, seus métodos devem ser Coach.sayPunchToBoxer(), Boxer.punchNearestBag()e Bag.punch(). Caso contrário, você deve adivinhar o que acontecerá toda vez que ligar Coach.punch(). A regra geral é: se o objeto que experimenta ação não for especificado no nome do método, o destinatário será esse objeto.
Bem, acho que isso também está certo: coach.say_punch (boxer, punching_bag), boxer.punch (punching_bag). ou seja, o receptor não está no nome do método, mas nos parâmetros.
clime
1
Claro, eu quis dizer que o receptor de ação deve ser adivinhado a partir da declaração de chamada.
2

Você tem duas mensagens diferentes: uma para comandar um objeto a ser perfurado e outra para informar um objeto que foi perfurado. Considere que um objeto Boxer provavelmente precisará responder a ambos . Diferentemente . Essa é realmente uma boa razão para dar nomes diferentes a eles.

Minha inclinação seria manter punch(boxer, object, strength)e renomear o método oposto a punched. Você pode chamá-lo handle_punchou algo assim, mas ainda é ambíguo se é para manipular um comando de soco ou a notificação de que ele foi socado.

user2313838
fonte
Um bom argumento sobre o Boxer que precisa de soco e algo como handle_punch (seria defendneste caso em particular). Mas o saco de pancadas nunca será bidirecional assim. E já existe este file.close () ...
clime
defendé um comando. É uma ação possível que um objeto poderia executar em resposta punched, mas você não gostaria que outros objetos invocassem defenddiretamente.
User2313838 de
2

Sua abordagem acabará por levar a códigos muito acoplados.

Para resumir Eric Lippert idealmente aqui, você gostaria que o boxeador fosse capaz de perfurar muitas coisas. Ter o saco de pancadas como assinatura da função boxeador implica que o boxeador seja criado com um conhecimento imediato de Tudo (que é passível de punção). Além disso, dar um soco e receber um soco são duas coisas MUITO diferentes, portanto elas não devem compartilhar o mesmo nome.

Prefiro modelar isso como um Boxer que cria um soco (outro objeto que contém o atributo de força, alcance, direção, etc.).

Em seguida, faça com que o saco de pancadas com um método como o onPunch, que recebe esse objeto de punção, possa calcular o efeito do punção sobre si mesmo.

Tendo isso em mente, o nome das coisas importa muito. Ele deve se encaixar no modelo mental que você tem da situação. Se você está tentando explicar como algo pode acontecer que não faz sentido à primeira vista, ou se você está tendo mais dificuldade em nomear algo, talvez seu modelo esteja errado e precise mudar.

É difícil mudar um modelo depois que você começou, as pessoas geralmente tendem a distorcer a realidade para se ajustar ao modelo. O problema é que, ao dobrar as coisas para encaixar (como um saco de pancadas que pode perfurar as coisas), o mundo que você está criando se torna cada vez mais complexo e as interações ficam cada vez mais difíceis de implementar. Você chegará a um ponto em que adicionar até a coisa mais trivial se tornará um pesadelo de mudanças e bugs. Essa dívida técnica conceitual pode ter um preço muito alto, mesmo que o custo inicial tenha sido percebido na época como a coisa mais barata a se fazer.

Newtopian
fonte
1

Esse é o problema que chamo de confusão "objeto / sujeito" e é bastante prevalente.

As frases geralmente têm um sujeito que faz o verbo em seu objeto de destino .

Agora, com relação à programação, a única coisa que realmente faz as coisas é o computador. Ou virtualmente um processo, fio ou fibra. Os objetos não são animados por padrão. Eles não têm seus próprios threads em execução, então não podem realmente fazer nada.

Isso significa que os métodos operam neles, eles são o alvo da ação e não quem a executa. É por isso que os chamamos de "objetos" e não de "sujeitos"!

Quando você diz File.closeque não é o arquivo que se fecha, é o thread em execução atual que fecha o arquivo. Se você disser Array.sort, o segmento em execução atual classifica a matriz. Se você diz HttpServer.sendRequest, o encadeamento em execução atual envia a solicitação ao servidor (não vice-versa!). Da mesma forma, dizer PunchingBag.punchsignifica que o fio de corrida atual perfura a bolsa.

Isso significa que, se você deseja Boxerperfurar, deve ser uma subclasse de um Threadpara que possa executar ações como sacos de perfuração em sua função de rosca.

No entanto, às vezes também faz sentido dizer que o saco de pancadas se perfura no caso em que cada objeto tem seu próprio encadeamento. Você pode evitar condições de corrida e implementar chamadas de método como passagem de mensagem: você perfura o saco enviando a punchmensagem, são perfurações no encadeamento ele envia a punch successfulmensagem de volta , mas isso é apenas um detalhe de implementação.

Calmarius
fonte
0

Concordo que "punch" é um bom nome de método para a classe Boxer, pois (com alguns ajustes) poderia ser reutilizado contra outros objetos. Também descreve com precisão que um objeto de uma classe está executando uma ação em outro objeto. No entanto, renomearia o método para "doPunch", para demonstrar mais claramente a relação.

Para a classe PunchingBag, no entanto, acho o nome do método muito vago ou um pouco impreciso do que está acontecendo no método. Quando vejo "soco", acho que algo está socando outra coisa. No entanto, aqui, o objeto PunchingBag está reagindo a um soco de um objeto (nesse caso, um objeto Boxer). Portanto, renomeio o método aqui para "isPunched" para ilustrar que o objeto está reagindo a um soco.

Porém, esta é a minha interpretação de como eu nomearia os métodos. É tudo uma questão de gosto e quais padrões você está seguindo.

Marvon
fonte
3
isPunchedé realmente enganoso (mais ou menos, dependendo do esquema de nomenclatura da estrutura).
É comum que o método seja aplicado a esse objeto, no qual é chamado. O que há de errado com apenas punch()?
Bem, compreendo perfeitamente que é necessário especificar a direção da ação, mas acho que há algo sobre OOP e sua filosofia que torna isso desnecessário. Algum tipo de abstração que está relacionada com a famosa explicação de que os objetos "enviam mensagens" um para o outro.
clime
Se não for óbvio pelo nome do método o que o método está fazendo, será um problema com o nome. Não é um problema com OO ou qualquer outro paradigma que esteja sendo usado. É por isso que punch () em um saco de pancadas está errado em qualquer contexto que você queira usá-lo. O que significa quando você diz soco em um saco de pancadas? É também por isso que você não pode assumir, por nenhuma filosofia, que algo é desnecessário em situações em que a suposição cria ambiguidade. Há casos em que as regras práticas funcionam e situações em que não. Se as regras práticas sempre funcionassem, elas seriam chamadas de regras (sem o "polegar").
Dunk
-2

hmmmm. Estou questionando o saco de pancadas como uma classe, porque você realmente não se importa com o saco de pancadas - você se preocupa com o impacto e a força do punho dos Boxers. portanto, os métodos devem ser sobre o que quer que esteja medindo e relatando o impacto do soco. mesmo que isso venha do 'saco de pancadas', a nomeação ainda deve revelar a responsabilidade - como punchImpactMeter etc.

cartalote
fonte
-3

O boxeador soca o saco de pancadas -> boxer.punch

O punchingbag é perfurado pelo boxeador -> punchingbag.get_punch

P3Dr0
fonte
3
este não parece oferecer nada substancial sobre pontos feitos e explicado em anteriores 6 respostas
mosquito