当前位置:  开发笔记 > 编程语言 > 正文

我怎样才能确保非法行为是不可行的?

如何解决《我怎样才能确保非法行为是不可行的?》经验,为你挑选了1个好方法。

如何使非法行为无法执行?

摘要:

自从开始学习F#之后,我学习了类型驱动设计和基于属性的测试.结果,我爱上了让非法国家无法代表的想法.

但我真正想做的是使非法行为无法执行.

我正在通过写一个BlackJack游戏来学习F#.因此,我想确保当经销商分发卡片时,经销商只能处理"初始手"或"击中".所有其他卡片分发都是非法的.

在C#中,我将实现策略模式,从而创建DealHandCommand和DealHitCommand.然后我会硬编码一个常数整数值来表示要处理的卡数(每个策略).

DealHandCommand = 2张牌

DealHitCommand = 1张卡

基于这些策略,我将实现一个状态机来代表BlackJack游戏的一个会话.因此,在我处理初始手(即DealHandCommand)之后,我执行状态转换,其中未来的交易只能执行"DealHitCommand".

具体而言,在混合功能语言中实现状态机是否有意义,以实现不可执行的非法行为?



1> Mark Seemann..:

在F#中实现状态机很容易.它通常遵循三个步骤,第三步是可选的:

    使用每个州的案例定义一个被区分的联盟

    为每种情况定义转换函数

    可选:实现所有其余代码

步骤1

在这种情况下,我觉得有两种状态:

最初的牌有两张牌

命中一个额外的卡

这表明这种Deal歧视的联盟:

type Deal = Hand of Card * Card | Hit of Card

另外,定义一个Game是什么:

type Game = Game of Deal list

注意使用单一案例的歧视联盟; 这是有原因的.

第2步

现在定义一个从每个状态转换为a的函数Game.

事实证明,你不能任何游戏状态转换到Hand案例,因为a Hand开始新游戏的原因.在另一方面(双关语意),你需要提供要插入的手:

let init c1 c2 = Game [Hand (c1, c2)]

另一种情况是游戏正在进行中,你应该只允许Hit,但不是Hand,所以定义这个过渡:

let hit (Game deals) card = Game (Hit card :: deals)

如您所见,该hit功能要求您传入现有功能Game.

第3步

什么阻止客户创建无效Game值,例如[Hand; Hit; Hand; Hit; Hit]

您可以使用签名文件封装上述状态机:

BlackJack.fsi:

type Deal
type Game
val init : Card -> Card -> Game
val hit : Game -> Card -> Game
val card : Deal -> Card list
val cards : Game -> Card list

这里,类型DealGame声明,但他们的'构造函数'不是.这意味着您无法直接创建这些类型的值.例如,这不编译:

let g = BlackJack.Game []

给出的错误是:

错误FS0039:未定义值,构造函数,命名空间或类型"Game"

创建Game值的唯一方法是调用为您创建值的函数:

let g =
    BlackJack.init
        { Face = Ace; Suit = Spades }
        { Face = King; Suit = Diamonds }

这也使您可以继续游戏:

let g' = BlackJack.hit g { Face = Two; Suit = Spades }

您可能已经注意到,上面的签名文件还定义了两个函数来获取卡GameDeal值.以下是实施:

let card = function
    | Hand (c1, c2) -> [c1; c2]
    | Hit c -> [c]

let cards (Game deals) = List.collect card deals

客户端可以像这样使用它们:

> let cs = g' |> BlackJack.cards;;
>

val cs : Card list = [{Suit = Spades;
                       Face = Two;};
                      {Suit = Spades;
                       Face = Ace;};
                      {Suit = Diamonds;
                       Face = King;}]

请注意,这种方法主要是结构性的; 移动部件很少.

附录

这些是上面使用的文件:

Cards.fs:

namespace Ploeh.StackOverflow.Q34042428.Cards

type Suit = Diamonds | Hearts | Clubs | Spades
type Face =
    | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten
    | Jack | Queen | King | Ace

type Card = { Suit: Suit; Face: Face }

BlackJack.fsi:

module Ploeh.StackOverflow.Q34042428.Cards.BlackJack

type Deal
type Game
val init : Card -> Card -> Game
val hit : Game -> Card -> Game
val card : Deal -> Card list
val cards : Game -> Card list

BlackJack.fs:

module Ploeh.StackOverflow.Q34042428.Cards.BlackJack

open Ploeh.StackOverflow.Q34042428.Cards

type Deal = Hand of Card * Card | Hit of Card

type Game = Game of Deal list

let init c1 c2 = Game [Hand (c1, c2)]

let hit (Game deals) card = Game (Hit card :: deals)

let card = function
    | Hand (c1, c2) -> [c1; c2]
    | Hit c -> [c]

let cards (Game deals) = List.collect card deals

Client.fs:

module Ploeh.StackOverflow.Q34042428.Cards.Client

open Ploeh.StackOverflow.Q34042428.Cards

let g =
    BlackJack.init
        { Face = Ace; Suit = Spades }
        { Face = King; Suit = Diamonds }
let g' = BlackJack.hit g { Face = Two; Suit = Spades }

let cs = g' |> BlackJack.cards

推荐阅读
ERIK又
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有