Які проблеми процесуального програмування вирішує ООП на практиці?


17

Я вивчив книгу "С ++ Демістифікований" . Тепер я почав читати "Об'єктно-орієнтоване програмування в першому виданні Turbo C ++ (1-е видання)" Роберта Лафора. Я не знаю жодного знання програмування, що знаходиться поза цими книгами. Ця книга може бути застарілою, оскільки їй 20 років. У мене є останнє видання, я використовую старе, тому що мені це подобається. В основному я просто вивчаю основні поняття OOP, які використовуються в C ++ через перше видання книги Лафора.

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

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

Об'єктно-орієнтоване програмування було розроблено тому, що в попередніх підходах до програмування були виявлені обмеження .... Коли програми стають все більшими і складнішими, навіть структурований підхід до програмування починає проявляти ознаки напруги ... .... Аналіз причин ці невдачі показують, що в самій процедурній парадигмі є слабкі місця. Незалежно від того, наскільки вдало реалізовано структурований підхід до програмування, великі програми стають надмірно складними .... ... Існують дві пов'язані з цим проблеми. По-перше, функції мають необмежений доступ до глобальних даних. По-друге, незв’язані функції та дані, що є основою процедурної парадигми, забезпечують бідну модель реального світу ...

Я вивчив книгу "дистиміфікований C ++" Джеффа Кента, мені ця книга дуже подобається, в цій книзі пояснюється переважно процедурне програмування. Я не розумію, чому процедурне (структуроване) програмування слабке!

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

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

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

Отже, ось моє питання:

На які обмеження процедурного програмування звертається ООП і як це ефективно усуває ці обмеження на практиці?

Зокрема, чи є приклади для програм, які складно спроектувати, використовуючи процедурну парадигму, але їх легко створити за допомогою OOP?

PS: Хрест розміщено з: /programming//q/22510004/3429430


3
Різниця між процедурним програмуванням та об'єктно-орієнтованим програмуванням певною мірою є предметом подання та акценту. Більшість мов, які рекламують себе як об'єктно-орієнтовані, також є процедурними - терміни розглядають різні аспекти мови.
Жил "ТАК - перестань бути злим"

2
Я знову відкрив це питання; подивимось, як це йде. Майте на увазі, що будь-яке лікування, яке представляє ООП, - це святий грааль, який вирішує всі проблеми месії мов програмування - це розсипання дурниць. Є плюси і мінуси. Ви запитуєте плюси, і це справедливе питання; не чекайте більше.
Рафаель

Думка створити (сучасний) графічний інтерфейс настільних ПК без OOP та деякі прийоми, які виросли на ньому (наприклад, шаблон подій / слухачів) мене лякає. Це не означає, що це неможливо зробити (це, безумовно, може ), хоча. Крім того, якщо ви хочете побачити збій ПП на роботі, подивіться PHP і порівняйте API з, скажімо, Ruby.
Рафаель

1
"Незалежно від того, наскільки вдало реалізовано структурований підхід до програмування, великі програми стають надмірно складними ..." Це дуже стосується також ООП, але в основному це краще керувати складністю, якщо застосовувати розробниками встановлений спосіб ... і чи це значною мірою через кращі / чіткіші рамки / обмеження сфери, тобто свого роду систему компартменталізації ... ака APIE: абстракція, поліморфізм, спадкування, інкапсуляція
vzn

Відповіді:


9

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

Оголошення типів є найбільш очевидним декларативним обмеженням (тобто: "довести, що x - плаваючий потік"). Інше примушування мутацій даних проходити через функцію, яка, як відомо, призначена для цих даних. Застосування протоколу (замовлення виклику методу) - ще одне частково підтримуване обмеження, тобто: "конструктор -> іншіметоди * -> деструктор".

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

тип x1 - підтип x, t1 - підтип t

Це один із способів інкапсуляції даних процедурною мовою, щоб мати тип t методами f і g та підклас t1, який робить так само:

t_f (t, x, y, z, ...), t_g (t, x, y, ...) t1_f (t1, x, y, z, ...)

Щоб використовувати цей код таким, яким він є, вам потрібно зробити перевірку типу та увімкнути тип t, перш ніж вирішити тип f, який ви будете викликати. Ви могли б обходити це так:

тип t {d: дані f: функція g: функція}

