Уникнення об'єктно-орієнтованих підводних каменів, міграції з C, що для вас спрацювало?


12

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

Я пройшов університетський курс з ООП і розумію основи інкапсуляції, абстрагування даних, поліморфізм, модульність та успадкування.

Я читаю /programming/2688910/learning-to-think-in-the-object-oriented-way та /programming/1157847/learning-object-oriented-thinking , і перегляне деякі книги, на які вказували ці відповіді.

Я думаю, що кілька моїх проектів середнього та великого розміру отримають користь від ефективного використання OOP, але як новачок я хотів би уникнути багаторазових помилок.

Виходячи з вашого досвіду, що це за підводні камені та які розумні шляхи навколо них? Якщо ви можете пояснити, чому вони підводні камені, і як ваша пропозиція ефективна у вирішенні проблеми, це буде вдячно.

Я думаю про те, як на кшталт "Чи загально мати достатню кількість методів спостерігачів і модифікаторів і використовувати приватні змінні чи є методи їх консолідації / зменшення?"

Мене не хвилює використання C ++ як чистої мови OO, якщо є вагомі причини для змішування методів. (Нагадуючи про причини використання GOTO, хоч і економно.)

Дякую!


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

@stijn По суті, ви говорите, якщо це не потрібно в класі, не кладіть його туди. Наприклад, я бачу багато функцій членів утиліти, які легко можуть бути безкоштовними функціями в коді, який я читав до цього часу.
Стівен

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

Відповіді:


10

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


7
Додавши до цього, ми можемо «програмувати за наміром», тобто сказати, написати який-небудь зразок коду, який би використовував новий клас, побачити, які типи методів та політик дозволяють зручно користуватися. Потім використовуйте цю інформацію як базову лінію для реалізації.

2
Чудово теоретично - дизайнерські інтерфейси, і ви в основному все зробили. Але на практиці це не так просто. Дизайн та реалізація інтерфейсу часто йдуть рука об руку в послідовних ітераціях, поки остаточний інтерфейс не кристалізується. На той час ви, ймовірно, будете мати і остаточну реалізацію.
Джин Бушуєв

9

Ну перше - гріз викриття занадто багато інформації. За замовчуванням має бути private, ні public.

Після цього надходить занадто багато геттерів / сетерів. Скажімо, у мене є учасник даних. Мені справді потрібні ці дані в іншому класі? Гаразд, зробіть геттер. Чи мені дійсно, дійсно потрібно змінювати ці дані протягом тривалості життя об'єкта? Потім зробіть сетер.

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


2
так, це досить популярно серед тих, хто розуміє інкапсуляцію поверхово, щоб зробити дані приватними, а потім підірвати інкапсуляцію за допомогою геттерів та сетерів.
Джин Бушуєв

Java - це єдина популярна мова, яка все ще потребує методів get / set. Кожна інша мова підтримує властивості, тому між членами загальнодоступних даних та властивістю немає синтаксичної різниці. Навіть на Java не гріх мати членів публічних даних, якщо ви також контролюєте всіх користувачів класу.
кевін клайн

Учасників публічних даних важче підтримувати. За допомогою геттерів / сетерів (або властивостей) ви можете зберігати інтерфейс, змінюючи внутрішнє представлення даних, не змінюючи зовнішнього коду. У мовах, де властивості синтаксично ідентичні змінним-членам з точки зору споживача, ця точка не відповідає.
tdammers

Поки що я думаю, що я дивлюся на приватних членів "свого роду" як на локальні змінні у функції ... іншому світу не потрібно нічого знати про них, щоб fn працював ... і якщо fn змінюється, лише інтерфейс повинен бути послідовним. Я не схиляюсь до отримання спаму-геттера / сеттера, щоб я був на правильному шляху, адже, дивлячись на буферний клас, про який я писав, взагалі немає членів публічних даних. Дякую!
Стівен

2

Коли я перетнув цю прірву, я вирішив наступний підхід:

0) Я почав повільно, з невеликими / середніми процедурними програмами, і не мав критичних завдань на роботі.

