将结果推送到构建器并在构建器完成时收到通知

问题描述

我第一次尝试通过 System.Reactive 使用 observable。

我希望将生成的 3 个特征的结果推送到同一个 ReportBuilder,然后在完成时通知调用者,以便调用者可以将生成Report 传递给另一个服务。

我面临一些问题:

  1. 如果我有一个观察者实现了多种类型的 IObserver,将调用 OnCompleted 方法来完成第一个观察到的序列,不是吗?如何观察多个不同类型的序列直到全部完成?

  2. 注册我的订阅后,执行流程是返回到调用者对象还是仅在调用 OnCompleted 之后?这个我不太清楚。

  3. 由于构建器既是可观察者又是观察者,我的实现对我来说似乎很尴尬,实现既是可观察者又是观察者的对象的正确方法是什么?

  4. 我是否应该合并我的 3 个 observable 并使特征异步以在产生结果时使用它们?如果是这样,如何,因为我的收藏是不同类型的?此外我会消耗三个任务结果的序列,所以我仍然会一个一个地消耗特征,不是吗?

这是将我的想法写成代码的尝试,但除了提到的问题之外,与直接调用和延迟执行相比,实现似乎没有太多好处。

public interface IFeatureC
{
    IEnumerable<ModelC> ExecuteFeature();
}

public interface IFeatureB
{
    IEnumerable<ModelB> ExecuteFeature();
}

public interface IFeatureA
{
    IEnumerable<ModelA> ExecuteFeature();
}


public class ModelA { } 
public class ModelB { } 
public class ModelC { }


public class Report
{
    public Report()
    {
        ReportElements = new List<ReportElement>();
    }
    public List<ReportElement> ReportElements { get; }
}



public class ReportElement
{
    public ReportElement(ModelA item)
    {
        // DoSomething
    }

    public ReportElement(ModelB item)
    {
        // DoSomething
    }

    public ReportElement(ModelC item)
    {
        // DoSomething
    }
}

public interface IOtherService
{
    void DoSomething(Report report);
}



public class orchestrator : IObserver<Report>
{
    private readonly ReportBuilder _builder;
    private readonly IOtherService _otherService;
    private readonly IFeatureA _featureA;
    private readonly IFeatureB _featureB;
    private readonly IFeatureC _featureC;
    private readonly ILogger _logger

    public orchestrator(IFeatureA featureA,IFeatureB featureB,IFeatureC featureC,ReportBuilder builder,IOtherService otherService,ILogger logger)
    {
        _featureA = featureA;
        _featureB = featureB;
        _featureC = featureC;
        _builder = builder;
        _otherService = otherService;
        _logger = logger;
    }

    public void Run()
    {
        IObservable<ModelA> resultAObservable = _featureA.ExecuteFeature().ToObservable();  
        IObservable<ModelB> resultBObservable = _featureB.ExecuteFeature().ToObservable();
        IObservable<ModelC> resultCObservable = _featureC.ExecuteFeature().ToObservable();

        resultAObservable.Subscribe(_builder);
        resultBObservable.Subscribe(_builder);
        resultCObservable.Subscribe(_builder);

        // Do I need to wait until OnCompleted is called ?
    }

    public void OnCompleted()
    {
        _logger.information("Report pushed");
    }

    public void OnError(Exception error)
    {
        logger.Error(error);
        throw error;
    }

    public void OnNext(Report report)
    {
        _otherService.DoSomething(report);
    }
}

public class ReportBuilder : IObserver<ModelA>,IObserver<ModelB>,IObserver<ModelC>
{
    private readonly IObserver<Report> _reportObserver;
    private Report Report { get; } = new Report();
    private readonly IObservable<Report> _observableReport;

    public ReportBuilder(IObserver<Report> reportObserver)   // Is there a better than passing the caller ?
    {
        _reportObserver = reportObserver;
        _observableReport = Observable.Return(Report);  
    }
    public void OnCompleted()  // This will get called by the first finished sequence and I will miss some observed elements ?
    {
        _observableReport.Subscribe(_reportObserver);
    }

    public void OnError(Exception error)
    {
        throw error;
    }

    public void OnNext(ModelA value)
    {
        Report.ReportElements.Add(new ReportElement(value));
    }

    public void OnNext(ModelB value)
    {
        Report.ReportElements.Add(new ReportElement(value));
    }

    public void OnNext(ModelC value)
    {
        Report.ReportElements.Add(new ReportElement(value));
    }
}

解决方法

如果我有一个观察者实现了多种类型的 IObserver,那么将调用 OnCompleted 方法来完成第一个观察到的序列,不是吗?

实现 IObservable<T>IObserver<T> 的情况非常罕见 - 通常最好只使用现有的运算符和类型。如果你自己动手,很难把这些东西弄好 - 正如你发现的那样。

如何观察多个不同类型的序列直到全部完成?

不要实现这样的类。

注册我的订阅后,执行流程是返回到调用者对象还是仅在调用 OnCompleted 之后?这个我不太清楚。

执行通常返回给调用者,但这取决于可观察对象。例如,Observable.Return(42).Subscribe(x => Console.WriteLine(x)) 在当前线程上执行,因此它将在返回给调用者之前完成可观察对象。这个问题会有所帮助:What are the default Schedulers for each observable operator?

由于构建器既是可观察者又是观察者,我的实现对我来说似乎很尴尬,实现既是可观察者又是观察者的对象的正确方法是什么?

你也没有实施。

我是否应该合并我的 3 个 observable 并使特征异步以在生成结果时使用它们?

是和否。合并,但不要担心异步。 Observable 很好。

如果是这样,如何,因为我的收藏是不同的类型?

使用 Select 使它们成为一种类型。

此外我会消耗三个任务结果的序列,所以我仍然会消耗一个又一个特征,不是吗?

没有。您可以一次完成所有操作。

以下是您应该如何实现所有代码:

public IObservable<Report> Run(IFeatureA featureA,IFeatureB featureB,IFeatureC featureC) =>
    Observable
        .Merge(
            featureA.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),featureB.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),featureC.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)))
        .ToArray()
        .Select(xs =>
        {
            var report = new Report();
            report.ReportElements.AddRange(xs);
            return report;
        });

就是这样。


或者,您可以在不失去 Rx 优势的情况下使其同步或异步:

public Report Run(IFeatureA featureA,featureC.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)))
        .ToArray()
        .Select(xs =>
        {
            var report = new Report();
            report.ReportElements.AddRange(xs);
            return report;
        })
        .Wait();

public async Task<Report> RunAsync(IFeatureA featureA,IFeatureC featureC) =>
    await Observable
        .Merge(
            featureA.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)),featureC.ExecuteFeature().ToObservable().Select(x => new ReportElement(x)))
        .ToArray()
        .Select(xs =>
        {
            var report = new Report();
            report.ReportElements.AddRange(xs);
            return report;
        });

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...