我试图通过使用Rx.Net来实现Tic-Tac-Toe来学习更多关于功能反应式编程的知识。我遇到的问题是我的游戏逻辑似乎存在循环依赖。
commands
流(PlaceToken
,ResetGame
等)是从用户输入流生成的。
游戏的当前状态(boardStates
)是通过将commands
应用于先前状态(从初始状态开始)得出的:
var initialBoardState = new BoardState();
var boardStates = commands
.Scan(initialBoardState, (boardState, command) => command.Apply(boardState))
.DistinctUntilChanged();
但是,commands
流应该依赖于boardStates
流。这是因为有效的命令集随当前状态而变化。
例如,PlaceToken
命令只应在用户点击空图块时发出,但空图块集由当前状态定义!
总而言之,我有两个似乎依赖于彼此的流。我应该如何在功能反应式编程中解决这个问题?
答案 0 :(得分:2)
虽然@ LeeCampbell的解决方案确实有效,但它需要使您的核心模型类可变。相反,我发现最好复制cycle.js采用的方法。你可以看到他们的解释here。
问题在于我们有一个循环。行动流取决于董事会状态流,而这又依赖于行动流:
boardStream = f(actionStream)
actionStream = g(boardStream)
cycle.js解决方案是使用代理流将所有内容连接在一起:
// Create a proxy
proxyActionStream = new Stream()
// Create our mutually dependent streams using the proxy
boardStream = f(proxyActionStream)
actionStream = g(boardStream)
// Feed actionStream back into proxyActionStream
actionStream.Subscribe(x => proxyActionStream.OnNext(x))
在Rx.Net域中,代理流应为ReplaySubject
。
唯一需要注意的地方是逃避反馈循环:如果你的流不会稳定,那么它们会无限循环!将流视为相互递归是有帮助的。
答案 1 :(得分:1)
并非一切都需要成为一个事件。记住Rx / Callbacks是一种允许你依赖的东西,给你回电话(不依赖你)。
根据经验
2)也可以翻译为
只需调用依赖项上的方法来更改其状态,但订阅其事件以查看其更改
因此,您不应将Commands视为流,而应将用户操作视为流,当收听时可能会创建命令。
所以BoardState可能看起来像这样
public class BoardState
{
public void PlaceToken(PlaceTokenCommand placeToken)
{
//Process, then raise event
}
public void Reset()
{
//Process, then raise event
}
public IObservable<?> StateUpdates()
{
}
}
并且ViewModel(?)代码可能看起来像这样
public class TicTacToeViewModel
{
private readonly BoardState _board;
public TicTacToeViewModel()
{
_board = new BoardState();
MoveTokenCommand = new DelegateCommand(MoveToken, CanMoveToken);
ResetBoardCommand = new DelegateCommand(_board.Reset);
board.StateUpdates(state => UpdatePresentation(state));
}
public DelegateCommand MoveTokenCommand { get; private set;}
public DelegateCommand ResetBoardCommand { get; private set;}
private void MoveToken()
{
var token = CurrentToken;
var location = ActiveLocation;
var cmd = new PlaceTokenCommand(token, location);
_board.PlaceToken(cmd);
}
private bool CanMoveToken()
{
//?
}
}
但是作为@Enigmativity,评论中的请求,没有MCVE,很难提供合理的帮助。
最后请注意,虽然Rx是功能性的并且它是反应性的,但是狂热者会反对将Rx视为 FRP (参见Conal,Behaviors等)。