5

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

让我说明一下我的意思:

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

我需要两者,实际的 Report 类型和 DataSource 类型,以便我可以相应地执行。我可以使用访问者模式获得一致的报告类型,但不知道如何为 DataSource 做同样的事情

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

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

当前代码:

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

2 回答 2

3

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

选择性访客模式

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

  • 您正在使用支持多分类(理想情况下是方法重载)的编程语言。
  • 您想对对象结构中的(可能不同种类的)元素执行不同种类的操作。
  • 您希望避免与元素类的基本职责无关的操作污染元素类。
  • 您希望能够在不影响现有设计的情况下轻松地将新元素添加到结构中。
于 2012-02-19T17:56:44.943 回答
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);
    }
}
于 2012-02-19T12:45:19.427 回答