如何实现用于返回声明为私有的结构的策略模式?

时间:2018-08-30 01:06:17

标签: f#

如何实现返回已声明为私有的结构的策略模式?

或已翻译...

如何将私有结构的创建限制为多个功能?

我想为用户登录操作实现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 :(得分:5)

您应该使用私有构造函数为每种类型定义一个模块,并在该模块中具有createvalue函数。该模块应与该类型位于同一编译单元中,以便它可以访问私有构造函数。以下是我一直使用的模式的简单示例:

[<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函数。

答案 1 :(得分:0)

我在声明 private 构造函数的同一个包含模块中添加了策略功能(即* 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://..." }
              }