Таким чином, ви замість цього запускаєте tf (x, y, z), де перевірка набору тексту та перемикач на пошук методу замінюється лише явним вказівником методу зберігання кожного примірника. Тепер, якщо у вас є величезна кількість функцій для кожного типу, це марне уявлення. Тоді ви могли б використовувати іншу стратегію, як, наприклад, t вказувати на змінну m, яка містить усі функції-учасниці. Якщо ця функція є частиною мови, тоді ви можете дозволити компілятору зрозуміти, як впоратися з ефективним поданням цього шаблону.

Але інкапсуляція даних - це визнання того, що стан, що змінюється, поганий. Об'єктно-орієнтоване рішення - приховати його за методами. В ідеалі всі методи в об'єкті мали б чітко визначений порядок виклику (тобто: конструктор -> відкрити -> [читати | написати] -> закрити -> знищити); який іноді називають "протоколом" (дослідження: "Сингулярність Microsoft"). Але, крім побудови та руйнування, ці вимоги, як правило, не є частиною типової системи - або добре задокументованою. У цьому сенсі об'єкти є паралельними екземплярами державних машин, які переходять за допомогою викликів методів; таким чином, що у вас може бути кілька примірників і використовувати їх довільно переплетеними способами.

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

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

Необмежена орієнтація на об'єкт у поєднанні з паралелізмом є проблемою через блокування. Існують методи, починаючи від програмної оперативної пам'яті (тобто трансакції ACID на об'єкті пам’яті, подібної до баз даних) до використання підходу «обмін пам’яттю шляхом передачі (незмінних даних)» (функціональний гібрид програмування).

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


Ви говорите, що в мовах ООС компілятор може перевірити, чи використовуються методи у встановленому порядку, чи інші обмеження щодо використання модулів? Чому "інкапсуляція даних [...] визнає, що стан, що змінюється, поганий"? Якщо ви говорите про поліморфізм, чи вважаєте ви, що ви використовуєте мову ОО?
бабу

В OO найважливіша здатність полягає в тому, щоб вміти приховати структуру даних (тобто: вимагати, щоб посилання були з подібного this.x), щоб довести, що всі звернення проходять через методи. Статично набраною мовою OO ви заявляєте ще більше обмежень (на основі типів). У примітці про впорядкування методу йдеться лише про те, що OO змушує конструктора викликати перший, а деструктор останній; що є першим кроком у структурі заборони поганих замовлень. Легкі докази - важлива мета в мовному дизайні.
Роб

8

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

Деякі парадигми програмування розвиваються з теоретичного бачення обчислення. Мова на зразок Ліспа розвинулася з лямбда-числення та ідеї метациркуляції мов (подібно до рефлексивності в природній мові). Запропоновані ріжками пропонують програмування Prolog та обмеження. Сімейство Алгол також завдячує лямбда-числення, але без вбудованої рефлексивності.

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

Однак мови тоді розвиваються, часто під новими назвами. Основним фактором еволюції є практика програмування. Користувачі визначають практику програмування, що покращує властивості програм, такі як читабельність, ремонтопридатність, доказовість правильності. Потім вони намагаються додати до мов функції або обмеження, які підтримуватимуть, а іноді і застосовувати ці практики, щоб покращити якість програм.

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

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

Приклад: відтворення орієнтації об'єкта

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

Як я вже говорив, Лісп був зразком еволюції мови, включаючи парадигму ОО (хоча першою мовою ОО можна вважати Симула 67, в родині Алгол). Lisp дуже простий, а код для його основного перекладача - менше сторінки. Але ви можете зайнятися програмуванням OO в Lisp. Все, що вам потрібно, - це функції вищого порядку.

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

Припустимо, я хочу створити клас під назвою вектор, що представляє двовимірні вектори, методами, що включають: додавання вектора, розмір вектора та паралелізм.

function vectorrec () {  
  function createrec(x,y) { return [x,y] }  
  function xcoordrec(v) { return v[0] }  
  function ycoordrec(v) { return v[1] }  
  function plusrec (u,v) { return [u[0]+v[0], u[1]+v[1]] }  
  function sizerec(v) { return sqrt(v[0]*v[0]+v[1]*v[1]) }  
  function parallelrec(u,v) { return u[0]*v[1]==u[1]*v[0]] }  
  return [createrec, xcoordrec, ycoordrec, plusrec, sizerec, parallelrec]  
  }  

Тоді я можу призначити створений вектор фактичним іменам функцій, які будуть використовуватися.

