【问题标题】:How to observe different concepts of an interface separately?如何分别观察一个接口的不同概念?
【发布时间】:2014-09-20 08:17:30
【问题描述】:

基本上,我想做的是有一个通用的模拟器界面,它充当模型和用户界面之间的丢失耦合,充当视图。我的模拟器界面是这样的:

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

'Collections是数据集合的类型,'Item是单个数据项的类型,'Value是其实际值的类型(例如,等)。现在,一行

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#.

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

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

我该怎么办?

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

【问题讨论】:

  • 为什么代码会以如此奇怪、不可预测的方式着色?除了提到的错误,这段代码是有效的 F#,如果我删除了有问题的行,它会完美编译。
  • Stack Overflow 没有语法高亮 F# 源代码正确。具体来说,它被 'a 中的 ' 弄糊涂了。

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


【解决方案1】:

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

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

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

【讨论】:

  • 好的,我终于决定走那条路,即组合,并提供 IObservable 的基本通用实现,我像你描述的那样公开了它的特性。我仍然不确定这是否是最好的解决方案,但比迄今为止我能想到的任何其他方法都要好。 ;)
  • 如果您确实提出了更好的建议,请告诉我们。 :)
  • 好吧,现在我已经有了一个现成的 IObservable 实现,这实际上还不错。基本上,我已经实现了接口加方法来激活所有注册观察者的“OnNext”、“OnError”和“OnCompleted”事件,并且只有这个类的实例。作为公共财产。我将在下面的答案中发布实现。
【解决方案2】:

我们开始吧,我做了这个 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&lt;'a&gt; 对象,而对其他人仅公开为IObservable&lt;'a&gt; 接口。我认为这在松散耦合方面很好,并且仍然允许在 Observer/Observable 对的任一端非常简单地使用。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-29
    • 2016-08-30
    • 1970-01-01
    • 2014-07-15
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    相关资源
    最近更新 更多