Збентежений щодо вказівок щодо кодування інтерфейсу та класу для TypeScript


81

Я прочитав керівні принципи кодування TypeScript

І мені здалося, що це твердження досить загадкове:

Не використовуйте "I" як префікс для назв інтерфейсів

Я маю на увазі, що щось подібне не мало б сенсу без префікса "Я"

class Engine implements IEngine

Мені не вистачає чогось очевидного?

Ще одне, що я не зовсім зрозумів, це:

Заняття

Для узгодженості не використовуйте класи в конвеєрі основного компілятора. Замість цього використовуйте функції закриття.

Чи сказано, що я взагалі не повинен користуватися класами?

Сподіваюся, хтось може це зрозуміти для мене :)


13
Для уточнення, це документація про стиль коду для TypeScript, а не рекомендація щодо стилю для реалізації вашого проекту. Якщо використання Iпрефіксу має сенс для вас та вашої команди, ВИКОРИСТОВУЙТЕ. Якщо ні, можливо стиль Java SomeThing(інтерфейс) з SomeThingImpl(реалізація), тоді неодмінно використовуйте це.
Brocco

2
Краса вказівок щодо кодування полягає в тому, що вони є настановами , тому ви можете вибрати їх впроваджувати чи ні. Особисто я використовую Iдля позначення інтерфейс, оскільки це те, до чого я звик, і це має сенс для людей у ​​моїй команді.
Джон Прес,

Так, це лише рекомендації. Але здебільшого за цією настановою є вагома причина. Тож просто дотримуватися того, до чого ти звик, не розвиває тебе як програміста :)
Snæbjørn

3
Коли моя команда почала використовувати TS, ми також використовували I-префікс для інтерфейсів. Вплив на C #, я припускаю. Потім я прочитав рекомендації команди TypeScript про те, як не використовувати I-префікс. На це мені зайняло кілька тижнів. Я нетерпляче шукав інформацію, чому у них таке правило. Через деякий час я зрозумів: I-префікс для інтерфейсів - це недолік (принаймні в TypeScript), він викликає більше проблем, ніж забезпечує переваги.
Станіслав Берков,

У VSCode, якщо ви вводите текст let engine = new E..., автоматично пропонуватимуться лише класи. Інтерфейсів не буде. Не зрозумів цього - це одна з причин, чому я ставлю префікс до "Я"
Дренай

Відповіді:


164

Коли команда / компанія постачає фреймворк / компілятор / набір інструментів, вони вже мають певний досвід, набір кращих практик. Вони поділяють це як настанови. Настанови - це рекомендації . Якщо вам не подобається, ви можете нехтувати ними. Компілятор все одно скомпілює ваш код. Хоча коли в Римі ...

Це моє бачення, чому команда TypeScript рекомендує не- Iпрефікс інтерфейсів.

Причина №1 Часи угорської нотації минули

Основним аргументом Iприхильників -prefix-for-interface є те, що префікс корисний для негайного прокручування (заглядання), чи є тип інтерфейсом. Заява про те, що префікс є корисним для негайного продирання (заглядання), є зверненням до угорської нотації . Iпрефікс для імені інтерфейсу, Cдля класу, Aдля абстрактного класу, sдля рядкової змінної, cдля змінної const, iдля цілочисельної змінної. Я погоджуюсь, що така обробка імен може надати вам інформацію про тип, не наводячи курсор миші на ідентифікатор або не переходячи до визначення типу за допомогою гарячої клавіші. Цю крихітну перевагу переважають недоліки угорських позначень та інші причини, згадані нижче. Угорські позначення не використовуються в сучасних рамках. C # маєIпрефікс (і це єдиний префікс у C #) для інтерфейсів через історичні причини (COM). Ретроспективно один з архітекторів .NET (Бред Абрамс) вважає, що було б краще не використовувати Iпрефікс . TypeScript не містить COM-спадщини, отже, він не має Iправила -prefix-for-interface.

Причина №2- Iпрефікс порушує принцип інкапсуляції

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

Приклад: припустимо, вам потрібно виправити у своєму коді міф API Design: Interface as Contract, наприклад, видалити ICarінтерфейс і використовувати Carзамість нього базовий клас. Тоді вам потрібно виконати таку заміну у всіх споживачів. I-prefix веде до неявної залежності споживачів від деталей реалізації чорної скриньки.

Причина №3 Захист від неправильних імен

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

Випадок абстракції: ви не повинні визначати ICarінтерфейс та пов'язаний Carклас. Carє абстракцією, і вона повинна бути тією, яка використовується для контракту. Реалізації повинні мати описові, відмінні назви, наприклад SportsCar, SuvCar, HollowCar.

Хороший приклад: WpfeServerAutosuggestManager implements AutosuggestManager, FileBasedAutosuggestManager implements AutosuggestManager.