[вектор, xcoord, ycoord, vplus, vsize, vparallel] = vectorclass ()

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

Ми можемо зробити ще одну колекцію за полярними координатами

function vectorpol () {  
  ...  
  function pluspol (u,v) { ... }  
  function sizepol (v) { return v[0] }  
  ...  
  return [createpol, xcoordpol, ycoordpol, pluspol, sizepol, parallelpol]  
  }  

Але я, можливо, хочу байдуже використовувати обидві реалізації. Один із способів зробити це - додати компонент типу до всіх значень і визначити всі вищевказані функції в одному середовищі: Потім я можу визначити кожну з повернутих функцій, щоб вона спочатку перевірила тип координат, а потім застосувала конкретну функцію для нього.

function vector () {  
    ...  
    function plusrec (u,v) { ... }  
    ...  
    function pluspol (u,v) { ... }  
    ...  
    function plus (u,v) { if u[2]='rec' and v[2]='rec'  
                            then return plusrec (u,v) ... }  

    return [ ..., plus, ...]  
    }

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

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

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

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

Я зупинюсь тут, на цій реконструкції об’єктно-орієнтованого об’єкта.

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

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

Але думка про те, що мови, які не мають функцій ОО, в деяких проектах приречені на збій, просто необґрунтована. За потреби вони можуть досить імітувати реалізацію цих функцій. Багато мов мають синтаксичну та семантичну силу досить ефективно робити орієнтацію на об'єкти, навіть коли вона не вбудована. І це більше, ніж аргумент Тьюрінга.

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

Я вважаю, що гарною книгою для розуміння всього цього може стати Abelson & Sussman: структура та інтерпретація комп'ютерних програм .


8

Трохи історії в порядку, я думаю.

Епоха з середини 1960-х до середини 1970-х років сьогодні відома як "криза програмного забезпечення". Я не можу сказати про це краще за Дійкстра в його лекції про нагороду Тюрінга 1972 року:

Головною причиною кризи програмного забезпечення є те, що машини стали на кілька порядків потужнішими! Якщо говорити прямо, то, доки не було машин, програмування взагалі не було жодних проблем; коли у нас було кілька слабких комп’ютерів, програмування стало легкою проблемою, а тепер у нас є гігантські комп’ютери, програмування стало настільки ж гігантською проблемою.

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

Не дивно, що це був надзвичайно родючий час у дослідженні програмування. До середини 1960-х у нас були LISP та AP / L, але "основні" мови були принципово процедурними: FORTRAN, ALGOL, COBOL, PL / I тощо. З середини 1960-х до середини 1970-х ми отримали логотип, Pascal, C, Forth, Smalltalk, Prolog, ML та Modula, і це не рахує таких DSL, як SQL та його попередники.

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

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

Коли Алан Кей ввів термін "об'єктно-орієнтований", він мав на увазі, що програмні системи будуть структуровані як біологічна система. У вас вийде щось на зразок окремих комірок ("об'єктів"), які взаємодіють між собою, посилаючи щось аналогічне хімічним сигналам ("повідомленням"). Ви не могли (або, принаймні, не хотіли) заглянути всередину клітини; Ви б спілкувалися з нею лише через сигнальні шляхи. Більше того, у вас може бути більше ніж один із клітин кожного типу, якщо вам потрібно.

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

Одне, що ви можете зауважити, відсутнє, і це спадщина, і в цьому є причина.

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

Спадщина - це конкретна особливість мови програмування. Спадкування може бути корисним для впровадження систем OOP. Або, принаймні, так було до початку 1990-х.

Час від середини 1980-х до середини 1990-х років також був часом в історії, коли все змінювалося. За цей час у нас виникло дешеве, всюдисущий 32-розрядний комп'ютер, тому підприємства та багато будинків могли дозволити собі поставити на кожному столі робочий стіл, який був майже таким же потужним, як найнижчі мейнфрейми дня. Це також був розквіт цього. Це була також епоха підйому сучасного GUI та мережевої операційної системи.

Саме в цьому контексті виникли об'єктно-орієнтований аналіз та дизайн.

Вплив OOAD, роботи "трьох Амігос" (Буха, Румбара та Якобсона) та інших (наприклад, метод Шлаера – Меллора, дизайн, орієнтований на відповідальність тощо), не можна применшити. Це причина, чому більшість нових мов, розроблених з початку 1990-х (принаймні, більшість з них, про які ви чули) мають об'єкти в стилі симула.

