Чи слід визначати типи для всього?


141

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

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

public string CreateNewThing()

Це робить наміри типу повернення неясними. Я думав перетворити цей рядок в інший тип, що робить його значення зрозумілішим, як таким чином:

public OperationIdentifier CreateNewThing()

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

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

Як ви вважаєте, чи є хорошою практикою обгортати прості типи в класі з міркувань безпеки?


4
Можливо, вам буде цікаво почитати про крихітні типи. Я трохи погрався з ним, і не рекомендував би його, але це цікавий підхід. darrenhobbs.com/2007/04/11/tiny-types
Jeroen Vannevel

40
Щойно ви використовуєте щось на зразок Haskell, ви побачите, наскільки допомагає використання типів. Типи допомагають забезпечити правильну програму.
Нейт Симер

3
Навіть динамічна мова, як Ерланг, має перевагу від типової системи, саме тому вона має систему typespec та інструмент перевірки типу, знайомий Haskellers, незважаючи на те, що він є динамічною мовою. Такі мови, як C / C ++, де реальний тип чого-небудь є цілим числом, яке ми погоджуємось із компілятором, щоб трактувати певний спосіб, або Java, де ви майже маєте сорти, якщо ви вирішили робити вигляд, що класи - це типи, що представляють собою семантичну проблему перевантаження в мова (це "клас" чи "тип"?) або двозначність типу (це "URL", "рядок" чи "UPC"?). Зрештою, використовуйте будь-які інструменти, які ви можете відключити.
zxq9

7
Я не впевнений, звідки ідея, що C ++ має накладні витрати на типи. За допомогою C ++ 11 ніхто з виробників компіляторів не бачив проблеми в наданні кожному лямбда свого унікального типу. Але TBH ви не можете реально розраховувати, що ви знайдете багато розуміння в коментарі про "систему типів C / C ++" так, ніби вони розділяють систему типів.
MSalters

2
@JDB - ні, це не так. Навіть не схожий.
Давор Ждрало

Відповіді:


152

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

Ось чому виклик предметів Calisthenics є одним із його правил: обгортання примітивів:

Правило 3: Обмотайте всі примітиви та струни

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

Цей же документ пояснює, що є додаткова вигода:

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

Дійсно, коли використовуються примітиви, зазвичай надзвичайно важко відстежити точне розташування коду, пов’язаного з цими типами, що часто призводить до суворого дублювання коду . Якщо є Price: Moneyклас, то природно знайти перевірку діапазону всередині. Якщо замість цього int(гірше, а double) використовується для зберігання цін на продукцію, хто повинен перевірити асортимент? Продукт? Знижка? Візок?

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

Недолік - і той самий аргумент стосується кожного правила вправи «Об'єктна гігієніка» - полягає в тому, що якщо швидко стає занадто непосильним, щоб створити клас для всього . Якщо Productміститься, ProductPriceщо успадковує, від PositivePriceчого успадковує, від Priceчого в свою чергу успадковує Money, це не чиста архітектура, а скоріше повний безлад, де для того, щоб знайти одну річ, сервіс повинен кожного разу відкривати кілька десятків файлів.

Ще один момент, який слід врахувати, - це вартість (з точки зору рядків коду) створення додаткових класів. Якщо обгортки незмінні (як це має бути, як правило), це означає, що якщо ми візьмемо C #, ви повинні мати в обгортці щонайменше:

  • Власник,
  • Поле підкладки,
  • Конструктор, який присвоює значення поле резервного копіювання,
  • Звичай ToString(),
  • Зауваження щодо документації XML (що складає багато рядків),
  • A Equalsі GetHashCodeпереоцінка (також багато LOC).

і в кінцевому підсумку, якщо це доречно:

  • DebuggerDisplay атрибут,
  • Перебор ==і !=операторів,
  • Врешті-решт, перевантаження оператора неявного перетворення для безперебійного перетворення в інкапсульований тип та з нього,
  • Код контрактів (включаючи інваріант, який є досить тривалим методом, з трьома його ознаками),
  • Кілька перетворювачів, які будуть використовуватися під час серіалізації XML, серіалізації JSON або зберігання / завантаження значення в / з бази даних.

Сотня LOC для простої обгортки робить її досить заборонною, тому ви можете бути повністю впевнені в довгостроковій прибутковості такої обгортки. Поняття обсягу, пояснене Томасом Юнком , особливо актуальне тут. Написання сотень локальних місць для представлення ProductIdвикористаних у всій базі коду виглядає досить корисним. Написання класу такого розміру для фрагмента коду, який складає три рядки в рамках одного методу, набагато сумнівніше.