Поганий приклад: AutosuggestManager implements IAutosuggestManager.

Причина №4 Правильно підібрані імена вакцинують вас проти міфу про дизайн API: Інтерфейс як контракт .

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

ПРИМІТКА. У TS вам не потрібен окремий інтерфейс для глузування класів або функцій перевантаження. Замість створення окремого інтерфейсу, що описує загальнодоступні члени класу, ви можете використовувати типи утиліт TypeScript . Наприклад, Required<T>будує тип, що складається з усіх публічних членів типу T.

export class SecurityPrincipalStub implements Required<SecurityPrincipal> {
  public isFeatureEnabled(entitlement: Entitlement): boolean {
      return true;
  }
  public isWidgetEnabled(kind: string): boolean {
      return true;
  }

  public areAdminToolsEnabled(): boolean {
      return true;
  }
}

Якщо ви хочете створити тип, виключаючи деяких публічних членів, ви можете використовувати комбінацію Omit та Exclude .


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

1
@demisx, якщо це незручно, тоді ти не повинен цього робити. Дайте відповідь на запитання: вони вам справді потрібні? Вони роблять якийсь сенс, або ви використовуєте їх лише для хибного почуття, що займаєтеся правильною технікою?
Станіслав Берков

2
Хоча ваші точки зору звучать добре в теорії, реальність полягає в тому, що префікс "I" часто продається для суфікса "Impl" замість цього у досить великих командах
jameslk,

3
@jameslk Я знаю, як називати речі без Iі Implважко ("У Комп'ютерних науках є лише дві важкі речі: скасування кешу та імена" - Філ Карлтон), але це можливо.
Станіслав Берков

5
Я згоден з тим, що видалення I зазвичай замінюється додаванням префікса або суфікса до реалізацій: DefaultFooService або FoodServiceImpl. З префіксом ви отримуєте грубість і відсутність накладання проблем на імена між контрактами та реалізаціями / абстракціями. Також пункт щодо повторення є дійсним, але сенс інтерфейсів - це наявність контрактів і можливість замінити реалізацію чимось іншим. Зазвичай це є надзвичайно важливим у модульному тестуванні ... не кажучи вже про переваги абстракцій та незмінності тощо. Я все для інтерфейсів, які я складаю з префіксами.
Кріс

45

Пояснення щодо посилання, яке ви посилаєтесь:

Це документація про стиль коду для TypeScript, а не рекомендація щодо стилю реалізації вашого проекту.

Якщо використання Iпрефіксу має сенс для вас та вашої команди, використовуйте його (я це роблю).

Якщо ні, можливо стиль Java SomeThing(інтерфейс) з SomeThingImpl(реалізація), тоді неодмінно використовуйте це.


19
Тільки для роз’яснень. У довіднику машинопису все ще сказано: In general, do not prefix interfaces with I (e.g. IColor). Because the concept of an interface in TypeScript is much more broad than in C# or Java, the IFoo naming convention is not broadly useful.Це не суворо, але виглядає як настійна рекомендація.
Guria

3
Погодьтеся. Це звучить як настанова коду від Microsoft для всіх проектів, заснованих на TypeScript, а не тільки самого TypeScript. Ми знайшли цю інструкцію дуже заплутаною і все ще додаємо до всіх інтерфейсів префікс "Я".
demisx

3
Є сценарій, який, на мою думку, має сенс дотримуватися I. У проекті React Cardє інтерфейс і компонент, який називається Card. У реквізиті мені потрібен масив Card, але тип не компонент. Мені потрібно вибрати ім’я ICardчи CardComponent...
BrunoLM

3
SomeThingImpl implements SomeThingтак само добре, як SomeThing implements ISomeThing. Якщо SomeThingце абстракція, тоді імена повинні бути Concrete[Some]Thing implements SomeThing.
Станіслав Берков

Як сказав @Guria, я думаю, що у довіднику TypeScript все сказано, нам просто потрібно звернути увагу на приклади, і ми будемо знати, як ним добре користуватися.
Джованніпдс

0

Я знаходжу @ stanislav-berkov досить гарну відповідь на запитання ОП. Я б поділився лише своїми 2-ма копійками, додавши, що врешті-решт ваша команда / Департамент / Компанія / Що завгодно залежить від того, щоб дійти до спільного порозуміння та встановити власні правила / настанови, яких слід дотримуватися.

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

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


-1

Тип інтерфейсу є деталлю реалізації. Деталі реалізації повинні бути приховані в API: s. Ось чому вам слід уникати I.

Слід уникати обох prefix і suffix . Це обидва помилки:

  • ICar
  • CarInterface

Що ви повинні зробити, це зробити гарне ім'я видимим в API і мати деталь реалізації, приховану в реалізації. Ось чому я пропоную:

  • Car - Інтерфейс, який відображається в API.
  • CarImpl - Реалізація того API, який прихований від споживача.

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

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