Принципы ООП и названия методов

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)

Без сомнения, punchэто хорошее имя метода в случае боксера. Но punchхорошо ли название для метода пробивая? В обоих случаях я имею в виду удар как команду (то есть делать удар).

климат
источник

Ответы:

23

Хорошее практическое правило заключается в том, что имена методов должны быть глаголами или предикатами так, чтобы объект, к которому вы их вызывали ( selfв стандартном соглашении Python, thisв большинстве других языков), становился субъектом.

Это правило file.closeявляется своего рода неправильным, если вы не используете ментальную модель, согласно которой файл закрывается сам или fileобъект не представляет сам файл, а скорее дескриптор файла или какой-либо прокси-объект.

Боксерская груша никогда не пробивает себя сама, поэтому punchingBag.punch()в любом случае это неправильно. be_punched()технически правильно, но безобразно. receive_punch()может сработать или handle_punch(). Другой подход, довольно популярный в JavaScript, состоит в том, чтобы рассматривать такие вызовы методов как события, и существует соглашение, заключающееся в том, чтобы использовать имя события с префиксом «on», так что это будет on_punched()или on_hit(). В качестве альтернативы вы могли бы принять соглашение, согласно которому причастия прошлых лет указывают на пассивный голос, и согласно этому соглашению имя метода будет справедливым punched().

Еще один аспект, который следует рассмотреть, заключается в том, знает ли боксерская груша, что на нее попало: имеет ли значение, бьете ли вы ее, бьете ее палкой или врезаетесь в нее грузовиком? Если так, то в чем разница? Можете ли вы свести разницу к аргументу или вам нужны разные методы для разных видов наказания? Один метод с универсальным параметром, вероятно, является наиболее элегантным решением, поскольку он поддерживает низкую степень связи, и такой метод не следует вызывать punched()или handle_punch(), а скорее как нечто более общее receive_hit(). Используя такой метод, вы можете реализовать все виды актеров, которые могут поразить боксерские груши, не меняя саму боксерскую грушу.

tdammers
источник
4
@ Артур: да и нет. Файлы могут (концептуально говоря) закрывать себя, когда их просят; массивы могут сортировать себя; но боксерские груши не бьют себя.
tdammers
2
Хорошо, если наш боксерский грохот ударяется о стену с безумной скоростью, это стена, которая ударила его, или это груша испытывает удар по себе и по себе?
1
@tdammers: Ваше предложение обобщить также может привести к интерфейсу под названием Hitable.
Jens Piegsa
2
@Artur: Я думаю, что это то, где предположение ООП, что у каждого предложения есть естественный предмет, и что эта идея применима к программированию, разрушается.
tdammers
1
Итак, главный вопрос. Если файлы могут закрываться, массивы могут сортироваться и т. Д., Почему боксерские груши не могут пробивать себя? Есть ли реальная разница или просто в первом случае мы к этому привыкли, а во втором нет?
Клим
6

Я думаю, что это концептуальная проблема (как мы думаем о мире). Можно сказать:

  • Смотри, дверь закрывается. door.close()
  • Ничего себе, бумага складывается сама по себе. paper.fold()
  • Что за черт?! Этот файл на столе только что закрыт, вокруг никого нет. file.close()

Странно говорить:

  • Эта боксерская груша в спортзале только что пробила себя. bag.punch()

Во-первых, ему нужно было бы что-то ударить (например, оружие). Вы, вероятно, сказали бы:

  • Боксерская груша начала двигаться сама по себе, как если бы кто-то ее ударил. punching_bag.move()

Программные объекты могут делать то, что обычно делают с ними / с ними (в «реальном мире»). Но я думаю, что всегда должно быть хоть какое-то чувство, что вещь делает это с собой . Вы должны быть в состоянии представить это легко, не становясь неясным (как в случае с punching_bag).

климат
источник
2

Это вопрос вкуса, я думаю. Punching bagЭтот punch()метод, по крайней мере, соответствует file.close()или frame.move()в смысле ощущения воздействия на себя. Большой вопрос был бы, почему Boxerесть punch(something)метод вообще?


