Треки
/
Elm
Elm
/
Вправи
/
Захищена скриня зі скарбами
Захищена скриня зі скарбами

Захищена скриня зі скарбами

Навчальна вправа

Вступ

Непрозорі типи

Вступ

Непрозорі типи - це техніка інкапсуляції в Elm. Дехто також називає це приховуванням інформації або приховуванням деталей реалізації. Їх також часто використовують, щоб забезпечити валідацію, застосовуючи підхід Розбирати, а не перевіряти. Наприклад, ми могли б мати непрозорий тип EmailAddress, який може містити лише дійсні адреси електронної пошти (на противагу String, який може містити будь-що).

Створення непрозорого типу

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

type Maybe a
    = Nothing --> Variant implies a 'Nothing' function, with type 'Maybe a', and destructuring of 'Nothing'
    | Just a  --> Variant implies a 'Just' function, with type 'a -> Maybe a', and destructuring of 'Just _'

Типово все це є внутрішнім для файлу / модуля, і ми використовуємо ключове слово exposing, щоб вибрати, що експортувати. Щоб створити непрозорий тип, ми просто вирішуємо не експортувати варіанти.

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

Розбирати, а не перевіряти

Мабуть, найпоширеніше застосування непрозорих типів - забезпечувати валідацію за допомогою підходу Розбирати, а не перевіряти. У цьому підході ми експортуємо функцію, яка приймає вхідні дані з меншою структурою / типовою безпекою, ніж нам потрібно, наприклад String, а потім розбирає їх у структурованіший тип, наприклад EmailAddress, і повертає або цей тип, або помилку (зазвичай Maybe чи Result).

Наприклад, у коді нижче зовнішні модулі можуть створити тип EmailAddress лише за допомогою функції parseEmailAddress, тож коли ми працюємо з типом EmailAddress, ми певні, що він дійсний.

module EmailAddress exposing ( EmailAddress, parseEmailAddress, getEmailAddress )

-- The `EmailAddress` custom type has one variant, also called `EmailAddress`
-- When a custom type has one variant, it is idiomatic to use the same name for both.
type EmailAddress = 
  EmailAddress String

-- create an EmailAddress variant
parseEmailAddress: String -> Maybe EmailAddress
parseEmailAddress candidateEmailAddress =   
  if isValidEmailAddress candidateEmailAddress then
    Just (EmailAddress candidateEmailAddress)
  else
    Nothing

-- get an EmailAddress variant
getEmailAddress: EmailAddress -> String
getEmailAddress ( EmailAddress  emailAdress ) =
  emailAdress

isValidEmailAddress: String -> bool
-- ... 

Вказівки

У цій вправі ми напишемо узагальнений (або чарівний!) SecureTreasureChest, щоб зберігати скарб. Уважні читачі помітили, що попередній TreasureChest не був таким уже й безпечним! Якщо ми використали функцію getTreasure, то мусили надати пароль, але все одно могли просто деструктурувати тип TreasureChest, щоб отримати скарб, навіть не знаючи пароля. Що гірше, так само можна було отримати й сам пароль! Виправмо це за допомогою непрозорого типу.

1. Визначте непрозорий тип Password за шаблоном «Parse, don't validate»

Визначте власний тип Password з єдиним варіантом.

Варіант має містити повʼязане значення типу String для пароля.

Варто відкрити тип Password, але не його варіант. Це робить Password непрозорим типом, і означає, що більше не можна скористатися деструктуруванням, щоб отримати пароль. Неможливо перевірити, чи є тип непрозорим; у робочому коді для цього ми використали б elm-review (інструмент статичного аналізу).

Визначте й відкрийте функцію createPassword з описом типу, яка приймає String і повертає Maybe Password.

Owasp рекомендує, щоб паролі містили щонайменше 8 символів, тож застосуймо шаблон Parse, don't validate, щоб забезпечити цей інваріант.

Якщо пароль коротший за 8 символів, createPassword має повернути Nothing, інакше він має повернути Just Password.

2. Відкрийте SecureTreasureChest, але не його варіант

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

3. Визначте функцію createTreasure

Ця функція має приймати два параметри й мати опис типу.

  • узагальнений тип (для скарбу)
  • Password (для пароля)

Функція має повертати SecureTreasureChest.

Відкрийте цю функцію, щоб інші модулі могли створювати примірники SecureTreasureChest.

4. Визначте функцію getTreasure

Ця функція має приймати два параметри й мати опис типу.

  • String (для пароля)
  • узагальнений власний тип SecureTreasureChest

Ця функція має звірити надану спробу пароля з Password у SecureTreasureChest.

Функція має повертати Maybe.

Якщо паролі збігаються, повертаємо Just із узагальненим значенням із SecureTreasureChest (скарбом!)

Якщо паролі не збігаються, повертаємо Nothing.

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

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Elm Exercism

Час розпочати Захищена скриня зі скарбами?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Elm, а також 28 концепцій110 вправ та справжнє наставництво від людей, і все це безкоштовно.