"Використовуйте карту замість класу для представлення даних" -Річ Хікі


19

У цьому відео Річ Хікі , творець Clojure, він радить використовувати карту для представлення даних, а не використовувати клас для їх представлення, як це робиться на Java. Я не розумію, як це може бути краще, оскільки як користувач API може знати, що таке вхідні ключі, якщо вони просто представлені у вигляді карт.

Приклад :

PersonAPI {
    Person addPerson(Person obj);
    Map<String, Object> addPerson(Map<String, Object> personMap);
}

У другій функції, як користувач API може знати, які входи створюють людину?



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

Я знаю, що я бачив цю дискусію раніше десь на ПП. Я вважаю, що це було в контексті JavaScript, але аргументи були однакові. Неможливо знайти його.
Себастьян Редл

2
Оскільки Clojure - це Lisp, ви повинні робити речі, відповідні Lisp. коли ви використовуєте Java, код у ... ну Java.
AK_

Відповіді:


12

Резюме Exagg'itive (TM)

Ви отримуєте кілька речей.

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

Є один фатальний недолік.

  • Компілятор не перевіряє наявність неправильно написаних рядків для вас.
  • Автоматичні інструменти рефакторингу не перейменують назви ключових властивостей для вас, якщо тільки ви не заплатите за вигадливі.

Річ у тому, що ви можете отримати самоаналіз, використовуючи, гм, самоаналіз. Ось що зазвичай буває:

  • Увімкнути роздуми.
  • Додайте у свій проект велику бібліотеку самоаналізу.
  • Позначте різні методи та властивості об’єктів за допомогою атрибутів чи приміток.
  • Нехай бібліотека самоаналізу робить магію.

Іншими словами, якщо вам ніколи не потрібно взаємодіяти з FP, вам не доведеться приймати поради Річ Хікі.

І останнє, але не найменше (ні найкрасивіше), хоча використання Stringключа властивості має найпростіший сенс, вам не доведеться використовувати Strings. Багато застарілих систем, включаючи Android ™, широко використовують цілі ідентифікатори через весь фреймворк для посилань на класи, властивості, ресурси тощо.

Android є торговою маркою Google Inc.


Ви також можете зробити обидва світи щасливими.

Для світу Java реалізуйте геттери та сетери як завжди.

Для світу FP реалізуйте

  • Object getPropertyByName(String name)
  • void setPropertyByName(String name, Object value) throws IllegalPropertyChangeException
  • List<String> getPropertyNames()
  • Class<?> getPropertyValueClass(String name)

Всередині цієї функції так, некрасивий код, але є плагіни IDE, які заповнять це для вас, використовуючи ... е, розумний плагін, який читає ваш код.

Ява сторона Java буде такою ж ефективною, як і зазвичай. Вони ніколи не використовуватимуть цю потворну частину коду. Можливо, ви навіть захочете сховати його від Javadoc.

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


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

Зокрема, серіалізація / десеріалізація часто вимагає подібної методики.

Просто деякі загальні думки щодо "карти як об'єкта".

  1. Ви все ще повинні надати функцію для перевірки такої "карти як об'єкта". Різниця полягає в тому, що "карта як об'єкт" дозволяє отримати більш гнучкі (менш обмежувальні) критерії перевірки.
  2. Ви можете легко додати поля додавання в "карту як об'єкт".
  3. Щоб забезпечити специфікацію мінімальної вимоги дійсного об'єкта, вам потрібно буде:
    • Перелічіть набір мінімально необхідних клавіш, які очікуються на карті
    • Для кожного ключа, значення якого потрібно перевірити, надайте функцію перевірки значення
    • Якщо є правила перевірки, для яких потрібно перевірити кілька ключових значень, вкажіть це також.
    • Яка користь? Надання специфікації таким способом є інтроспективним: ви можете написати програму для запиту мінімально необхідного набору клавіш та отримання функції перевірки для кожної клавіші.
    • В OOP все це згортається в чорний ящик, на ім’я "інкапсуляція". Замість машиночитаної логіки перевірки абонент може лише зчитувати читану людиною "API документацію" (якщо, на щастя, вона існує).

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

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