Тож відповідь на ваше запитання 1990-х років полягає в тому, що він підтримує найкраще (на той час) рішення для доменно-орієнтованого аналізу та методології проектування.

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

Сучасні графічні інтерфейси та будь-які операційні системи, розроблені за останні 20 років, мають тенденцію надавати свої послуги як об'єкти, тому будь-яка нова мова практичного програмування потребує, принаймні, способу прив’язання до систем, якими ми користуємось сьогодні.

Отже, сучасна відповідь така: Це вирішує проблему взаємодії з сучасним світом. Сучасний світ побудований на ООП з тієї ж причини, що світ 1880 року був побудований на пару: ми його розуміємо, можемо контролювати, і він робить цю роботу досить добре.

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


Однією стороною, яку я не хотів ставити в основному нарисі, є те, що графічні інтерфейси WIMP та OOP видаються надзвичайно природними. Багато поганих речей можна сказати про ієрархії глибокого спадкування, але це одна ситуація (можливо, ТОЛЬКО ситуація), коли це, здається, має певний сенс.
Псевдонім

1
OOP з'явився спочатку в Simula-67 (моделювання), у внутрішній організації операційних систем (ідея "класу пристроїв" в Unix по суті є класом, від якого успадковуються драйвери). Парнас "Про критерії, які слід використовувати при розкладанні систем на модулі" , CACM 15:12 (1972), стор. 1052-1058, мова модуля Вірта з сімдесятих років, "абстрактні типи даних" - це всі попередники так чи інакше. інший.
vonbrand

Це все правда, але я вважаю, що ООП не розглядався як "рішення проблеми процедурного програмування" до середини 70-х. Визначити "OOP", як відомо, важко. Первісне використання терміна Алана Кей не узгоджується з моделлю Simula, і, на жаль, світ стандартизував модель Simula. Деякі об'єктні моделі мають тлумачення, подібне до Каррі-Говарда, але Симула - ні. Мабуть, Степанов мав рацію, коли зазначив, що спадщина не є голосною.
Псевдонім

6

Ні, насправді. OOP насправді не вирішує проблему, строго кажучи; нічого не можна зробити з об'єктно-орієнтованою системою, чого не можна було б зробити з не об’єктно-орієнтованою системою - дійсно, нічого, що можна зробити з тим, що не можна було б зробити з машиною Тьюрінга. З часом все це перетворюється на машинний код, і ASM, безумовно, не орієнтована на об'єкти.

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

Скажіть, я хочу написати карткову гру в Python. Як би я представляв картки?

Якби я не знав про ООП, я міг би зробити це так:

cards=["1S","2S","3S","4S","5S","6S","7S","8S","9S","10S","JS","QS","KS","1H","2H",...,"10C","JC","QC","KC"]

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

Тепер, коли я хочу перетасувати колоду, мені потрібно лише "перетасувати" список. Потім, щоб зняти карту з верхньої частини колоди, я спливу верхній запис зі списку, даючи мені рядок. Простий.

Тепер, якщо я хочу розібратися, з якою карткою я працюю, щоб відобразити її гравцеві, мені потрібна така функція:

def card_code_to_name(code):
    suit=code[1]

    if suit=="S":
        suit="Spades"
    elif suit=="H"
        suit="Hearts"
    elif suit=="D"
        suit="Diamonds"
    elif suit=="C"
        suit="Clubs"

    value=code[0]

    if value=="J":
        value="Jack"
    elif value="Q":
        value="Queen"
    elif value="K"
        value="King"

    return value+" of "+suit

Трохи великий, довгий і неефективний, але він працює (і дуже непітонічно, але це тут не до речі).

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

cardpositions=( (1,1), (2,1), (3,1) ...)

Потім я записую свій код, щоб індекс позиції кожної картки у списку був таким самим, як індекс самої картки в колоді.

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

А що, якщо я хочу зберігати ще більше інформації про картки? Що робити, якщо я хочу зберігати, чи перевернута кожна карта? Що робити, якщо я хочу якийсь двигун фізики, і мені також потрібно знати швидкість карт? Мені знадобиться інший список повністю для зберігання графіки для кожної карти! І для всіх цих точок даних мені знадобиться окремий код, щоб вони були правильно організовані, щоб кожна картка якось відповідала всім своїм даним!