Висновок:

  • Загортайте примітиви в класи, які мають значення у бізнес-області програми, коли (1) це сприяє зменшенню помилок, (2) зменшує ризик дублювання коду або (3) допомагає змінити базовий тип пізніше.

  • Не обертайте автоматично кожного примітиву, який ви знайдете у своєму коді: є багато випадків, коли користуються stringабо intідеально добре.

На практиці public string CreateNewThing()повернення екземпляра ThingIdкласу замість stringможе допомогти, але ви також можете:

  • Поверніть екземпляр Id<string>класу, який є об'єктом загального типу, що вказує, що базовим типом є рядок. Ви маєте перевагу читабельності, без недоліку необхідності підтримувати багато типів.

  • Повернути примірник Thingкласу. Якщо користувачеві потрібен лише ідентифікатор, це можна легко зробити за допомогою:

    var thing = this.CreateNewThing();
    var id = thing.Id;
    

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

4
Що стосується грошей, переконайтеся, що валюта або включена до об'єкта Гроші, або вся ваша програма використовує ту саму (яка потім повинна бути задокументована).
Paŭlo Ebermann

5
@Blrfl: хоча є поважні випадки, коли необхідне глибоке успадкування, я зазвичай бачу таке успадкування в переробленому коді, написаному без уваги до читабельності, що виникає внаслідок нав'язливого ставлення до оплати за LOC. Таким чином, негативний характер цієї частини моєї відповіді.
Арсеній Муренко

3
@ArTs: Це аргумент "засудимо інструмент, тому що деякі люди використовують його неправильно".
Blrfl

6
@MainMa Приклад того, де сенс міг уникнути дорогих помилок: en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure
cbojar

60

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

Скажімо, у вас є такий псевдокод

id = generateProcessId();
doFancyOtherstuff();
job.do(id);

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


14
Дуже хороший момент. Явні типи є важливим інструментом для спілкування про наміри , що особливо важливо через межі об'єктів та послуг.
Авнер Шахар-Каштан

+1, хоча якщо ви ще не відремонтували весь код doFancyOtherstuff()в підпрограмі, ви можете подумати, що job.do(id)посилання недостатньо локальне, щоб зберігати його як простий рядок.
Марк Херд

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

34

Системи статичного типу стосуються запобігання неправильного використання даних.

Існують очевидні приклади таких типів:

  • ви не можете отримати місяць UUID
  • ви не можете перемножувати дві нитки.

Є більш тонкі приклади

  • ви не можете заплатити за щось, використовуючи довжину письмового столу
  • ви не можете зробити запит HTTP, використовуючи чиєсь ім’я як URL-адресу.

Ми можемо спокуситись використовувати doubleяк за ціною, так і за довжиною, або використовувати stringім’я та URL-адресу. Але це підриває нашу дивовижну систему типів і дозволяє цим зловживанням проходити статичні перевірки мови.

Якщо плутати секунди фунта з Newton-секундами, це може мати погані результати під час виконання .


Особливо це проблема зі струнами. Вони часто стають "універсальним типом даних".

Ми звикли в основному текстові інтерфейси з комп'ютерами, і ми часто поширюємо ці людські інтерфейси (інтерфейси) на інтерфейси програмування (API). Ми думаємо, що 34,25 є персонажами 34,25 . Ми думаємо про побачення як персонажів 05-03-2015 . Ми вважаємо UUID як символи 75e945ee-f1e9-11e4-b9b2-1697f925ec7b .

Але ця ментальна модель шкодить абстракції API.

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

Альберт Ейнштейн

Аналогічно, текстові подання не повинні грати ніякої ролі в розробці типів та API. Остерігайся string! (та інші надто загальні "примітивні" типи)


Типи повідомляють "які операції мають сенс".

Наприклад, я колись працював над клієнтом API HTTP REST. REST, правильно виконаний, використовує гіпермедіальні об'єкти, які мають гіперпосилання, що вказують на суміжні об'єкти. У цьому клієнті не тільки були введені сутності (наприклад, Користувач, Обліковий запис, Підписка), також були набрані посилання на ці об'єкти (UserLink, AccountLink, SubscriptionLink). Посилання були трохи більше, ніж обгортки навколо Uri, але окремі типи унеможливили спробу використання облікового запису для отримання користувача. Якби все було просто Uri- або ще гірше string- ці помилки були б виявлені лише під час виконання.

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


Звичайно, всі хороші речі можна використовувати в надлишку. Розглянемо

  1. скільки чіткості додає ваш код

  2. як часто він використовується

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


21
"рядки часто стають" універсальним "типом даних" - ось походження монітору "строго набрані мови".
MSalters

