Как соблюдать разные концепции интерфейса отдельно? - PullRequest
2 голосов
/ 27 ноября 2010

По сути, я пытаюсь создать общий интерфейс симулятора, который действует как потерянная связь между моделью и пользовательским интерфейсом, который выступает в качестве представления. Мой интерфейс симулятора выглядит так:

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

'Коллекции - это тип сбора данных,' Элемент - это тип отдельного элемента данных, а 'Значение - это тип его фактического значения (например, , и т. д.). Теперь линия

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

По сути, я хочу, чтобы этот интерфейс говорил, что и Коллекция, которая служит данными, на которых выполняется симуляция, и сам Симулятор должны наблюдаться отдельно. В конце я хочу, чтобы часть моего пользовательского интерфейса отображала текущие данные (например, матрицу), а другая часть - для отображения и управления симулятором, с некоторыми кнопками, такими как «Run», «Stop», «Reset» и т. Д. Поскольку симулятор также может быть остановлен другими способами, кроме простого нажатия кнопки (например, после достижения какого-то определенного состояния, генерации и т. Д.), Этот элемент управления также нуждается в обновлениях из симулятора, но не в отношении состояния данных, но сам симулятор.

Невозможно сделать интерфейс коллекции, который я бы написал, наблюдаемым, поскольку эта коллекция не будет изменена во время моделирования, но будет преобразована с помощью функции, и преобразование создаст новую коллекцию, которую затем сохранит симулятор ( и уведомляет наблюдателей о сборе).

Что мне делать?

  • нарушить концепцию неизменности и всегда держите одну и ту же коллекцию (в условия идентичности, не содержащиеся значения), который просто меняется с течением времени вместо производства новых, модифицированных коллекции?
  • Разорвать связь и получить мой пользователь интерфейс знает точное реализация, которая будет вне интерфейса, предоставьте второй значит наблюдать за симулятором сам? Есть весь пользовательский интерфейс компоненты, которые требуют обновления от симулятор наблюдать за всем вещь, а не только соответствующие данные?
  • Создать отдельный интерфейс для наблюдать за коллекцией и иметь мой реализация реализации симулятора оба интерфейса?
  • Что-то еще?

Ответы [ 2 ]

3 голосов
/ 27 ноября 2010

Рассматривали ли вы разоблачение наблюдаемых через свойства, вроде традиционных событий?

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

Это позволяет потребителям четко указывать, какое поведение они наблюдают.

0 голосов
/ 02 декабря 2010

Итак, я сделал эту простую реализацию IObservable, из которой затем выставляю открытые свойства в соответствии с определением интерфейса, предоставленным dahlbyk. Я нашел основную идею для этой реализации на этом сайте и немного обобщил оттуда:

  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>. Я думаю, что это хорошо с точки зрения слабой связи и все же допускает очень прямое использование на любом конце пары наблюдатель / наблюдаемый.

P.S .: Именно поэтому я люблю F # - вся эта конструкция была бы полным беспорядком для реализации на языке, подобном C ++; но здесь я могу просто передать функцию в другую функцию, чтобы применить ее ко всем наблюдателям. :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...