Чим відрізняються абстрактні типи даних від об'єктів?


11

Відповідь на Programmers.SE характеризує есе Кука ( об'єкти не АТД ) , як кажуть

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

  • ADT зазвичай мають унікальну реалізацію в програмі. Якщо в мові є модулі, можливе кілька реалізацій ADT, але вони зазвичай не можуть взаємодіяти.

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

Крім того, робота Олдріча Сила сумісності: чому об'єкти неминучі ¹ підказує

Визначення Кука по суті визначає динамічну розсилку як найважливішу характеристику об'єкта

погоджуючись з цим і з Аланом Кей, коли він сказав

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

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

Мої запитання є

  1. Чи правильно я стверджую, що характерні функції не є ключовою особливістю об’єктів і що Френк Ширар помиляється?

  2. Чи є дані, які спілкуються один з одним через інтерфейси Java, приклади об'єктів, хоча вони не використовують динамічну розсилку? Чому? (Наскільки я розумію, динамічна диспетчеризація є більш гнучкою, а інтерфейси - це крок до обміну повідомленнями у стилі об'єктивний C / smalltalk / erlang.)

  3. Чи пов'язана ідея принципу інверсії залежності з розрізненням ADT та об'єктів? (Див. Сторінку Вікіпедії або Говорячі об’єкти: казка про програмування, орієнтовану на повідомлення ) Хоча я новачок у цій концепції, я розумію, що вона передбачає додавання інтерфейсів між "шарами" програми (див. Схему сторінки вікіпедії).

  4. Будь ласка, надайте будь-які інші приклади / роз’яснення різниці між об'єктами та ADT, якщо ви хочете.

¹ Цей документ (опублікований у 2013 р.) Є легким для читання та узагальнює папір Кука 2009 року з прикладами на Java. Я настійно рекомендую принаймні знехтувати його, не відповідати на це питання, а лише тому, що це хороший папір.


5
Цікаво, але будь ласка, спробуйте обмежитися одним питанням за повідомленням.
Рафаель

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

Відповіді:


8

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. Більше інформації можна знайти тут , тут , тут і тут .

Редагування: Я знайшов дуже, дуже гарне виклад визначення Алана Кея ЛСГ тут .


3

Якщо ви подивитесь на прихильників ADT, вони вважають ADT тим, що OOP називав би класом (внутрішнім, приватним станом; дозволений обмежений набір операцій), але ніякого відношення між класами (в основному немає успадкування). Справа в тому, що однакову поведінку можна отримати в різних реалізаціях. Наприклад, набір може бути реалізований у вигляді списку, елементів у масиві чи хеш-таблиці, або якогось дерева.


2

Я завжди розумів це так:

  1. ADT - це інтерфейс: це лише сукупність методів, їх підписів на тип, можливо, з умовами до і після.

  2. Клас може реалізувати один або кілька ADT, даючи фактичні реалізації для методів, визначених у ADT.

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

Можливо, що в літературі розрізнення різні, але це "стандартна" термінологія, яку ви почуєте в інформатиці.

Наприклад, у Java Collection- ADT, ArrayListце клас, і ви можете зробити ArrayListоб'єкт з newоператором.

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


1
З точки зору функціонального програмування, чи не мають ADT певні обмеження, яких класи взагалі не мають?
Рафаель

@Raphael Як, що?
jmite

1
Це загальний вигляд ADT, і це розумне наближення. Однак, як я розумію, АДТ, як це розглядається в літературі про ПЛ та як формально визначений, насправді має дещо конкретніше значення. ADT - це специфікація такої структури даних: не про те, як вони реалізовані чи як представлені дані, а інтерфейс до нього (які види операцій можна виконувати?) Та поведінку / семантику кожної з цих операцій. Тож це не лише Java-інтерфейс (список методів з типовими підписами), а й специфікація їх поведінки.
DW

1
Наприклад, моє враження, що Collectionінтерфейс Java не є ADT. Він надає перелік методів, але не визначає їх семантику. Чи надає це семантику множини? мультисети (сумка)? упорядкований список? Це не визначено. Тому я не впевнений, що це вважається АДТ. Це моє враження, але цілком можливо, що моє розуміння могло бути неправильним ...
DW

На слайдах лекцій, до яких я пов’язаний, клас Java (навіть не інтерфейс!) Вважається ADT, оскільки клас має як приватну, так і публічну частини (я припускаю, що частина класу буде неофіційно вказана, але я не впевнений) . З іншого боку, клас, доступ до якого здійснюється через інтерфейс, вважається об'єктом, при цьому методи, визначені інтерфейсом, є "повідомленнями" (наміри високого рівня). Коли об'єкти розмовляють один з одним за допомогою намірів, різні реалізації об'єкта можуть "говорити" один з одним.
ЛМЗ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.