将复合模式应用于一般图形模型

apply composite pattern to general graph models

我想建立一个数据模型,主要由一个图组成,稍后要遍历。特殊要求:图中的节点可以是要执行的动作,也可以是带有另一个动作或容器图的容器。遍历时,必须检查当前节点是否为容器,如果是,则开始子图的遍历。

这是如何实现的?一个数据模型应该是什么样子,才能搭建出下图这样的结构?

到目前为止我得到了一些 类。有几种动作类型,因此我在这里使用一个界面

public interface IAction : IContainer
{
    public ActionType ActionType{ get; set; }
    public Name { get; set; }
}

容器只包含一个名称和一个自引用。我最初也尝试通过使用接口来完成此操作,但后来我只能访问接口属性,而不能访问容器属性

public interface IContainer
{
    public string Name { get; set; }
}

public class Container : IContainer
{
    public string Name { get; set; }
    public IContainer Content { get; set; }
}

主要的测试用法如下所示:

myJob.Content = new QuikGraph.BidirectionalGraph<IContainer, QuikGraph.Edge<IContainer>>();
myJob.Content.AddVertex(new MyCustomAction()
{
    ActionType = ActionType.MyCustomAction,
    Name = "Test"
});

myJob.Content.Vertices.First().Name // only property I could access, no action-specific ones

我做错了什么?

我认为您在 IContainer 中遗漏了一个操作(方法),该操作可从图中的任何节点调用,并且其行为符合您的预期(将调用转发到其所有封闭操作的容器和执行实际操作的动作)。

此外,与其在 Container 中保存对 IContainer 的单个引用,不如拥有一个子集合 IContainer,因为在您的图表中,一个容器有 3 个操作。

请参阅 Composite pattern 了解更多说明。

public interface IContainer
{
    string Name { get; set; }
    void DoWork();
}

public sealed class Action1 : IAction
{
    public override DoWork()
    {
        //Do the real work
    }
}

public sealed class Container : IContainer
{
    private readonly IReadOnlyList<IContainer> _children;
    public Container(IEnumerable<IContainer> children) => _children = children.ToList();
    public override DoWork()
    {
        foreach(var child in _children)
        {
            child.DoWork();
        }
    }
}

这是我想出的结果。我花了一些时间才知道如何对此建模,但最终我按照@Spotted 的建议应用了复合模式。我从 类 中完全删除了 quikgraph 库并将其外包给另一个服务,我只在我想遍历图形或检查周期等时调用它...

此外,我选择不 link 实体直接相互联系,而是通过名称,否则序列化会输出双倍的信息(节点可能被多次引用)。

public interface IContainer
{
    string Name { get; set; }
    IList<string> DependsOn { get; set; }
    void DoWork();
    void AddDependency(string name)
}

public interface IAction : IContainer
{
    // more common stuff
}

public sealed class Action1 : IAction
{
    public string Name { get; set; }
    IList<string> DependsOn { get; set; }
    public DoWork()
    {
        //Do the real work
    }
    
    public void AddDependency(string name)
    {
        this.DependsOn.Add(name);
    }
}

public sealed class Container : IContainer
{
    public string Name { get; set; }
    public IList<string> DependsOn { get; set; }
    public IList<IContainer> SubGraph { get; set; }

    public override DoWork()
    {
        foreach(var item in SubGraph)
        {
            item.DoWork();
        }
    }

    public void AddSubGraph(IList<IContainer> subGraph)
    {
        this.SubGraph = subGraph;
    }
}

谢谢你的帮助,伙计!