источник
Мне нравится ваша точка зрения о file.close (). Это было то, к чему я стремился. Возможно у боксера есть метод удара, потому что есть тренер, тренирующий боксера. Ну, на самом деле, я просто пытался привести пример действия (сообщения), проходящего через несколько объектов, последний из которых был «объектом действия». У меня небольшая проблема с list.append (4), account.deposit (50), file.close (), paper.fold () против boxer.punch (), dog.bark (), logger.log () и т. Д. .
климат
При прохождении через несколько объектов есть 2 случая: вы используете связанный контекст (self), а вы - нет. Если вы делаете, ваши методы должны быть Coach.sayPunchToBoxer(), Boxer.punchNearestBag()и Bag.punch(). В противном случае вы должны угадать, что будет происходить каждый раз, когда вы звоните Coach.punch(). Общее правило: если объект, который испытывает действие, не указан в имени метода, тогда получатель является этим объектом.
Ну, я думаю, что это тоже хорошо: coach.say_punch (boxer, punching_bag), boxer.punch (punching_bag). т.е. получатель не в имени метода, а в параметрах.
Клим
1
Конечно, я имел в виду, что получатель действия должен быть угадан из оператора вызова.
2

У вас есть два разных сообщения: одно, чтобы дать команду объекту пробить, а другое сообщить объекту, что он был пробит. Учтите, что объекту Boxer, вероятно, понадобится ответить на оба вопроса . По разному . Это действительно хорошая причина дать им разные имена.

Я бы хотел сохранить punch(boxer, object, strength)и переименовать противоположный метод punched. Вы могли бы назвать это handle_punchили что-то в этом роде, но тогда все еще остается неоднозначным, обрабатывать ли это команду перфорации или уведомление о том, что она была перфорирована.

user2313838
источник
Хорошая мысль о том, что Boxer нужен и punch, и что-то вроде handle_punch ( defendв данном конкретном случае). Но боксерская груша никогда не будет такой двунаправленной. И уже есть этот файл .close () ...
clime
defendэто команда. Это одно из возможных действий, которое объект может выполнить в ответ punched, но вы не хотели бы, чтобы другие объекты вызывались defendнапрямую.
user2313838
2

Ваш подход в конечном итоге приведет к очень связанному коду.

Подводя итог, можно сказать, что в идеале Эрик Липперт хотел бы, чтобы боксер мог ударить по многим вещам. Наличие боксерской груши в качестве подписи функции боксера подразумевает, что боксер создан с немедленным знанием всего (что можно пробить). Плюс удар и получение удара - это две ОЧЕНЬ разные вещи, поэтому они не должны иметь одно и то же имя.

Я предпочел бы смоделировать это как боксера, который создает удар (другой объект, который содержит силу удара удара, направление, направление и т. Д.).

Затем используйте боксерскую грушу с помощью метода, такого как onPunch, который получает этот объект перфорации, чтобы вычислить эффект перфорации на себе.

Помня об этом, имя вещей имеет большое значение. Это должно соответствовать вашей ментальной модели ситуации. Если вы обнаружите, что пытаетесь объяснить, как что-то может произойти, что на первый взгляд не имеет смысла, или если вам тяжело назвать что-либо, тогда, возможно, ваша модель неправильна и должна измениться.

Трудно изменить модель после того, как вы начали, люди обычно склоняются к реальности, чтобы соответствовать модели. Проблема заключается в том, что по мере того, как вы сгибаете вещи, чтобы соответствовать (например, боксерской груше, которая может пробивать вещи), мир, который вы создаете, становится все более и более сложным, а взаимодействие становится все более и более трудным для реализации. В конечном итоге вы достигнете точки, когда добавление даже самых тривиальных вещей станет кошмаром изменений и ошибок. Эта концептуальная техническая задолженность может иметь очень высокую цену, даже если первоначальная стоимость в то время воспринималась как самая дешевая вещь.

Newtopian
источник
1

Это проблема, которую я называю «объект / субъект» путаница, и она довольно распространена.

У предложений обычно есть субъект, который делает глагол на их целевом объекте .

