如何分别观察界面的不同概念?

时间:2010-11-27 11:40:38

标签: model-view-controller generics interface f#

基本上,我要做的是拥有一个通用的模拟器接口,它充当模型和用户界面之间的失去耦合,用作视图。我的模拟器界面如下所示:

type ISimulator<'Collection, 'Item, 'Value> =
  inherit System.IObservable<'Collection>
  inherit System.IObservable<ISimulator<'Collection, 'Item, 'Value>>
  abstract Start:           unit -> unit
  abstract Stop:            unit -> unit
  abstract Reset:           unit -> unit
  abstract Reset:           'Collection -> unit
  abstract Advance:         int<gen> -> unit
  abstract InitialState:    'Collection
    with get
  abstract CurrentState:    'Collection
    with get
  abstract Rule:            ('Item -> 'Value)
    with get, set
  abstract Generation:      int<gen>
    with get, set
  abstract Speed:           float<gen/sec>
    with get, set
  abstract Running:         bool
    with get

'集合是数据集合的类型,'项是单个数据项的类型,'值是其实际值的类型(例如&lt; Matrix,Cell,float&gt;,&lt; Tree,节点,字符串&gt;等)。现在,行

inherit System.IObservable<ISimulator<'Collection, 'Item, 'Value>>

产生错误:

This type implements or inherits the same interface at different generic instantiations 'System.IObservable<Interface.ISimulator<'Collection,'Item,'Value>>' and 'System.IObservable<'Collection>'. This is not permitted in this version of F#.

实际上,我希望这个界面能够说明作为模拟运行的数据的Collection和可以单独观察模拟器本身。最后,我希望我的用户界面的一部分显示当前数据(例如矩阵)和显示和控制模拟器的不同部分,有一些按钮,如“运行”,“停止”,“重置”等。由于模拟器也可能通过其他方式停止,而不仅仅是单击按钮(例如,在达到某个特定状态,生成等)之后,该控件也需要来自模拟器的更新,但不需要更新数据的状态,但模拟器本身。

我不可能创建可观察的集合接口,因为该集合在模拟过程中不会被修改,而是通过应用函数进行转换,转换会产生一个新的集合,然后模拟器会存储(并通知收集者的观察者。)

我该怎么办?

  • 打破不变性概念 始终保持相同的集合(in 身份条款,未包含在内 值)随着时间的推移而变化 而不是生产新的,修改过的 集合?
  • 打破失去耦合并拥有我的用户 界面知道确切的 外面的实施 的界面,提供第二个 意味着观察模拟器 本身?拥有所有用户界面 需要更新的组件 模拟器观察整体 事情,而不仅仅是相关数据?
  • 创建一个单独的界面 观察收藏,并拥有我的 模拟器实现工具 两个接口?
  • 别的什么?

2 个答案:

答案 0 :(得分:3)

您是否考虑过通过属性公开可观察物,有点像传统事件?

type ISimulator<'Collection, 'Item, 'Value> =
  abstract Items:           System.IObservable<'Collection>
  abstract Control:         System.IObservable<ISimulator<'Collection, 'Item, 'Value>>
  abstract Start:           unit -> unit
  ...

这允许消费者明确他们正在观察的行为。

答案 1 :(得分:0)

在这里,我做了IObservable的这个简单实现,然后我根据dahlbyk提供的接口定义公开公共属性。我在this website找到了这个实现的基本思路,并从那里概括了一点:

  open System

  type Observable<'a>() =
    let mutable _observers: List<IObserver<'a>> = []
    let Notify func =
      _observers
      |> Seq.map(fun (observer: IObserver<'a>) -> async { return func observer} )
      |> Async.Parallel
      |> Async.RunSynchronously
      |> ignore

    interface IObservable<'a> with

      member this.Subscribe (observer: IObserver<'a>) =
        _observers <- observer :: _observers
        { new IDisposable with
              member this.Dispose() =
                _observers <- _observers |> List.filter((<>) observer) }

    member this.Next value =
      Notify(fun (observer: IObserver<'a>) -> observer.OnNext value)

    member this.Error error =
      Notify(fun (observer: IObserver<'a>) -> observer.OnError error)

    member this.Completed() =
      Notify(fun (observer: IObserver<'a>) -> observer.OnCompleted)

包含此实现的实例作为属性的类只将其视为Observable<'a>对象,而对于其他所有对象,它仅作为IObservable<'a>接口公开。我认为这在松散耦合方面很好,并且仍允许在Observer / Observable对的任一端非常简单地使用。

P.S。:这也是我喜欢F#的原因 - 这个整个结构在用C ++这样的语言实现时会非常麻烦;但是在这里我可以将一个函数传递给另一个函数,以便将它应用于所有观察者。 :)