Принципи та назви OOP


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_punched()або on_hit(). Крім того, ви можете прийняти умову про те, що минулі дієприкметники вказують на пасивний голос, і за цією умовою назва методу була б справедливою punched().

Ще один аспект, який слід враховувати, чи знає мішок для пробивання насправді, що його вдарило: чи має значення це чи вдарити його, бити палицею чи наткнутися на нього вантажівкою? Якщо так, то в чому різниця? Чи можете ви звести різницю до аргументу, або вам потрібні різні методи для різних видів отриманого покарання? Один метод із загальним параметром, мабуть, є найелегантнішим рішенням, оскільки він підтримує низький ступінь зв'язку, і такий метод не слід називати punched()або handle_punch(), а щось більш загальне, як receive_hit(). За допомогою такого методу ви можете реалізовувати всілякі актори, які можуть вдарятись пробивати мішки, не змінюючи самого мішка для штампування.


4
@Artur: так і ні. Файли можуть (концептуально кажучи) закриватися, коли запитують; масиви можуть сортувати себе; але пробиваючі мішки не пробивають себе.
tdammers

2
Гаразд, якщо наш мішок для штампування ударяє об стіну з шаленою швидкістю, це стіна, яка його пробила, чи це мішок, що пробиває, зазнав удару по собі та власне самому?

1
@tdammers: Ваша пропозиція щодо узагальнення також може призвести до викликаного інтерфейсу Hitable.
Jens Piegsa

2
@Artur: Я думаю, що саме тут випливає припущення OOP про те, що кожне речення має природний предмет, і що ця ідея застосовна для програмування.
tdammers

1
Тож головне питання. Якщо файли можуть закриватися самостійно, масиви можуть сортувати себе тощо, чому боксерські мішки не можуть пробивати себе? Чи є реальна різниця чи це просто те, що в першому випадку ми звикли до цього, а в другому - ні?
clime

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 () vs. boxer.punch (), dog.bark (), logger.log () тощо .
clime

При проходженні через декілька об'єктів є 2 випадки: ви використовуєте зв'язаний контекст ("Я"), а ви - ні. Якщо ви це робите, ваші методи повинні бути Coach.sayPunchToBoxer(), Boxer.punchNearestBag()і Bag.punch(). Інакше вам доведеться здогадуватися, що буде щоразу, коли ви телефонуєте Coach.punch(). Загальне правило: якщо об'єкт, який зазнає дії, не вказаний у назві методу, то приймачем є цей об'єкт.

Ну, я думаю, це теж добре: coach.say_punch (боксер, пробиваючий мішок), boxer.punch (punching_bag). тобто приймач не в назві методу, а в параметрах.
clime

1
Звичайно, я мав на увазі, що приймач дій повинен відповідати заяві виклику.

2

У вас є два різних повідомлення: одне - командувати об'єктом для удару та одне - повідомити об'єкт про його удар. Вважайте, що об'єкт Boxer, ймовірно, потребуватиме відповіді на обидва . По-різному . Це дійсно вагомий привід дати їм різні імена.

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


Хороший момент про те, що Boxer потребує як удар, так і щось на зразок handle_punch (було б defendу цьому конкретному випадку). Але мішок для штампування ніколи не буде таким двостороннім. І вже є цей файл.close () ...
clime

defendце команда. Це одна з можливих дій, яку об’єкт може вжити у відповідь punched, але ви не хочете, щоб інші об'єкти викликали defendбезпосередньо.
user2313838

2

Ваш підхід врешті-решт призведе до дуже зв'язаного коду.

Якщо підсумувати Еріка Ліпперта, то в ідеалі тут ви хочете, щоб боксер міг пробити багато чого іншого. Якщо мішок для перфорації є підписом функції боксера, це означає, що боксер створений з негайним знанням Все (що є перфораційним). Плюс надання удару та отримання удару - це ДУЖЕ різні речі, тому вони не повинні мати одне й те саме ім’я.