@MSalters, ха-ха, дуже приємно.
Пол Дрейпер

4
І звичайно, що 05-03-2015 означає, коли його трактують як побачення ?
CVn

10

Як ви вважаєте, чи є хорошою практикою обгортати прості типи в класі з міркувань безпеки?

Іноді.

Це один із тих випадків, коли потрібно зважувати проблеми, які можуть виникнути в результаті використання stringзамість більш конкретного OperationIdentifier. Яка їх суворість? Яка їхня прихильність?

Тоді вам потрібно врахувати вартість використання іншого типу. Наскільки боляче це використовувати? Скільки роботи потрібно зробити?

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

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


10

Я, як правило, погоджуюся, що багато разів вам слід створити тип для примітивів і рядків, але оскільки вищевказані відповіді рекомендують створити тип у більшості випадків, я перерахую кілька причин, чому / коли не потрібно:

  • Продуктивність. Я повинен посилатися на фактичні мови тут. Якщо у C #, якщо ідентифікатор клієнта короткий, і ви зафіксуєте його у класі - ви просто створили багато накладних витрат: пам'ять, оскільки зараз це 8 байт у 64-бітовій системі та швидкості, оскільки тепер вона виділяється на купі.
  • Коли ви робите припущення щодо типу. Якщо ідентифікатор клієнта короткий і у вас є певна логіка, яка певним чином пакує його - зазвичай ви вже робите припущення щодо типу. У всіх тих місцях вам зараз доведеться порушити абстракцію. Якщо це одне місце, це не велика справа, якщо це всюди, ви можете виявити, що половину часу ви використовуєте примітив.
  • Тому що не кожна мова має typedef. А для мов, які цього не пишуть, та кодування, про які вже написано, внесення таких змін може бути великим завданням, яке може навіть ввести помилки (але кожен має тестовий набір з хорошим покриттям, правда?).
  • У деяких випадках це знижує читабельність. Як надрукувати це? Як я це підтверджую? Чи повинен я перевіряти наявність нуля? Чи є всі питання, які потребують детального ознайомлення з визначенням типу.

3
Безумовно, якщо тип обгортки є структурою, а не класом, тоді short(наприклад) має таку ж вартість, яку обгорнули або розгорнули. (?)
Математична

1
Чи не було б гарним дизайном для типу, щоб інкапсулювати власну перевірку? Або створити тип помічника, щоб перевірити його?
Нік Уделл

1
Упаковка або маніпулювання без зайвих зусиль є анти-зразком. Інформація повинна надходити прозоро через код програми, за винятком випадків, коли трансформація явно та справді необхідна . Типи хороші, оскільки вони, як правило, замінюють невелику кількість хорошого коду, розташованого в одному місці, для великої кількості поганої реалізації, що розбризкується по всій системі.
Thomas W

7

Ні, ти не повинен визначати типи (класи) для "всього".

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

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

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


5

На поверхні виглядає все, що вам потрібно зробити - це визначити операцію.

Крім того, ви кажете, що повинна робити операція:

щоб почати операцію [і] контролювати його обробку

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

Він говорить

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

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

Щодо коментаря

Було б wtf-y називати цей клас командою, а тоді не мати логіки всередині нього.

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

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

  1. Якби у вас була вся ця логіка в команді, вона була б роздута і безладна. Ви б подумали, «о добре, вся ця функціональність повинна бути в окремих класах». Ви б повернули логіку до команди.
  2. Якби у вас була вся ця логіка в команді, було б важко перевірити і перешкодити тестуванню. "Святий дерьмо, чому я повинен використовувати цю команду дурниць? Я хочу лише перевірити, чи випадає ця функція 1 чи ні. Я не хочу її називати пізніше, я хочу перевірити її зараз!"
  3. Якби у вас була вся ця логіка в команді, не було б занадто сенсу звітувати про її завершення. Якщо ви думаєте про функцію, яка буде виконуватися синхронно за замовчуванням, немає сенсу отримувати сповіщення про її завершення. Можливо, ви починаєте інший потік, тому що ваша операція полягає в тому, щоб перевести аватар у формат кінотеатру (може знадобитися додаткова нитка на малиновому пі), можливо, вам доведеться отримати деякі дані з сервера, ... що б це не було, якщо є причина для Звіт про завершення, можливо, тому, що відбувається деяка асинхронність (це слово?). Я думаю, що запуск потоку або звернення до сервера - це логіка, яка не повинна бути у вашій команді. Це дещо мета-аргумент і може бути суперечливим. Якщо ви вважаєте, що це не має сенсу, скажіть, будь ласка, у коментарях.

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


Оскільки шаблони абстрактні, важко придумати хороші метафори реального світу. Ось спроба:

