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

发布于 2025-01-06 16:45:16 字数 1569 浏览 1 评论 0原文

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

让我说明我的意思:

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

我需要两者,即实际的报表类型和数据源类型,以便我可以相应地执行。我可以使用访问者模式获取一致的报告类型,但不知道如何对数据源之后/也执行相同的操作。

访问报表后我无法访问数据源,因为我会丢失报表的具体类型(因为您必须让它接受基本报表类型: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);
}

I've come to a road block in my application architecture. I've just started using the visitor pattern to execute specific algos on abstract objects of which type I don't know at runtime. My problem is that my algo also depends on the type of a nested abstract type as well.

Let me illustrate what I mean:

I have an abstract DataSource class. From this I implement concerete DataSourceReference and DataSourceExplicit classes. I also have an abstract Report class (the deserialized metadata), from which I implement concrete Report classes ReportTypeA and ReportTypeB. When these objects are created, their DataSource may be any extending DataSource class.

I need both, the actual Report type and DataSource type so I can execute accordingly. I can get the concerte Report type using the visitor pattern, but don't know how to do the same for DataSource afterwards/also.

I can't visit DataSource after visiting Report, because I'd lose the concrete type of Report (as you would have to let it accept the base Report type: Accept(SomeDataSourceVisitor d, MetaReport m) - or overload for every possible Report type, which defeats the purpose of Visitor Pattern. See my problem?

Any ideas? I'd like to stay away from using dynamic, as it wouldn't require developers of new Report types to make sure the dispatcher(visitor) supports the new Report.

Current Code:

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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

撩发小公举 2025-01-13 16:45:16

在网上搜索的时候也发现了这个。发布在这里以供将来参考和讨论。

选择性访客模式

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

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

Also found this while scouring the interwebs. Posting it here for future reference and discussion.

Selective Visitor Pattern

Use the Selective Visitor pattern when

  • you're using a programming language that supports multiple classification (and ideally method overloading).
  • you want to perform different kinds of operations on the (potentially disparate kinds of) elements in an object
    structure.
  • you want to avoid contaminating the element classes with operations that do not relate to their essential responsibilities.
  • you want to be able to easily add new kinds of elements to the structure without compromising their existing designs.
梦在夏天 2025-01-13 16:45:16

您想要有 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);
    }
}

You want to have N * M methods, where N is the count of report types and M is the count of data source type. I think the right approach here is to split them into N types, each with M methods, plus one helper type, that helps us make the first step in dispatching. Something like this:

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
}

Now we just need to modify our data sources to accept the new dispatcher:

public abstract class DataSource
{
    public abstract FinalReport Execute(IDataSourceExecutionDispatcher d);
}

public class DataSourceReference : DataSource
{
    public override FinalReport Execute(IDataSourceExecutionDispatcher d)
    {
        return d.ExecuteReport(this);
    }
}

And also modify reports:

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);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文