2
Щодо фатальних недоліків, погодьтесь. Але, якщо назви властивостей "легко неправильно написати" та "важко переробляти" зберігаються, наскільки це можливо, в константах або перерахунках , це питання відходить. Звичайно, це обмежує розширюваність деяким :-(.
user949300

Якщо "один фатальний недолік" насправді є фатальним, чому деякі люди здатні ефективно його використовувати. Також класи та статичне введення є ортогональними - ви можете визначити класи в Clojure, навіть якщо це динамічно набрано.
Натан Девіс

@NathanDavis (1) Я визнаю, що моя відповідь написана з точки зору статичного набору тексту (C #), і я написав цю відповідь, тому що я поділяю ту саму точку зору запитувача. Зізнаюся, мені не вистачає точки зору на FP. (2) Ласкаво просимо до SE.SE, і оскільки ви шановний діяч Clojure, будь ласка, знайдіть час, щоб написати свою відповідь, якщо існуючі не задовольняють. Низькі репутації віднімають репутацію, а нові відповіді привертають відгуки, що швидко додає репутацію. (3) Я бачу, наскільки "неповні об'єкти" можуть бути корисними - ви можете запитати 2 властивості для даного об'єкта (ім'я, аватар) і залишити решту.
rwong

9

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

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

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

Нижче наведено деякі зміни коду (одна або дві з них були згадані у бесіді), які потенційно простіші за допомогою списку карт порівняно зі списком Personоб’єктів:

  • Надсилання особи на сервер REST. (Функція, створена для розміщення Mapпримітивів у форматі, що передається, може бути багаторазовим і може бути навіть надана в бібліотеці. Для Personвиконання тієї ж роботи об'єкту, ймовірно, знадобиться спеціальний код).
  • Автоматично побудувати список людей із запиту реляційної бази даних. (Знову одна загальна та багаторазово використана функція).
  • Автоматично генерувати форму для відображення та редагування людини.
  • Використовуйте загальні функції для роботи з особовими даними, які вкрай неоднорідні, як студент проти співробітника.
  • Отримайте список усіх осіб, які проживають у певному поштовому індексі.
  • Використовуйте цей код, щоб отримати список усіх підприємств у певному поштовому індексі.
  • Додайте людині специфічне поле, не зачіпаючи інших клієнтів.

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


Чи є для цього назва? Скажіть, картографування об'єктів-властивостей або картографування атрибутів об'єкта (у тому ж рядку, що і ORM)?
rwong

4
Choosing a class to represent a Person provides the immediate benefit of creating a statically-verifiable API... but that comes with the cost of limiting opportunities or increasing costs for change and reuse later on.Неправильно, і неймовірно відверто. Це покращує вашу можливість зміни пізніше, тому що, коли ви внесете переломні зміни, компілятор автоматично знайде та вкаже на вас кожне місце, яке потребує оновлення, щоб привести всю швидкість роботи вашої кодової бази. Саме в динамічному коді, де ви цього не можете зробити, ви дійсно приєднуєтесь до попереднього вибору!
Мейсон Уілер

4
@MasonWheeler: Те, що ви насправді говорите, це те, що ви цінуєте безпеку типу компіляції в часі над більш динамічними (і більш вільно набраними) структурами даних.
Роберт Харві

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

1
@GillBates Чому ти це кажеш? Ви просто втрачаєте можливість помістити ці віртуальні методи "всередину Карти" - але саме про це говорить Річ Хікі, "ActiveObjects" - це справді антидіаграма. Ви повинні ставитися до даних як до того, що вони є (дані), а не переплітати їх з поведінкою. Існує величезна користь від простоти, яку можна досягти, розділивши проблеми.
Вергілій

4
  • Якщо дані мають незначну поведінку або взагалі відсутні, з гнучким вмістом, який може змінитися, скористайтеся картою. IMO, типовий "явабейський" або "об'єкт даних", який складається з анемічної моделі домену з N полями, N сеттерами і N Getters, - це марна трата часу. Не намагайтесь вразити інших своєю прославленою структурою, загорнувши її у клас фантазії. Будьте чесні, уточнюйте свої наміри та використовуйте Карту. (Або, якщо це має сенс для вашого домену, об'єкта JSON або XML)

  • Якщо дані мають істотну фактичну поведінку, наприклад методи ( Tell, Don't Ask ), тоді використовуйте клас. І погладьте себе по спині за використання справжнього об'єктно-орієнтованого програмування :-).

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

  • Якщо дані мають помірну кількість поведінки перевірки, це є межею.

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

  • Основним недоліком використання Map є те, що користувач повинен передати значення Strings, ints, Foos тощо. Якщо це сильно дратує і схильне до помилок, розгляньте клас. Або розгляньте клас помічників, який обертає Карта відповідними дітьми.


1
Насправді, те, що Річ Хікі стверджує, це те, що якщо дані мають реальну фактичну поведінку ... ви, мабуть, робите всю "дизайнерську" річ неправильно. Дані - це "інформація". Інформація, в реальному світі НЕ "місце, де зберігаються дані". Інформація не має "операцій, які контролюють те, як змінюється інформація". Ми не передаємо інформацію, повідомляючи людям, де вона зберігається. Об'єктно-орієнтовані метафори САМОСТАВЛЯють відповідну модель світу ... але частіше за все вони не є. Ось що він говорить - «подумайте про проблему ypur». Не все є об’єктом - мало речей.
Вергілій

0

API для a mapмає два рівні.

  1. API для карт.
  2. Конвенції програми.

API може бути описано на карті за умовами. Наприклад, пара :api api-validateможе бути розміщена на карті або :api-foo validate-fooможе бути умовою. На карті можна навіть зберігати api api-documentation-link.

Використання конвенцій дозволяє програмісту створити специфічну для домену мову, яка стандартизує доступ до всіх типів, реалізованих у вигляді карт. Використання (keys map)дозволяє визначати властивості під час виконання.

Немає нічого магічного в картах і немає нічого магічного в об’єктах. Це все відправлення.

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