Как я могу реализовать шаблон стратегии для возврата структуры, которая объявлена ​​частной? - PullRequest
0 голосов
/ 30 августа 2018

Как реализовать шаблон стратегии для возврата структуры, которая объявлена ​​закрытой?

Или перевод ...

Как ограничить создание частной структуры несколькими функциями?

Я хочу реализовать интерпретатор IOlogin и интерпретатор MockLogin для операции входа пользователя в систему. Обе функции должны возвращать значение типа AuthenticatedManager .

Приступая к работе, я хотел определить тип в модуле так:

module Specification =

    type AuthenticatedManager = private { Name:string }

Приведенный выше код предназначен для ограничения создания AuthenticatedManager .

Я хочу сослаться на AuthenticatedManager в отдельном модуле:

module Mock =

    open Specification

    let username =        Username "test_manager"
    let password =        Password "123"
    let invalidPassword = Password "invalid password"

    let login username' password' : Result<AuthenticatedManager,Username*string> =

        if  ( username',password') = ( username,password  )

        then  Ok    { Name="authenticated manager" } // compile error
        else  Error ( username',"Failed to login" )

Я знаю, что ошибка компиляции возникла из-за того, что я пытался ссылаться на закрытую структуру вне модуля, в котором я ее объявил. Однако я считаю, что имеет смысл иметь отдельные библиотеки для размещения интерпретаторов для этого типа операций.

Ответы [ 2 ]

0 голосов
/ 31 августа 2018

Я добавил стратегическую функцию (т.е. * AuthenticationStrategy *) в тот же содержащий модуль, в котором был объявлен приватный конструктор.

Затем я смог собрать без проблем.

Спецификация

namespace Access.Specification

type Username = Username of string
type Password = Password of string

type AuthenticationStrategy = Username -> Password -> unit -> bool

type AuthenticatedManager = private { Name:string }

let authenticate username password strategy : AuthenticatedManager option = 

    if strategy username password ()
    then Some { Name= "username" }
    else None

Ложная

let login username' password' : Result<AuthenticatedManager,Username*string> =

    let strategy username'' password'' () =

        if  ( username'',password'') = ( username,password  )
        then true
        else false

    authenticate username' password' strategy 
     |> function
        | Some manager -> Ok manager
        | None         -> Error <| (username',"Login failed")

шлюз

module Access.Gateway

open Mobile.Core

let login username password : Result<AuthenticatedManager,Username*string> =

    let strategy username' password' () =
        // IO Logic goes here...
        true

    authenticate username password strategy
     |> function
        | Some manager -> Ok        manager
        | None         -> Error <| (username,"Login failed")

Приложение

module AppLogic.Configuration

open Access
open Access.Specification.Login
open Access.Specification
open TestAPI

type Environment = DEV | QA | PROD

type Dependencies = {
    Environment:    Environment
    ServerLogin:    Login.Attempt
    ForgotPassword: Login.PasswordRequest
}

let configure = function

    | DEV  -> { Environment=      DEV
                ServerLogin=    { Login=   Mock.login;          Endpoint= Endpoint "n/a" }
                ForgotPassword= { Forgot=  Mock.forgotPassword; Endpoint= Endpoint "n/a" }
              }
    | QA   -> { Environment=      QA
                ServerLogin=    { Login=   Gateway.login;       Endpoint= Endpoint "http://..." }
                ForgotPassword= { Forgot=  Mock.forgotPassword; Endpoint= Endpoint "http://..." }
              }
    | PROD -> { Environment=      PROD
                ServerLogin=    { Login=   Gateway.login;       Endpoint= Endpoint "http://..." }
                ForgotPassword= { Forgot=  Mock.forgotPassword; Endpoint= Endpoint "http://..." }
              }
0 голосов
/ 30 августа 2018

Вы должны определить модуль для каждого типа с закрытым конструктором и иметь функции create и value для типа в этом модуле. Модуль должен находиться в том же модуле компиляции, что и тип, чтобы иметь доступ к приватному конструктору. Ниже приведен простой пример шаблона, который я использую все время:

[<Struct>] type Username = private Username of string

// Make one module for each type
module Username =
    let create = function // Use whatever the real business rules are here
    | username when not <| String.IsNullOrEmpty(username) -> Ok <| Username username
    | _ -> Error "Username must not be blank"

    let value (Username username) = username

Затем, где бы вам ни понадобился Username, вы вызываете функцию create, которая имеет доступ к приватному конструктору, и она проверяет данные и строит их для вас. Аналогично, где бы вам ни понадобилось извлечь необработанный string из Username, вы используете функцию value.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...