клас з мови та типу OOP


9

У теорії мови програмування тип - це набір значень. Наприклад, тип "int" - це набір усіх цілих значень.

У мовах OOP клас - це тип, чи не так?

Коли клас визначено з більш ніж одним членом, наприклад

class myclass{
    int a; 
    double b;
}

Коли ми говоримо про клас, маємо на увазі

  • " (a,b)де aє int і bє подвійним", або
  • "{ (x,y)| xбудь-який int, чи yє подвійний}"?

Що означає екземпляр myclass?

  • " (a,b)де aє int і bє подвійним", або
  • об’єкт, який займає простір пам'яті і який може (не обов'язково, тобто може бути порожнім) зберігати (x,y), де xє будь-який int і чи yє якийсь подвійний?

2
Клас - це тип. "{(x, y) | x - це будь-який int, y - будь-який подвійний}" " було б майже правильним, за винятком двох речей: 1) ви використовували кортеж, а клас - це концептуально запис - ви посилаєтесь на його поля по імені, а не по позиції; і 2) Не кожен запис із полями aі bє членами цього типу, як згадує Кілліан Форт.Міклас ізоморфний записам із полями aта bтипу intі double- ви можете взяти такий запис і перетворити його на приклад myclass.
Довал

1
У сильно набраних мовах клас - це тип. У мовах зі слабким типом це може бути, а може й не бути типом.
shawnhcorey

1
У теорії мови програмування тип - це набір значень? Я думаю, вам потрібно придбати собі іншу книгу чи іншого вчителя чи обох. "Змінна" або "константа" має "тип" і часто має "значення". Існують типи нульових значень, скаляри та типи значень композиту, де значення змінної або константи містить підзмінні / субконстанти.
user1703394

1
@ user1703394 Тип - це набір значень. 32-бітний цілочисельний тип - це набір з 2 ^ 32 різних значень. Якщо вираз оцінює значення цього типу, ви знаєте, що значення є в цьому наборі. Математичні оператори - це лише функції над значеннями цього набору.
Довал

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

Відповіді:


30

Ні.

Я вважаю, що ви запитуєте, чи достатньо мати один і той же набір типів полів, щоб класифікувати їх як один клас, чи повинні вони також бути названі однаково. Відповідь така: "Досить навіть мати однакові типи та однакові імена!" Структурно еквівалентні класи не обов'язково сумісні з типом.

Наприклад, якщо у вас є клас CartesianCoordinatesі PolarCordinatesклас, вони можуть мати два числа як свої поля, і вони можуть мати навіть один і той же Numberтип і однакові імена, але вони все одно не будуть сумісні, і екземпляр PolarCoordinatesне буде екземпляр CartesianCoordinates. Здатність окремих видів за їх прямим призначенням , а не їх поточної реалізація є дуже корисною частиною написання більш безпечним, більш супроводу коду.


9
Слід зазначити , що в деяких мовах є структурно еквівалентними є досить , щоб зробити один тип підтип іншого (і часто навпаки). Хоча це, звичайно, нечасто / непопулярно.
Теластин

7
@Tim Typedef не створює тип, він псевдонімує ім'я, яке використовується для позначення існуючого типу.
Довал

1
@DevSolar Він чітко згадував C, окрім C ++, я не знаю жодної іншої мови, яка використовує це ключове слово.
Довал

3
@Telastyn - ці мови повинні бути вбиті вогнем.
Історія Джона

4
@JonStory Структурне підтипування корисно на рівні модуля; його відсутність - це те, що змушує вас перетворити все на interfaceJava та C #, якщо ви хочете коли-небудь зробити тестування одиниць. Ви в кінцевому підсумку пишете тонну плитки, щоб ви могли змінити конкретний клас, який буде використовувати ваша програма, навіть якщо ви не маєте наміру змінювати її під час виконання.
Довал

6

Типи не є наборами.

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

Теорія наборів дозволяє створювати підмножини на льоту , якщо у вас є правило, яке описує, що належить до підмножини. Теорія типів цього взагалі не дозволяє. Хоча більшість мов мають Numberтип або щось подібне, вони не мають EvenNumberтипу, і не було б просто створити його. Я маю на увазі, що досить легко визначити сам тип, але будь-які існуючі Numbers, які трапляються, навіть не будуть магічно перетворені на EvenNumbers.

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

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

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

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

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


Поняття «підтипу» насправді зовсім відрізняється від поняття «підмножина». Якщо Xце підтип Y, це означає, що ми можемо замінити екземпляри Yна екземпляри, Xі програма все ще буде "працювати" в якомусь сенсі. Це поведінка, а не структурна, хоча деякі мови (наприклад, Go, Rust, імовірно, C) обрали останню з міркувань зручності, як для програміста, так і для мовної реалізації.


Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
Світовий інженер

