Google поставив подібне запитання з відповіддю, яке, на мою думку, дуже добре. Я процитував це нижче.
Тут ховається ще одна відмінність, яка пояснюється в есеї Кука, з яким я пов'язаний.
Об'єкти - не єдиний спосіб здійснити абстракцію. Не все є об’єктом. Об'єкти реалізують те, що деякі називають процедурним абстрагуванням даних. Абстрактні типи даних реалізують іншу форму абстрагування.
Ключова відмінність з’являється при розгляді двійкових методів / функцій. За допомогою процедурної абстракції даних (об'єктів) ви можете написати щось подібне для інтерфейсу Int:
interface IntSet {
void unionWith(IntSet s);
...
}
Тепер розглянемо дві реалізації IntSet, скажімо, одну, яку підтримують списки, та одну, яку підтримує більш ефективна структура бінарного дерева:
class ListIntSet implements IntSet {
void unionWith(IntSet s){ ... }
}
class BSTIntSet implements IntSet {
void unionWith(IntSet s){ ... }
}
Зауважте, що unionWith повинен взяти аргумент IntSet. Не більш конкретний тип, як ListIntSet або BSTIntSet. Це означає, що реалізація BSTIntSet не може вважати, що її вхід є BSTIntSet, і використовувати цей факт для ефективної реалізації. (Він може використати деяку інформацію про тип часу запуску, щоб перевірити її і використовувати більш ефективний алгоритм, якщо він є, але він все-таки може бути переданий ListIntSet і повинен повернутися до менш ефективного алгоритму).
Порівняйте це з ADT, де ви можете написати щось подібне до наступного у файлі підпису чи заголовка:
typedef struct IntSetStruct *IntSetType;
void union(IntSetType s1, IntSetType s2);
Ми програмуємо проти цього інтерфейсу. Зокрема, тип залишається абстрактним. Ви не знаєте, що це таке. Тоді у нас є реалізація BST, тоді передбачено конкретний тип та операції:
struct IntSetStruct {
int value;
struct IntSetStruct* left;
struct IntSetStruct* right;
}
void union(IntSetType s1, IntSetType s2){ ... }
Тепер об'єднання насправді знає конкретні уявлення як s1, так і s2, тому може використовувати це для ефективної реалізації. Ми також можемо написати підтримку списку та вибрати замість цього посилання.
Я написав синтаксис C (ish), але ви повинні подивитися, наприклад, Standard ML для абстрактних типів даних, виконаних належним чином (де ви можете, наприклад, реально використовувати більше ніж одну реалізацію ADT в тій же програмі, грубо визначивши типи: BSTImpl. IntSetStruct і ListImpl.IntSetStruct, скажімо)
Зворотним моментом є те, що процедурна абстракція даних (об'єктів) дозволяє легко запроваджувати нові реалізації, які працюють із вашими старими. наприклад, ви можете написати власну користувальницьку реалізацію LoggingIntSet та об'єднати її з BSTIntSet. Але це компроміс: ви втрачаєте інформативні типи для бінарних методів! Часто в інтерфейсі виникає необхідність розкрити більше функціональних можливостей та деталей про реалізацію, ніж у випадку реалізації ADT. Тепер я відчуваю, що я лише переробляю твір Кука, так що справді читайте його!
Я хотів би додати приклад до цього.
Кук припускає, що прикладом абстрактного типу даних є модуль на C. Дійсно, модулі на C передбачають приховування інформації, оскільки є загальнодоступні функції, які експортуються через файл заголовка, і статичні (приватні) функції, які не виконують. Крім того, часто є конструктори (наприклад, list_new ()) та спостерігачі (наприклад, list_getListHead ()).
Ключовим моментом того, що робить, скажімо, модуль списку під назвою LIST_MODULE_SINGLY_LINKED ADT полягає в тому, що функції модуля (наприклад, list_getListHead ()) передбачають, що введені дані були створені конструктором LIST_MODULE_SINGLY_LINKED, на відміну від будь-якого "еквівалента "реалізація списку (наприклад, LIST_MODULE_DYNAMIC_ARRAY). Це означає, що функції LIST_MODULE_SINGLY_LINKED можуть припускати при їх реалізації певне представлення (наприклад, окремо пов'язаний список).
LIST_MODULE_SINGLY_LINKED не може взаємодіяти з LIST_MODULE_DYNAMIC_ARRAY, тому що ми не можемо подавати створені дані, скажімо, з конструктором LIST_MODULE_DYNAMIC_ARRAY, спостерігачеві LIST_MODULE_SINGLY_LINKED, тому що подається в якості списку, що подається як лістинг, який подається в якості списку.
Це аналогічно тому, що дві різні групи з абстрактної алгебри не можуть взаємодіяти (тобто ви не можете прийняти добуток елемента однієї групи з елементом іншої групи). Це відбувається тому, що групи беруть на себе властивість закриття групи (добуток елементів у групі повинен бути у групі). Однак, якщо ми можемо довести, що дві різні групи насправді є підгрупами іншої групи G, тоді ми можемо використовувати добуток G, щоб додати два елементи, по одному від кожної з двох груп.
Порівняння ADT та об'єктів
Кухар пов'язано різницю між ADT та об'єктами частково до проблеми вираження. Грубо кажучи, ADT поєднуються із загальними функціями, які часто реалізуються у функціональних мовах програмування, тоді як об'єкти поєднуються з Java-об'єктами, доступ до яких здійснюється через інтерфейси. Для цілей цього тексту загальна функція - це функція, яка бере в деяких аргументах ARGS і тип TYPE (попередня умова); на основі TYPE він вибирає відповідну функцію та оцінює її за допомогою ARGS (після умови). І родові функції, і об'єкти реалізують поліморфізм, але із загальними функціями програміст ЗНАЄ, функція якого буде виконуватися загальною функцією, не дивлячись на код родової функції. З об'єктами з іншого боку, програміст не знає, як об’єкт буде обробляти аргументи, якщо програмісти не дивляться на код об'єкта.
Зазвичай проблема вираження продумується в термінах "у мене багато уявлень?" vs. "чи маю я багато функцій з невеликим представленням". У першому випадку слід організувати код за поданням (як це найчастіше, особливо на Java). У другому випадку слід організувати код за функціями (тобто мати єдину загальну функцію, обробляти декілька представлень).
Якщо ви організовуєте свій код за поданням, то, якщо ви хочете додати додаткову функціональність, ви змушені додавати функціональність до кожного представлення об'єкта; у цьому сенсі додавання функціональності не є "добавкою". Якщо ви впорядковуєте свій код за функціональністю, тоді, якщо ви хочете додати додаткове представлення - ви змушені додавати представлення до кожного об'єкта; у цьому сенсі додавання уявлень у не "добавці".
Перевага ADT над об'єктами
Додавання функціональності є адитивним
Можливо використовувати знання про представлення ADT для продуктивності або довести, що ADT гарантуватиме якусь постічну умову з урахуванням передумови. Це означає, що програмування за допомогою ADT полягає в тому, щоб робити правильні речі в правильному порядку (з’єднувати разом попередні умови та умови для досягнення поставленої умови "цілі").
Переваги об’єктів над ADT
Додавання подань у добавці
Об'єкти можуть взаємодіяти
Можна вказати умови до / після публікації об’єкта та з'єднати їх разом, як це стосується ADT. У цьому випадку переваги об’єктів полягають у тому, що (1) легко змінювати уявлення без зміни інтерфейсу та (2) об'єкти можуть взаємодіяти. Однак це перемагає мету ООП у розумінні малої розмови. (див. розділ "Версія OOP Alan Kay")
Динамічна відправка є ключовою для OOP
Зараз має бути очевидним, що динамічна відправка (тобто пізня прив'язка) є важливою для об'єктно-орієнтованого програмування. Це так, що можна визначити процедури загальним способом, що не передбачає конкретного подання. Якщо бути конкретним - об'єктно-орієнтоване програмування легко в python, оскільки можна запрограмувати методи об'єкта таким чином, що не передбачає конкретного подання. Ось чому python не потребує інтерфейсів, таких як Java.
У Java класи - ADT. однак клас, доступ до якого здійснюється через інтерфейс, який він реалізує, є об'єктом.
Додаток: версія OOP Alan Kay
Алан Кей прямо називав об'єкти "сімействами алгебр", а Кук припускає, що ADT є алгеброю. Отже, Кей, ймовірно, означав, що об'єктом є сім'я ADT. Тобто об’єктом є сукупність усіх класів, які задовольняють інтерфейс Java.
Однак зображення предметів, намальованих Куком, є набагато обмежуючим, ніж бачення Алана Кей. Він хотів, щоб предмети поводилися як комп'ютери в мережі або як біологічні клітини. Ідея полягала в застосуванні принципу найменшої прихильності до програмування - щоб легко змінювати шари низького рівня ADT, як тільки шари високого рівня були побудовані з їх допомогою. Маючи на увазі цю картину, інтерфейси Java є занадто обмежуючими, оскільки вони не дозволяють об'єкту інтерпретувати значення повідомлення або навіть повністю ігнорувати його.
Підводячи підсумок, ключова ідея об'єктів, для Кей, - не те, що вони є сімейством алгебр (як наголошує Кук). Скоріше, ключовою ідеєю Кей було застосувати модель, яка працювала у великому (комп'ютери в мережі) до малого (об'єкти в програмі).
редагувати: Ще одне уточнення щодо версії OOP від Кей: Мета об'єктів - наблизитися до декларативного ідеалу. Ми повинні сказати об’єкту, що робити - не розповісти, як мікроменеджмент є станом, як це прийнято в процедурному програмуванні та ADT. Більше інформації можна знайти тут , тут , тут і тут .
Редагування: Я знайшов дуже, дуже гарне виклад визначення Алана Кея ЛСГ тут .