Тепер спробуємо це ООП способом.

Замість списку кодів давайте визначимо клас Картки та створимо з нього список об’єктів Картки.

class Card:

    def __init__(self,value,suit,pos,sprite,flipped=False):
        self.value=value
        self.suit=suit
        self.pos=pos
        self.sprite=sprite
        self.flipped=flipped

    def __str__(self):
        return self.value+" of "+self.suit

    def flip(self):
        if self.flipped:
            self.flipped=False
            self.sprite=load_card_sprite(value, suit)
        else:
            self.flipped=True
            self.sprite=load_card_back_sprite()

deck=[]
for suit in ("Spades","Hearts","Diamonds","Clubs"):
    for value in ("1","2","3","4","5","6","7","8","9","10","Jack","Queen","King"):
        sprite=load_card_sprite(value, suit)
        thecard=Card(value,suit,(0,0),sprite)
        deck.append(thecard)

Зараз раптом все набагато простіше. Якщо я хочу перемістити карту, мені не потрібно з'ясовувати, де вона знаходиться в колоді, а потім скористайтеся нею, щоб вивести її положення з масиву позицій. Я просто повинен сказати thecard.pos=newpos. Коли я виймаю картку з основного списку колоди, мені не доведеться складати новий список, щоб зберігати всі інші дані; коли об’єкт картки рухається, разом з ним переміщуються всі його властивості. І якщо я хочу, щоб картка поводилася інакше, коли її перевертали, мені не потрібно змінювати функцію гортання в моєму головному коді, щоб вона виявляла ці картки та робила таку іншу поведінку; Мені просто потрібно підклас Card і змінити функцію flip () на підкласі.

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

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


1
Якщо єдине, що ви забираєте у OOP, це "управління пам'яттю", то я не думаю, що ви це дуже добре зрозуміли. Там є ціла філософія дизайну та щедра пляшка "правильного дизайну"! Також управління пам'яттю, безумовно, ніщо не властиве орієнтації на об'єкти (C ++?), Навіть якщо потреба в ньому стає більш вираженою.
Рафаель

Звичайно, але це односкладний варіант. І я вжив цей термін досить нестандартно. Можливо, було б краще сказати "обробка інформації", ніж "управління пам'яттю".
Шилькоте

Чи існують мови, що не належать до OOP, які дозволяли б функції взяти вказівник на щось, а також вказівник на функцію, першим параметром якої є вказівник на цю саму річ, і компілятор підтвердить, що функція відповідна для переданого вказівника?
supercat

3

Написавши вбудований C протягом декількох років, керуючи такими пристроями, як послідовні порти та комунікаційні пакети між послідовними портами, мережевими портами та серверами; Я виявив себе кваліфікованим інженером-електриком з обмеженим досвідом програмування в програмі, що вигадував власні абстракції від обладнання, яке виявилося в тому, що я пізніше зрозумів, - це те, що звичайні люди називають "Об'єктно-орієнтоване програмування".

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

Я особисто вважаю, що це єдиний спосіб навчити орієнтації на об'єкти. У мого першокурсника я мав клас Intro to OOP (Java), і це було повністю над моєю головою. Описи ООП, побудовані на класифікації кошеня-> кота-> ссавця-> жива-> річ чи листочок-> гілка-> дерево-> сад, на мою скромну думку, абсолютно смішні методики, тому що ніхто не збирається намагатися їх вирішити. проблеми, якщо ви навіть можете назвати їх проблемами ...

Я думаю, що простіше відповісти на ваше запитання, якщо ви подивитесь на це менш абсолютними поняттями - не "що це вирішує", а більше з точки зору "ось проблема, і ось як це полегшити". У моєму конкретному випадку послідовних портів у нас була купа #ifdefs часу компіляції, яка додавала та видаляла код, який статично відкривав і закривав послідовні порти. Функції відкритих портів викликали всюди, і вони могли розташовуватися в будь-якому місці в 100-ти рядкових кодах ОС, які ми мали, і IDE не сіріло, що не було визначено - вам довелося відстежувати це вручну, і несіть це в голові. Неминуче у вас може бути кілька завдань, намагаючись відкрити заданий послідовний порт, очікуючи їх пристрою з іншого кінця, і тоді жоден код, який ви тільки що написали, не працює, і ви не можете зрозуміти, чому.

