Использование делегатов для упрощения безопасного синтаксического дерева типов в C # (2.0), в настоящее время использующего шаблон Visitor. - PullRequest
2 голосов
/ 03 декабря 2009

У меня есть некоторый «ввод», который анализируется несколькими различными способами.

Я пытаюсь абстрагировать код синтаксического анализа от различных нижестоящих механизмов, которым требуется анализируемый результат (некоторые части будут отображать его, другие части фактически его выполняют и т. Д. И т. П.).

Пока у меня есть это:

interface IParsedNode
{
    // Visits this node, returns whatever the appropriate IVisitor function
    // returned.
    T Visit<T>(IVisitor<T> visitor);
}

interface IVisitor<T>
{
    T Load(Aspect aspect);
    T LoadFile(string group, string filename);
    T Process(PluginProperties pluginProperties, IParsedNode input);
    T Aggregate(List<IParsedNode> children);
    T Rename(string newName, string oldName, IParsedNode input);
    T Filter(string keep, IParsedNode input);
    T Cache(IParsedNode input);
}

class Cached : IParsedNode
{
    private readonly IParsedNode input;

    public Cached(IParsedNode input)
    {
        this.input = input;
    }
    #region IParsedNode Members
    public T Visit<T>(IVisitor<T> visitor)
    {
        return visitor.Cache(input);
    }
    #endregion
}

class Filter : IParsedNode
{
    private readonly string keep;
    private readonly IParsedNode input;

    public Filter(string keep, IParsedNode input)
    {
        this.keep = keep;
        this.input = input;
    }

    #region IParsedNode Members
    public T Visit<T>(IVisitor<T> visitor)
    {
        return visitor.Filter(keep, input);
    }
    #endregion
}

и т.д.

Как видите, это позволяет мне иметь полностью "абстрактное" дерево разбора, которое также является типобезопасным. Мне также нравится тот факт, что все неизменно.

Я абстрагируюсь от типа «T», потому что различные последующие системы, в свою очередь, будут создавать свои конкретные графы из абстрактного дерева разбора.

Например, вот одна реализация IVisitor:

// Used to convert the abstract tree into an actual tree that can then be post-processed.
class NodeTreeBuilder : IVisitor<Node>
{
    private readonly NodeFactory nodeFactory;

    public NodeTreeBuilder(NodeFactory nodeFactory)
    {
        this.nodeFactory = nodeFactory;
    }

    #region IVisitor<Node> Members
    public Node Load(Aspect aspect)
    {
        return nodeFactory.CreateRaw(aspect);
    }

    public Node LoadFile(string group, string filename)
    {
        return nodeFactory.CreateFile(group, filename);
    }

    public Node Process(PluginProperties pluginProperties, IParsedNode input)
    {
        ProcessInfo processInfo = new ProcessInfo();
        processInfo.AssemblyPath = pluginProperties.AssemblyPath;
        processInfo.ClassName = pluginProperties.ClassName;
        processInfo.Config = new PluginConfig(pluginProperties.Config, pluginProperties.HashConfig, pluginProperties.DeltaType);
        PluginInfo pluginInfo = Registry.CreatePluginInfo(pluginProperties.Id, processInfo);
        return nodeFactory.CreatePostProcess(pluginInfo, input.Visit(this), pluginProperties.RunOnEmpty);
    }

    public Node Aggregate(List<IParsedNode> children)
    {
        Node[] convertedChildren = children.ConvertAll<Node>(delegate(IParsedNode child) { return child.Visit(this); }).ToArray();
        return nodeFactory.CreateAggregated(convertedChildren);
    }

    public Node Rename(string newName, string oldName, IParsedNode input)
    {
        return nodeFactory.Rename(oldName, newName, input.Visit(this));
    }

    public Node Filter(string keep, IParsedNode input)
    {
        return nodeFactory.Filter(keep, input.Visit(this));
    }

    public Node Cache(IParsedNode input)
    {
        return input.Visit(this).Cache(true);
    }
    #endregion
}

Для реальных конкретных реализаций IVisitor все это работает лучше, чем я смел надеяться.

Тем не менее, реализация самого IParsedNode (как вы видели вначале), оказывается немного утомительной. Я предоставляю целую кучу реализаций интерфейса, которые содержат только один метод .... что заставило меня задуматься о том, что, возможно, я мог бы использовать делегат для уменьшения раздувания:

class ParsedNode : IParsedNode
{
    delegate T NodeType<T>(IVisitor<T> visitor);

    private readonly NodeType nodeType;

    public ParsedNode<T>(NodeType<T> nodeType)
    {
        this.nodeType = NodeType;
    }

    public T  Visit<T>(IVisitor<T> visitor)
    {
        return nodeType(visitor);
    }
}

Но вышесказанное не компилируется. Нет ли способа для меня реализовать IParsedNode с точки зрения какого-то общего делегата? Было бы хорошо, если бы был способ заставить это работать, поскольку вещи были бы менее многословны.

Возможно, если бы интерфейс IParsedNode был просто делегатом, это можно было бы заставить работать?

1 Ответ

1 голос
/ 03 декабря 2009

Я рекомендую прочитать статью Джудит Бишоп (автор C # 3.0 Design Patterns ) под названием Об эффективности шаблонов проектирования, реализованных в C # 3.0 .

Она специально обращается к шаблону Visitor с использованием делегатов и проходит базовую реализацию (по крайней мере, алгоритмически). Ее реализация очень быстрая и достаточно гибкая.

Это моя любимая реализация шаблона Visitor в C #, на сегодняшний день.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...