Я б скоріше моделював це як боксер, який створює удар (інший об'єкт, що містить силу атрибута удару, досяжність, напрямок тощо).

Потім у мішок для штампування таким способом, як onPunch, який отримує цей ударний об'єкт, можна обчислити вплив удару на себе.

Пам'ятаючи про це, ім'я речей має велике значення. Він повинен відповідати ментальній моделі ситуації, яку ви маєте. Якщо ви намагаєтеся пояснити, як може статися щось, що не має сенсу з першого погляду, або якщо у вас є найскладніший час називати щось, то, можливо, ваша модель неправильна і її потрібно змінити.

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


1

Це проблема, яку я називаю плутаниною "об'єкт / предмет", і вона досить поширена.

У реченнях зазвичай є суб'єкт, який виконує дієслово на їх цільовий об'єкт .

Що стосується програмування, то єдине, що насправді робить, це комп'ютер. Або практично процес, нитка або волокно. Об'єкти за замовчуванням не анімовані. У них немає власних потоків, тому вони нічого не можуть зробити.

Це означає, що методи діють на них, вони є ціллю дії, а не тим, хто робить дію. Ось чому ми називаємо їх «об’єктами», а не «предметами»!

Коли ви говорите File.close, що файл не закривається сам, це поточний запущений потік, який закриває файл. Якщо ви скажете Array.sort, поточний запущений потік сортує масив. Якщо ви скажете HttpServer.sendRequest, поточний запущений потік надсилає запит на сервер (а не навпаки!). Аналогічним чином PunchingBag.punchозначає, що поточна ходова нитка пробиває мішок.

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

Однак іноді також є сенс сказати, що штампуючий мішок пробиває себе у тому випадку, коли у кожного об’єкта є своя нитка, ви можете уникати перегонових умов і реалізовувати виклики методів як передачі повідомлення: ви пробиваєте сумку, відсилаючи їй punchповідомлення, це пробиває нитку. Тоді він сам надсилає вам punch successfulповідомлення, але це лише детальна інформація про реалізацію.


0

Я погоджуюся, що "удар" - це хороша назва методу для класу Boxer, оскільки (при певному налаштуванні) його можна було б повторно використовувати для інших об'єктів. Він також точно описує, що об’єкт класу робить дію на інший об’єкт. Хоча я б перейменував метод на "doPunch", щоб більш чітко продемонструвати співвідношення.

Для класу PunchingBag я вважаю, що назва методу є занадто розпливчастим або трохи неточним щодо того, що відбувається в методі. Коли я бачу "удар", я думаю, що щось пробиває щось інше. Однак тут об'єкт PunchingBag реагує на удар від об'єкта (в даному випадку об'єкта Boxer). Отже, я б перейменував тут метод на "isPunched", щоб проілюструвати, що об'єкт реагує на удар.

Хоча це моя інтерпретація того, як я би назвав методи. Це все питання смаку та яких стандартів ви дотримуєтесь.


3
isPunchedдійсно вводить в оману (більш-менш, залежно від схеми іменування рам).

Зазвичай метод застосовується до того об'єкта, на який він викликаний. Що з цим просто punch()?

Ну, я повністю розумію, що потрібно вказати напрямок дій, але я думаю, що є щось про OOP та його філософію, що робить це непотрібним. Якась абстракція, яка пов'язана з тим відомим поясненням, що об'єкти "надсилають повідомлення" один одному.
clime

Якщо з назви методу не очевидно, що робить метод, це проблема з ім'ям. Це не проблема з ОО або будь-якою парадигмою. Ось чому удар () на мішку для штампування помиляється в будь-якому контексті, який ви хочете використовувати. Що це означає, коли ти кажеш удар у мішок для пробивання? Це також, чому ви не можете припустити жодної філософії, що щось непотрібне в ситуаціях, коли припущення створює неоднозначність. Бувають випадки, коли правила роботи з великим пальцем і ситуації, коли їх немає. Якщо правила великого пальця завжди працювали, то їх називали б правилами (без "великого пальця").
Данк

-2

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


-3

Боксер пробиває мішок для пробивання -> boxer.punch

Боксер пробиває боксер -> punchingbag.get_punch


3
це, здається, не пропонує нічого суттєвого щодо пунктів, викладених та пояснених у попередніх 6 відповідях
гнат
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.