Хоча абстракція, хоч і досі знаходиться в С, "клас" послідовного порту (ну просто тип даних структури), у нас був масив - по одному для кожного послідовного порту - і замість того, щоб [еквівалент DMA в послідовних портах] Функції "OpenSerialPortA" "SetBaudRate" тощо, викликані безпосередньо на апаратному забезпеченні, ми викликали функцію помічника, якій ви передали всі параметри зв'язку (бод, паритет тощо), які спочатку перевірили масив структури, щоб побачити, чи це порт вже був відкритий-- якщо так, то яке завдання, яке б воно сказало вам як налагодження printf, щоб ви могли негайно перейти до розділу коду, який потрібно було вимкнути - а якщо ні, то він перейшов до встановлення всі параметри через їх функції складання HAL, і нарешті відкрив порт.

Звичайно, небезпеки для ООП теж є. Коли я, нарешті, очистив цю кодову базу і зробив усе акуратним та чистим - написання нових драйверів для цієї лінійки продуктів, нарешті, було обчислюваною, передбачуваною наукою. управляти, і він був прикордонним знімним середнім керівництвом. Що дуже багато у мене забрало / я виявив дуже відлякуючим, тому я кинув роботу. ЛОЛ.


1
Привіт! Це здається скоріше особистою історією, ніж відповіддю на запитання. З вашого прикладу я бачу, що ви переписали якийсь жахливий код в об'єктно-орієнтованому стилі, що покращило його. Але незрозуміло, чи поліпшення мало багато спільного з орієнтацією на об'єкти чи це було лише тому, що ви тоді були більш досвідченим програмістом. Наприклад, велика частина вашої проблеми, схоже, пов’язана з тим, що код розкиданий мимоволі-неволі навколо місця. Це можна було б вирішити, написавши процедурну бібліотеку, без об’єктів.
Девід Річербі

2
@DavidRicherby у нас була процедурна бібліотека, але це те, що ми застаріли, і справа не в тому, що код був повсюдно. Справа в тому, що ми це робили назад. Ніхто нічого не намагався OOP, це сталося природно.
paIncrease

@DavidRicherby Ви можете навести будь-які приклади впровадження процедурної бібліотеки, щоб я міг переконатися, що ми говоримо про те саме?
paIncrease

2
Дякуємо за вашу відповідь та +1. Давно інший досвідчений програміст поділився тим, як OOP зробив свій проект надійнішим forums.devshed.com/programming-42/… Я думаю, що OOP був розроблений дуже розумно деякими професіоналами, які могли зіткнутися з певними проблемами в процедурному підході.
user31782

2

Є багато претензій і намірів щодо того, що / де програмування OOP має перевагу перед процедурним програмуванням, у тому числі його винахідниками та користувачами. але лише тому, що технологія, яку розробляли дизайнери з певною метою, не гарантує успіху в цих цілях. це ключове розуміння в галузі інженерії програмного забезпечення, що дається відомим нарисом Брукса "Без срібної кулі", яке все ще актуально, незважаючи на революцію кодування OOP. (див. також Gypener Hype Cycle щодо нових технологій.)

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

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

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

  • Порівняння розуміння програмістами-початківцями об'єктно-орієнтованих та процедурних програм взаємодіють з комп'ютерами. Сьюзен Віденбек, Венніла Рамалінгам, Сусеела Сарасама, Сінтія Л Корріторі (1999)

    У цій статті представлені два експерименти, що порівнюють уявні уявлення та розуміння програми новачками в об'єктно-орієнтованому та процедурному стилях. Випробувані були початківцями програмістами, які записалися на другий курс програмування, який викладав або об'єктно-орієнтовану, або процедурну парадигму. Перший експеримент порівнював ментальні уявлення та розуміння коротких програм, написаних у процедурному та об’єктно-орієнтованому стилях. Другий експеримент розширив дослідження на більш широку програму, що включає більш вдосконалені мовні функції. У коротких програмах не було суттєвої різниці між двома групами щодо загальної кількості відповідей правильно, але об'єктно-орієнтовані суб'єкти переважали над процесуальними суб'єктами, відповідаючи на запитання про функцію програми. Це говорить про те, що інформація про функції була більш доступною у їхніх розумових уявленнях про програми і підтримує аргумент, що об'єктно-орієнтована нотація виділяє функціонування на рівні індивідуального класу. Для тривалої програми відповідного ефекту не знайдено. Розуміння процедурних суб'єктів було вищим для об'єктно-орієнтованих суб'єктів у всіх типах питань. Труднощі, з якими стикаються об'єктно-орієнтовані суб'єкти у відповіді на запитання у більшій програмі, говорять про те, що вони зіткнулися з проблемами в маршируванні інформації та виведенні з неї висновків. Ми припускаємо, що цей результат може бути пов'язаний з більш тривалою кривою навчання для новачків об'єктно-орієнтованого стилю, а також з особливостями стилю OO та конкретними позначеннями мови OO.