1) просте відображення першого проходу з того, як я б писав програму з нуля в стилі OO - найголовніше для мене в той час, і це IS суб'єктивно - було з'ясувати всі базові класи. Моїм завданням було якомога більше капсулювати в базові класи. Чисті віртуальні методи для всього можливого в базових класах.

2) Тоді наступним кроком було створення похідних.

3) завершальним кроком було - в оригінальному процесуальному коді відокремити структури даних від коду окупатора / модифікатора. Потім використовуйте приховування даних і перерахуйте всі загальні дані в базові класи, а в підкласи перейшли дані, які не були поширеними для всієї програми. І те саме поводження з процедурним кодом спостерігача / модифікатора - вся логіка «використовується скрізь» входила в базові класи. І перегляд / модифікація логіки, яка діяла лише на підмножині даних, перейшла до похідних класів.

Це суб'єктивно, але це ШВИДКО, якщо ви добре знаєте процедурний код та структури даних. ПОМИЛКА в огляді коду - це коли трохи даних або логіки не відображаються в базових класах, але використовуються всюди.


2

Погляньте на інші успішні проекти, які використовують OOP, щоб відчути гарний стиль. Рекомендую переглянути Qt - проект, на який я завжди звертаю увагу, приймаючи власні дизайнерські рішення.


Так! Перш ніж людина стає господарем чогось, він вчиться наслідувати великих майстрів, які працювали до нього. Вивчення хорошого коду інших реалізованих - це хороший спосіб навчитися. Я рекомендую поглянути на прискорення, починаючи від простих конструкцій і заглиблюючись, як розуміння людини поліпшується.
Джин Бушуєв

1

Залежно від того, з ким ви спілкуєтесь, усі дані в OOP повинні бути приватними або захищеними та доступними лише через аксесуари та мутатори. Взагалі, я вважаю, що це хороша практика, але бувають випадки, коли я розходяся з цією нормою. Наприклад, якщо у вас є клас (у Java, скажімо так), який є єдиним призначенням - пов'язувати деякі фрагменти даних в логічну одиницю, має сенс залишати поля загальнодоступними. Якщо це непорушна капсула, просто позначте їх остаточними та ініціалізуйте їх у конструкторі. Це зводить клас (у даному випадку) до трохи більше структури (адже, використовуючи C ++, ви насправді повинні називати це структурою. Працює як клас, але видимість за замовчуванням є загальнодоступною, і ви маєте намір зрозуміліше), але Я думаю, ви побачите, що користуватися ним у цих випадках набагато зручніше.

Одне, чого ви точно не хочете робити, - це поле, яке необхідно перевірити на узгодженість мутатора і залишити його загальнодоступним.


3
Я б також запропонував, якщо у вас є такі класи, які просто містять загальнодоступні дані, ви повинні оголосити їх як structs - це не має смислової різниці, але це уточнює намір і робить їх використання здаватися більш природним, особливо для програмістів C.

1
Так, я згоден тут, якщо ви перебуваєте на C ++ (про що згадував питання), але я більшу частину своєї роботи роблю на Java, і, на жаль, у нас немає структури.

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

1

Шаблони впровадження книги Кента Бека - це відмінне підґрунтя для використання об'єктно-орієнтованих механізмів, а не їх використання.


1

Мене не хвилює використання C ++ як чистої мови OO, якщо є вагомі причини для змішування методів. (Нагадуючи про причини використання GOTO, хоч і економно.)

Я не думав, що мені було багато запропонувати розмову, поки не побачив цього шматочка. Я повинен не погодитися з настроями. OOP - лише одна з парадигм, які можна і потрібно використовувати в C ++. Чесно кажучи, на мою думку, це не одна з найсильніших її особливостей.

З точки зору ОО, я думаю, що C ++ насправді дещо не вистачає. Наприклад, ідея наявності невіртуальних функцій - це галочка проти цього. У мене були аргументи з тими, хто не погоджується зі мною, але невіртуючі члени просто не відповідають парадигмі, наскільки я переживаю. Поліморфізм є ключовим компонентом ОО, і класи з невіртуальними функціями не є поліморфними в сенсі ОО. Отже, як мова OO, я думаю, що C ++ насправді досить слабкий у порівнянні з мовами, такими як Java або Objective-C.

