(Вложенный?) Многократная рассылка [Шаблон посетителя] - PullRequest
5 голосов
/ 18 февраля 2012

Я пришел к препятствию в своей архитектуре приложения.Я только начал использовать шаблон посетителя для выполнения определенных алгоритмов на абстрактных объектах того типа, который я не знаю во время выполнения. Моя проблема в том, что мой алгоритм также зависит от типа вложенного абстрактного типа.

Позвольте мне проиллюстрировать, что я имею в виду:

У меня есть абстрактный класс DataSource,Из этого я реализую конкретные классы DataSourceReference и DataSourceExplicit.У меня также есть абстрактный класс Report (десериализованные метаданные), из которого я реализую конкретные классы ReportTypeA и ReportTypeB.Когда эти объекты созданы, их DataSource может быть любым расширяющим классом DataSource.

Мне нужны оба , фактический тип отчета и тип DataSource, чтобы я мог выполнить соответственно.Я могу получить согласованный тип отчета, используя шаблон посетителя, но не знаю, как сделать то же самое для DataSource впоследствии / также .

Я не могу посетить источник данных после посещения отчета, поскольку я потеряю конкретный тип отчета (так как вы должны были бы позволить ему принять базовый тип отчета: Принять (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);
}

Ответы [ 2 ]

3 голосов
/ 19 февраля 2012

Также нашел это, прочесывая сети.Размещение его здесь для дальнейшего использования и обсуждения.

Выборочный шаблон посетителя

Используйте шаблон Выборочный посетитель, когда

  • you 'используется язык программирования, который поддерживает множественную классификацию (и в идеале перегрузку методов).
  • вы хотите выполнять различные виды операций над элементами (потенциально несопоставимые виды) в структуре объекта.
  • вы хотите избежать загрязнения классов элементов операциями, которые не связаны с их основными обязанностями.
  • вы хотите иметь возможность легко добавлять новые виды элементов в структуру, не ставя под угрозу их существующиеконструкций.
2 голосов
/ 19 февраля 2012

Вы хотите иметь 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);
    }
}
...