Дивись також:


1
Я поважаю експериментальні дослідження. Однак існує питання про те, щоб вони вирішили правильні питання. Занадто багато змінних у тому, що можна назвати OOP, і в способах його використання, щоб одне дослідження було осмисленим, imho. Як і багато речей у програмуванні, OOP був створений експертами для задоволення власних потреб . Обговорюючи корисність ООП (що я не сприймав як тему ОП, а це, скоріше, стосується недоліку процедурного програмування), можна запитати: яка функція, для кого, з якою метою? Тоді лише польові дослідження набувають повного значення.
бабу

1
Попередження про анекдот: Якщо проблема невелика (наприклад, приблизно до 500-1000 рядків коду), OOP не має жодних змін у моєму досвіді, це може навіть перешкодити тому, що потрібно турбуватися про речі, які мають дуже незначну різницю. Якщо проблема велика і має певну форму "взаємозамінних фрагментів", яку, крім того, потрібно буде додати пізніше (вікна в графічному інтерфейсі, пристрої в операційній системі, ...), що забезпечує організація OOP дисципліни неминуча. Ви, звичайно, можете програмувати OOP без підтримки мови (див., Наприклад, ядро ​​Linux).
vonbrand

1

Будь обережний. Прочитайте класику Р. Кінга "Мій кіт об'єктно-орієнтований" у "Об'єктно-орієнтованих концепціях, базах даних та додатках" (Кім та Лоховський, редактори) (ACM, 1989). "Об'єктно-орієнтована" стала скоріше гучним словом, ніж поняттям чіткого вирізу.

Крім того, існує багато варіацій на тему, мало спільного. Існують мови на основі прототипу (успадкування - від об'єктів, класів як таких немає) та мов на основі класів. Є мови, які дозволяють отримати багато успадкованих, інші - не. Деякі мови мають таке поняття, як інтерфейси Java (їх можна прийняти як форму, що знижує багато разів успадкування). Існує ідея міксинів. Спадкування може бути досить суворим (як у C ++, не може насправді змінити те, що ви отримуєте в підкласі), або дуже ліберально (в Perl підклас може переосмислити майже все). Деякі мови мають єдиний корінь для успадкування (типово називається "Об'єкт" із поведінкою за замовчуванням), інші дозволяють програмісту створювати кілька дерев. Деякі мови наполягають на тому, що "все є об'єктом", інші обробляють об'єкти та не об'єкти, деякі (як Java) мають "більшість об'єктів, але цих кількох типів тут немає". Деякі мови наполягають на суворій інкапсуляції стану об'єктів, інші роблять це необов'язковим (C ++ 'приватним, захищеним, загальнодоступним), третім взагалі немає інкапсуляції. Якщо ви косієте до такої мови, як Scheme, під прямим кутом, ви бачите OOP, вбудований без особливих зусиль (можна визначити функції, які повертають функції, які інкапсулюють деякий локальний стан).


0

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


Які проблеми безпеки даних є в процедурному програмуванні?
користувач31782

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

У процедурному програмуванні ми також можемо обмежити використання глобальної змінної, використовуючи змінну певними функціями, тобто не оголошуючи жодних даних глобальними.
user31782

Якщо ви не декларуєте це глобально, це не глобальна змінна.
март

1
"Безпечний" або "Правильний" нічого не означають без специфікації. Ці речі - це спроби поставити специфікацію коду для досягнення цієї мети: типи, визначення класів, DesignByContract тощо. Ви отримуєте "Безпеку" в тому сенсі, що Ви можете зробити межі приватних даних непорушними; припускаючи, що ви повинні виконувати набір інструкцій віртуальної машини для виконання. Об'єктна орієнтація не приховуватиме внутрішню пам'ять від когось, хто може читати пам'ять безпосередньо, а поганий дизайн протоколу об'єкта видає секрети за задумом.
Роб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.