(嵌套?)多次调度 [访客模式]

Posted

技术标签:

【中文标题】(嵌套?)多次调度 [访客模式]【英文标题】:(Nested?) Multiple Dispatch [Visitor Pattern] 【发布时间】:2012-03-09 16:33:46 【问题描述】:

我的应用程序架构遇到了障碍。我刚刚开始使用访问者模式在运行时我不知道的抽象对象上执行特定的算法。 我的问题是我的算法也依赖于嵌套抽象类型的类型。

让我来说明一下我的意思:

我有一个抽象的 DataSource 类。由此我实现了具体的 DataSourceReference 和 DataSourceExplicit 类。我还有一个抽象的报告类(反序列化的元数据),我从中实现了具体的报告类 ReportTypeA 和 ReportTypeB。创建这些对象时,它们的 DataSource 可以是任何扩展的 DataSource 类。

我需要 both,即实际的 Report 类型和 DataSource 类型,以便我可以相应地执行。我可以使用访问者模式获得一致的报告类型,但不知道如何为 DataSource afterwards/also 执行相同的操作。

访问 Report 后我无法访问 DataSource,因为我会丢失 Report 的具体类型(因为您必须让它接受基本的 Report 类型:Accept(SomeDataSourceVisitor d, MetaReport m) - 或重载每个可能的报告类型,这违背了访问者模式的目的。看到我的问题了吗?

有什么想法吗?我不想使用动态,因为它不需要新报告类型的开发人员确保调度程序(访问者)支持新报告。

当前代码:

public abstract class DataSource



public class DataSourceReference : DataSource

    // reference thing(s)


public class DataSourceExplicit : DataSource

    // explicit thing(s)


public abstract class Report

    // some shared Report attribute(s)
    // ...

    public DataSource DataSource  get; set; 

    public abstract FinalReport Execute(IReportExecutionDispatcher d);


public class ReportA : Report

    // ReportA specific attribute(s)
    // ...

    public override Execute(IReportExecutionDispatcher d)
    
        d.ExecuteReport(this);
    


public class ReportB : Report

    // ReportB specific attribute(s)
    // ...

    public override Execute(IReportExecutionDispatcher d)
    
        d.ExecuteReport(this);
    


public interface IReportExecutionDispatcher

    FinalReport ExecuteReport(ReportA);
    FinalReport ExecuteReport(ReportB);

【问题讨论】:

你能提供更多关于算法的细节吗?为什么你需要知道具体的类?为什么 DataSource \ Report 上的多态实例方法还不够? 我需要知道具体的报告类类型,因为它们的报告特定属性。根据这些属性,您将相应地执行。我们需要知道它包含的数据源的类型,以便获取它引用的数据提供者/或其他类型的数据源。 【参考方案1】:

在搜索互联网时也发现了这一点。在这里发布以供将来参考和讨论。

Selective Visitor Pattern

在以下情况下使用选择性访问者模式

您使用的编程语言支持多分类(最好是方法重载)。 您想对对象中的 (可能是不同种类的)元素执行不同种类的操作 结构。 您希望避免使用与其基本职责无关的操作污染元素类。 您希望能够在不影响现有设计的情况下轻松地向结构中添加新类型的元素。

【讨论】:

【参考方案2】:

您希望拥有 N * M 个方法,其中 N 是报表类型的计数,M 是数据源类型的计数。我认为这里正确的方法是将它们分成 N 种类型,每种类型都有 M 种方法,加上一个辅助类型,这有助于我们迈出调度的第一步。像这样的:

public interface IDataSourceExecutionDispatcher

    FinalReport ExecuteReport(DataSourceExplicit dataSource);
    FinalReport ExecuteReport(DataSourceReference dataSource);


public interface IReportExecutionDispatcher

    IDataSourceExecutionDispatcher GetDataSourceDispatcher(ReportA report);
    IDataSourceExecutionDispatcher GetDataSourceDispatcher(ReportB report);


public class ReportExecutionDispatcher: IReportExecutionDispatcher

    public IDataSourceExecutionDispatcher GetDataSourceDispatcher(
         ReportA report)
    
        return new ReportADataSourceExecutionDispatcher(report);
    

    public IDataSourceExecutionDispatcher GetDataSourceDispatcher(
         ReportB report)
    
        return new ReportBDataSourceExecutionDispatcher(report);
    


class ReportADataSourceExecutionDispatcher : IDataSourceExecutionDispatcher

    public ReportADataSourceExecutionDispatcher(ReportA report)
    
        // save the report into a field
    

    public FinalReport ExecuteReport(DataSourceExplicit dataSource)
    
        // use saved report A and explicit dataSource here
    

    public FinalReport ExecuteReport(DataSourceReference dataSource)
    
        // similar, but with reference dataSource
    


class ReportBDataSourceExecutionDispatcher : IDataSourceExecutionDispatcher

     // similar to ReportA dispatcher, except it takes ReportB in constructor

现在我们只需要修改我们的数据源以接受新的调度程序:

public abstract class DataSource

    public abstract FinalReport Execute(IDataSourceExecutionDispatcher d);


public class DataSourceReference : DataSource

    public override FinalReport Execute(IDataSourceExecutionDispatcher d)
    
        return d.ExecuteReport(this);
    

还有修改报告:

public abstract class Report

    public DataSource DataSource  get; set; 

    public abstract FinalReport Execute(IReportExecutionDispatcher d);


public class ReportA : Report

    public override FinalReport Execute(IReportExecutionDispatcher d)
    
        var dispatcher = d.GetDataSourceDispatcher(this);
        return DataSource.Execute(dispatcher);
    

【讨论】:

干得好!我最终需要更深入地了解兔子洞,因为我需要知道 DataSourceReferences 引用的提供者的类型。例如:我有一个扩展基本报表的 PivotReport,它包含一个指向“provider1”的 DataSourceReference,此时我有我的 PivotReport 并且知道它有一个 DataSourceReference,我还需要知道提供程序是 OlapProvider 还是DatabaseProvider 所以我可以生成正确的查询(基于数据透视属性),发送给提供者解析输出并返回最终报告。 问题:我使用 PivotReport 而不是 OlapPivotReport/DBPivotReport 是不是太过分了?这些将包含相同的属性(rows/col/value/filters/source-(table/cube-name))并且使每个单独的类是没有意义的。例如,只要字段和源名称相同,我就可以在数据库上执行我的 Olap 数据透视 - 为什么不呢?

以上是关于(嵌套?)多次调度 [访客模式]的主要内容,如果未能解决你的问题,请参考以下文章

没有访客模式的动态调度

为许多类似功能实现多次调度的有效方法

使用泛型进行多次调度

如何防止云调度器多次触发一个函数?

为啥嵌套的 using 块会多次处理对象? [复制]

嵌套选择的内部连接 ​​- 多次使用同一列时