З іншого боку, загальне програмування, C ++ має це досить непогано. Я чув, що вона говорила, що для цього є і кращі мови, але поєднання об'єктів і загальних функцій - це щось досить потужне і виразне. Крім того, це може бути чорт швидко як у програмуванні, так і в часі обробки. Я думаю, що саме в цій області світить C ++, хоча, мабуть, це може бути краще (наприклад, підтримка мови для понять). Хтось, хто думає, що вони повинні дотримуватися парадигми ОО і ставитись до інших за порядком заяви гото про рівні аморальності, насправді не вистачає, не дивлячись на цю парадигму.

Можливість метапрограмування шаблонів також досить вражаюча. Ознайомтеся, наприклад, з бібліотекою Boost.Units. Ця бібліотека забезпечує підтримку типу для розмірних величин. Я широко використав цю бібліотеку в інженерній фірмі, в якій зараз працюю. Він просто забезпечує набагато більш негайний зворотний зв’язок для одного аспекту можливого програміста або навіть помилки специфікації. Неможливо скласти програму, яка використовує формулу, де обидві сторони оператора '=' не є розмірними еквівалентами без явного кастингу. Я особисто не маю досвіду роботи з будь-якою іншою мовою, на якій це можливо, і, звичайно, не з тією, яка також має потужність і швидкість C ++.

Метапрограмування - суто функціональна парадигма.

Так справді, я думаю, ви вже вступаєте в C ++ з якимись прикрими помилками. Інших парадигм, окрім ОО, не слід уникати, вони повинні РІВНЯТИ. Використовуйте природну парадигму для аспекту проблеми, над якою працюєте. Не примушуйте об’єктів до того, що по суті не є проблемою, схильною до об'єктів. Що стосується мене, OO - це навіть не половина історії для C ++.


Чи не забезпечує віртуальне ключове слово функціональність віртуального класу в C ++? Що стосується коментаря Goto, то, за чим я їхав, - це те, що я не переймаюся порушенням емпіричних правил, доки я розумію міркування. Я, однак, дивився далі на ухилення від імперативних / процедурних процедур на користь ОО, ніж це було можливо. Дякую.
Стівен

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

1

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

@nightcracker

Ну перше - гріз викриття занадто багато інформації. За замовчуванням має бути приватним, а не загальнодоступним. Після цього надходить занадто багато геттерів / сетерів.

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

Домінік Гурто

Розробіть інтерфейс, перш ніж ви навіть почнете думати про реалізацію. Дизайн та реалізація інтерфейсу Гена Бушуєва часто йдуть рука об руку в послідовних ітераціях, поки остаточний інтерфейс не кристалізується.

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

хочутьБест

Я почав повільно, з невеликими / середніми процедурними програмами, і жодних критично важливих завдань на роботі. в оригінальному процедурному коді відокремте структури даних від коду одержимого / модифікатора

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

jpm

Одне, чого ви точно не хочете робити, - це поле, яке потрібно перевірити на узгодженість мутатора, і залишити його загальнодоступним

Я деякий час знав, що це одна з сильних сторін інкапсуляції даних ... здатність дотримуватися узгодженості та з цього приводу умов / діапазонів / тощо.

Божевільний Едді

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

Я спочатку дуже багато пропустив у відповіді Божевільного Едді, я думаю, тому що я не читав жодної з згаданих тем ... як метапрограмування. Я думаю, що чудовим повідомленням у посту CE було те, що C ++ - це така суміш можливостей та стилів, що кожен повинен бути використаний для найкращого потенціалу ... включаючи імператив, якщо в цьому є сенс.

Тож ще раз дякую всім, хто відгукнувся!


0

Найбільший підводний камінь - це віра, що ООП - це срібна куля, або "одна ідеальна парадигма".

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