Что касается программирования, единственное, что на самом деле делает вещи - это компьютер. Или практически процесс, нить или волокно. Объекты не анимированы по умолчанию. У них нет собственных потоков, поэтому они ничего не могут сделать.

Это означает, что методы воздействуют на них, они являются целью действия, а не тем, кто выполняет действие. Вот почему мы называем их «объектами», а не «субъектами»!

Когда вы говорите File.close, что файл закрывается не сам, а текущий запущенный поток закрывает файл. Если вы говорите Array.sort, текущий запущенный поток сортирует массив. Если вы говорите HttpServer.sendRequest, текущий запущенный поток отправляет запрос на сервер (не наоборот!). Подобное высказывание PunchingBag.punchозначает, что текущая бегущая нить пробивает мешок.

Это означает, что если вы хотите, Boxerчтобы Threadфункция была способна перфорировать, то она должна быть подклассом, чтобы она могла выполнять такие вещи, как перфорирование мешков в своей функции потока.

Однако иногда также имеет смысл сказать, что боксерская груша пробивает себя в случае, когда у каждого объекта есть свой собственный поток, вы можете избежать условий гонки и реализовывать вызовы методов как передачу сообщений: вы punchпробиваете мешок, отправляя ему сообщение, это пробивает поток Затем сам отправляет вам punch successfulсообщение, но это только детали реализации.

Calmarius
источник
0

Я согласен с тем, что «punch» - это хорошее имя метода для класса Boxer, поскольку (с некоторыми изменениями) его можно повторно использовать против других объектов. Он также точно описывает, что объект класса выполняет действие над другим объектом. Хотя я бы переименовал метод в «doPunch», чтобы более четко продемонстрировать связь.

Однако для класса PunchingBag я нахожу имя метода слишком расплывчатым или немного неточным относительно того, что происходит в методе. Когда я вижу «удар», я думаю, что что-то пробивает что-то другое. Однако здесь объект PunchingBag реагирует на удар от объекта (в данном случае, объекта Boxer). Итак, я бы переименовал метод здесь в isPunched, чтобы проиллюстрировать, что объект реагирует на удар.

Хотя это моя интерпретация того, как я бы назвал методы. Все дело вкуса и стандартов, которым вы следуете.

Марвон
источник
3
isPunchedдействительно вводит в заблуждение (более или менее, в зависимости от схемы именования платформы).
Обычно этот метод применяется к тому объекту, для которого он вызывается. Что не так с просто punch()?
Ну, я полностью понимаю, что нужно указать направление действий, но я думаю, что в ООП и его философии есть что-то, что делает это ненужным. Какая-то абстракция, связанная с тем известным объяснением, что объекты «отправляют сообщения» друг другу.
Клим
Если из имени метода не очевидно, что делает метод, тогда это проблема с именем. Это не проблема с ОО или какой-либо другой парадигмой. Вот почему punch () на боксерской груше не подходит для любого контекста, в котором вы хотите ее использовать. Что это значит, когда вы говорите удар в боксерскую грушу? Это также, почему вы не можете предположить ни одной философией, что что-то ненужное в ситуациях, когда предположение создает двусмысленность. Есть случаи, когда правила большого пальца работают и ситуации, когда они не работают. Если бы эмпирические правила всегда работали, тогда их называли бы правилами (без «большого пальца»).
Данк
-2

хммм. Я подвергаю сомнению боксерскую грушу как класс, потому что вы действительно не заботитесь о боксерской груше - вы заботитесь о воздействии и силе кулака боксеров. таким образом, методы должны быть о том, что измеряет и сообщает о воздействии удара. даже если это исходит из «боксерской груши», наименование должно по-прежнему раскрывать ответственность - как punchImpactMeter и т. д.

cartalot
источник
-3

Боксер пробивает боксерскую грушу -> boxer.punch

Боксер пробивает боксерскую грушу -> punchingbag.get_punch

P3Dr0
источник
3
это, кажется, не предлагает ничего существенного по сравнению с замечаниями, сделанными и объясненными в предыдущих 6 ответах
комнат