如何实现返回已声明为私有的结构的策略模式?
或已翻译...
如何将私有结构的创建限制为多个功能?
我想为用户登录操作实现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" )
我知道编译错误源于我试图在我声明了它的模块之外引用私有结构的方法。但是,我相信拥有单独的库来容纳此类操作的解释器是有道理的。
答案 0 :(得分:5)
您应该使用私有构造函数为每种类型定义一个模块,并在该模块中具有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
函数。
答案 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://..." }
}