Trilhas
/
Elm
Elm
/
Exercícios
/
Baú de Tesouro Seguro
Baú de Tesouro Seguro

Baú de Tesouro Seguro

Exercício de aprendizagem

Introdução

Tipos opacos

Introdução

Tipos opacos são uma técnica de encapsulamento em Elm. Algumas pessoas também chamam isso de ocultação de informações, ou ocultação de detalhes de implementação. Eles também são comumente usados para garantir a validação, usando o padrão Parse, dont validate. Por exemplo, você pode ter um tipo opaco EmailAddress, que só pode conter endereços de email válidos (ao contrário de uma String, que pode conter qualquer coisa).

Criando um tipo opaco

Tipos personalizados têm uma ou mais variantes, e o compilador gera automaticamente uma função para criar cada variante, usando o nome da variante. Essas são a única forma de criar tipos personalizados. O compilador também permite desestruturação para essas variantes.

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 _'

Por padrão, tudo isso é interno ao arquivo / módulo, e usamos a palavra-chave exposing para escolher o que exportar. Para criar um tipo opaco, você simplesmente escolhe não exportar as variantes.

Quando você faz isso, quase sempre precisa fornecer outra forma de criar e obter as variantes, expondo funções personalizadas. Essas funções personalizadas permitem que você aplique alguma lógica ao criar e obter as variantes. Isso oculta os detalhes de implementação internos do módulo e significa que você pode alterar os detalhes de implementação o quanto quiser, desde que as funções expostas permaneçam as mesmas.

Parse, dont validate

Provavelmente o uso mais comum de Tipos opacos é garantir a validação, usando o padrão Parse, dont validate. Nesse padrão, você expõe uma função que recebe uma entrada com menos estrutura / segurança de tipos do que você deseja, como uma String, e então a parseia para um tipo mais estruturado, como EmailAddress, retornando o tipo ou retornando um erro (geralmente Maybe ou Result).

Por exemplo, no código abaixo, só é possível para módulos externos criar um tipo EmailAddress usando a função parseEmailAddress, então quando você está trabalhando com o tipo EmailAddress, você tem certeza de que ele é válido.

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
-- ... 

Instruções

Neste exercício, você vai escrever um SecureTreasureChest genérico (e mágico!) para guardar um tesouro. Quem prestou atenção deve ter notado que o TreasureChest anterior não era tão seguro assim! Se você usasse a função getTreasure, precisava informar a senha, mas ainda podia simplesmente desestruturar o tipo TreasureChest para pegar o tesouro, sem precisar saber a senha. Pior ainda: você conseguia até recuperar a senha do mesmo jeito! Vamos resolver isso usando um Tipo Opaco.

1. Defina o tipo opaco Password, usando o padrão "Parse, don't validate"

Defina um tipo customizado Password com uma única variante.

A variante deve ter um valor String associado, para a senha.

Você deve expor o tipo Password, mas não a variante. Isso torna Password um Tipo Opaco e significa que você não pode mais usar desestruturação para pegar a senha. Não é possível testar se um tipo é opaco ou não; em código de produção, você usaria o elm-review (uma ferramenta de análise estática) para garantir isso.

Defina e exponha uma função createPassword, com uma definição de tipo, que recebe uma String e retorna um Maybe Password.

A Owasp recomenda que as senhas tenham pelo menos 8 caracteres, então vamos usar o padrão Parse, don't validate para garantir essa invariante.

Se a senha tiver menos de 8 caracteres, createPassword deve retornar Nothing; caso contrário, deve retornar Just Password.

2. Exponha SecureTreasureChest, mas não a sua variante

Você deve expor o tipo SecureTreasureChest, mas não a variante. Isso torna SecureTreasureChest um Tipo Opaco e significa que você não pode mais usar desestruturação para pegar a senha ou o tesouro.

3. Defina a função createTreasure

Essa função deve receber dois parâmetros e ter uma definição de tipo.

  • um tipo genérico (para o tesouro)
  • uma Password (para a senha)

A função deve retornar um SecureTreasureChest.

Exponha essa função para que outros módulos possam criar instâncias de SecureTreasureChest.

4. Defina a função getTreasure

Essa função deve receber dois parâmetros e ter uma definição de tipo.

  • uma String (para a senha)
  • um tipo customizado genérico SecureTreasureChest

Essa função deve conferir a tentativa de senha fornecida com a Password do SecureTreasureChest.

A função deve retornar um Maybe.

Se as senhas coincidirem, retorne Just com o valor genérico do SecureTreasureChest (o tesouro!)

Se as senhas não coincidirem, retorne Nothing.

Exponha essa função para que outros módulos possam pegar o tesouro de um SecureTreasureChest, desde que informem a senha correta.

Editar via GitHub O link abre em uma nova janela ou aba
Elm Exercism

Tudo pronto para começar Baú de Tesouro Seguro?

Crie sua conta no Exercism para aprender e dominar Elm com 28 conceitos110 exercícios e mentoria humana de verdade, tudo de graça.