"Ей, бабусю, ти можеш натиснути кнопку запису на телевізорі о 12 годині, щоб я не пропустив симпсонів на каналі 1?"

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


Ви маєте рацію, я не думав про це так. Але шаблон команди не відповідає 100%, оскільки логіка операції інкапсульована в інший клас, і я не можу помістити її всередині об'єкта, який є Command. Було б wtf-y називати цей клас командою, а тоді не мати логіки всередині нього.
Зів

Це має багато сенсу. Подумайте про повернення операції, а не ідентифікатора OperationIdentifier. Це схоже на C # TPL, де ви повертаєте завдання або завдання <T>. @ Ziv: Ви кажете, "логіка операції інкапсульована в інший клас". Але чи це насправді означає, що ви також не можете цього надати і тут?
Moby Disk

@Ziv Я прошу відрізнятися. Погляньте на причини, які я додав до своєї відповіді, і подивіться, чи мають вони для вас сенс. =)
null

5

Ідея загортання примітивних типів,

  • Для встановлення конкретної доменної мови
  • Обмежте помилки, які роблять користувачі, передаючи неправильні значення

Очевидно, це було б дуже важко і не практично робити це скрізь, але важливо обгортати типи, де потрібно,

Наприклад, якщо у вас є замовлення класу,

Order
{
   long OrderId { get; set; }
   string InvoiceNumber { get; set; }
   string Description { get; set; }
   double Amount { get; set; }
   string CurrencyCode { get; set; }
}

Важливими властивостями пошуку замовлення є переважно OrderId та InvoiceNumber. І Сума, і Кодекс валюти тісно пов'язані між собою, коли, якщо хтось змінить Валютний Код без зміни Суми, Порядок більше не може вважатися дійсним.

Отже, лише обгортання OrderId, InvoiceNumber та введення композиту для валюти має сенс у цьому сценарії, і, ймовірно, опис обгортання не має сенсу. Тому бажаний результат може виглядати так:

    Order
    {
       OrderIdType OrderId { get; set; }
       InvoiceNumberType InvoiceNumber { get; set; }
       string Description { get; set; }
       Currency Amount { get; set; }
    }

Тож немає причини обгортати все, а речі, які насправді мають значення.


4

Непопулярна думка:

Зазвичай не слід визначати новий тип!

Визначення нового типу для обговорення примітивного або базового класу іноді називають визначенням псевдотипу. IBM описує це як погана практика тут (вони зосереджені саме на неправильне використання дженериків в даному випадку).

Псевдо типи роблять загальні функції бібліотеки марними.

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

Псевдо типи переходять у вірусні

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

Забирай повідомлення

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

Приклад:

A uintможе чудово представляти UserId, ми можемо продовжувати використовувати вбудовані Java-оператори для uints (наприклад, ==), і нам не потрібна бізнес-логіка для захисту "внутрішнього стану" ідентифікатора користувача.


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

Якщо ви бачили рекламу на позики найбіднішим людям у Великобританії, ви побачите, що відсоток обернути вдвічі в діапазоні 0..1 не працює ...
gnasher729

1
У C / C ++ / Objective C ви можете використовувати typedef, який дає вам лише нове ім'я для існуючого примітивного типу. З іншого боку, ваш код повинен працювати незалежно від основного типу, наприклад, якщо я зміню OrderId з uint32_t на uint64_t (тому що мені потрібно обробити більше 4 мільярдів замовлень).
gnasher729

Я не буду заперечувати вас за вашу хоробрість, але псевдотипи та типи, визначені у відповіді топраптом, різні. Це типи, які є специфічними для бізнесу. Пседотипи - це типи, які все ще є загальними.
0fnt

@ gnasher729: C ++ 11 має також визначені користувачем літерали, які роблять процес обгортання дуже солодким для користувача: відстань d = 36.0_mi + 42.0_km; . msdn.microsoft.com/en-us/library/dn919277.aspx
damix911

3

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

NamingConvention є ключовим для читабельності. Двоє комбінованих можуть передати наміри чітко.
Але /// все-таки корисно.

Тож так, я б сказав, створити багато власних типів, коли їхнє життя виходить за межі класу. Також врахуйте використання обох Structі Class, не завжди, класів.


2
Що ///означає?
Гейб

1
/// - це коментар до документації на C #, який дозволяє intellisense показувати документацію підпису в точці методу виклику. Розглянемо найкращий спосіб документування коду. Можна обробляти інструментами та пропонувати поведінку інтелігенції. msdn.microsoft.com/en-us/library/tkxs89c5%28v=vs.71%29.aspx
phil soady
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.