4

Алгебраїчні типи даних - це спосіб обговорення цього питання.

Існує три основні способи комбінування типів:

  • Товар. Це в основному те, про що ти думаєш:

    struct IntXDouble{
      int a; 
      double b;
    }
    

    - тип товару; його значення - це всі можливі комбінації (тобто кортежі) одного intі одного double. Якщо ви розглядаєте типи чисел як набори, то кардинальність типу продукту насправді є продуктом кардинальності полів.

  • Сума. У процедурних мовах це трохи незручно виражати безпосередньо (класично це робиться з позначеними союзами ), тому для кращого розуміння ось тип суми в Haskell:

    data IntOrDouble = AnInt Int
                     | ADouble Double
    

    значення цього типу мають або форму AnInt 345, або ADouble 4.23, але завжди бере участь лише одне число (на відміну від типу продукту, де кожне значення має два числа). Отже, кардинальність: ви спочатку перераховуєте всі Intзначення, кожне повинно поєднуватися з AnIntконструктором. Плюс усі Doubleзначення, кожне в поєднанні з ADouble. Звідси вид суми .

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

То як щодо занять? Я навмисно використовував ключове слово, structа не classдля IntXDouble. Справа в тому, що клас як тип насправді не характеризується своїми полями, це лише деталі реалізації. Найважливішим фактором є швидше те, якими відмінними значеннями може володіти клас.

Що це відношення , хоча це, значення того чи іншого класу може бути значення будь-якого з його підкласів ! Отже, клас насправді є типом суми, а не типом продукту: якщо Aі Bобидва будуть похідні myClass, myClassто по суті було б сумою Aі B. Незалежно від фактичної реалізації.


1 Це функції (в математичному сенсі! ); тип функції Int -> Doubleпредставлений експоненціалом DoubleInt. До поганого, якщо ваша мова не має належних функцій ...


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

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

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

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

@Doval 1) Ви можете передавати методи навколо AFAIK, тому вони є першокласними значеннями; 2) має сенс порівнювати функції для рівності, JS люди це роблять постійно.
День

2

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


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

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

    int areacode;
    int number;

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

Побудова екземпляра може заперечувати будь-які негативні числа. Можливо, конструкція також якось нормалізувала б код коду або навіть перевірила б ціле число. Таким чином , ви б в кінцевому підсумку набагато ближче до вашого "(a,b) where a is an int and b is a double", тому що це , звичайно , НЕ який - або два ІНТ зберігається в цьому класі.

Але це не дуже важливо, що стосується класу. Клас не визначає ані тип членів даних, ані діапазон їх можливих значень. Це методи , визначені для нього.

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


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


1
It is neither the type of the data members, nor the range of their possible values that defines the class, it's the methods that are defined for it.Учасники даних не визначають клас лише тоді, коли ви їх ховаєте. Це може бути найпоширеніший випадок, але, звичайно, не можна сказати, що це стосується всіх класів. Якщо навіть одне поле є загальнодоступним, це так само важливо, як і його методи.
Довал

1
Якщо ви не кодуєте Java, де у вас немає вибору в цьому питанні, і навіть ваші німі недобросовісні фіксуючі записи повинні бути classes. (Позначення їх finalдопомагає зрозуміти, але все-таки). protectedТим не менш, у вас все ще виникає проблема з членами, які можуть бути успадковані і, таким чином, є частиною другого API для виконавців підкласів.
Довал

1
@Doval: Я зрозумів, що це "теоретичне" питання, і тому я залишався максимально чітким щодо актуальних мовних питань. (Так само, як я залишаюся максимально чітким від Яви і, protectedнаскільки це можливо, на практиці. ;-))
DevSolar

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

1
@Doval: Чи не означає це, що теорія типів сама по собі не застосовується до класів, оскільки вони є конструкцією поза сферою цієї теорії?
DevSolar

2
  • Типу є описом категорії / діапазон значень, складових структур, або що у вас. OOPwise, це схоже на "інтерфейс". (У мовно-агностичному сенсі. Мовно-специфічний сенс не так вже й багато. Наприклад, у Java intце тип , але не має відношення до interface. Загальнодоступні / захищені специфікації поля також не є частиною interface, але є частиною "інтерфейсу" або типу .)

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

  • Клас є реалізацією типу. Це шаблон, який фактично визначає внутрішню структуру, прикріплену поведінку тощо.

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