简体   繁体   English

流利的生成器模式返回根类型

[英]Fluent Builder pattern which returns root type

I want to create builder for my purpose, with such call chain: 我想使用以下调用链为我的目的创建构建器:

User user = new CommonBuilder(new UserNode()).Root //generic parameter, currently is User
    .Group.Group.Folder.Build();

Here is the code, which I use: 这是我使用的代码:

public abstract class AbstractNode
{
    public Guid Id { get; } = Guid.NewGuid();
}

public abstract class AbstractNode<T> where T : AbstractNode<T>
{

}

public class CommonBuilder<T> where T : AbstractNode<T>
{
    public T Root { get; private set; }
    public CommonBuilder(T root)
    {
        Root = root;
    }
}

public class UserNode : AbstractNode<UserNode>
{
    private GroupNode _group;
    public GroupNode Group
    {
        get
        {
            if (_group is null)
            {
                _group = new GroupNode();
            }
            return _group;
        }
    }
}

public class GroupNode : AbstractNode<GroupNode>
{
    private GroupNode _group;
    public GroupNode Group
    {
        get
        {
            if (_group is null)
            {
                _group = new GroupNode();
            }
            return _group;
        }
    }

    private FolderNode _folder;
    public FolderNode Folder
    {
        get
        {
            if (_folder is null)
            {
                _folder = new FolderNode();
            }
            return _folder;
        }
    }
}

public class FolderNode : AbstractNode<FolderNode>
{

}

The problem is in the Build() method, which need to return Root from CommonBuilder , not the File . 问题出在Build()方法中,该方法需要从CommonBuilder而不是File返回Root

Where must I place Build() method, which must be always called at the end of a chain, which returns Root of a builder? 我必须将Build()方法放在哪里,该方法必须始终在链的末尾调用,该方法返回生成器的Root

In case when it's required to make a chain the same object should be returned, even as another interface check first and second examples of implementation Builder with Fluent intefaces 如果需要制作一条链,则应该返回相同的对象,即使另一个接口检查带有Fluent接口的实施生成器的第一第二个示例

I've tried to implement your case to fit the role, check if it will fits your requirements: 我已尝试实现您的情况以适合该角色,请检查它是否适合您的要求:

public interface IGroup<T>
{
    IGroup<T> Group { get; }
    IFolder<T> Folder { get; }
}
public interface IFolder<T>
{
    T Build();
}

Builder implements all required interfaces. Builder实现所有必需的接口。 And returns itself in each call. 并在每次调用中返回自身。 In general you can put Build method in the builder itself and call it separately after the end of chain execution. 通常,您可以将Build方法放入Build器本身,并在链执行结束后分别调用它。

public class CommonBuilder<T> : IGroup<T>, IFolder<T> where T: INode, new()
{
    private T _root = new T();

    public T Build()
    {
        return _root;
    }

    public IGroup<T> Group
    {
        get
        {
            _root.MoveToGroup();
            return this;
        }
    }

    public IFolder<T> Folder
    {
        get
        {
            _root.MoveToFolder();
            return this;
        }
    }
}

Because of generics it's required to set some limitations on generic parameter which is done with INode interface 由于泛型,需要对通过INode接口完成的泛型参数设置一些限制

public interface INode
{
    void MoveToGroup();
    void MoveToFolder();
}

Testing user object 测试用户对象

public class User : INode
{
    public StringBuilder Path { get; } = new StringBuilder();

    public void MoveToFolder()
    {
        Path.AppendLine("Folder");
    }

    public void MoveToGroup()
    {
        Path.AppendLine("Group");
    }
    public override string ToString()
    {
        return Path.ToString();
    }
}

And the call will looks like 通话看起来像

var user = new CommonBuilder<User>().Group.Group.Folder.Build();

EDIT 编辑

Maybe as a the first stage it makes sence to get rid of Fluent interfaces and implement logic using just a Builder: 也许作为第一步,它有必要摆脱Fluent接口并仅使用Builder来实现逻辑:

public class FolderNode : INode<Folder>
{
    private readonly Folder _folder = new Folder();
    public Folder Build()
    {
        return _folder;
    }
    public void AppendGroup()
    {
        _folder.Path.AppendLine("Folder Group");
    }
    public void AppendFolder()
    {
        _folder.Path.AppendLine("Folder Folder");
    }
}

public class UserNode : INode<User>
{
    private readonly User _user = new User();
    public User Build()
    {
        return _user;
    }
    public void AppendGroup()
    {
        _user.Path.AppendLine("Group");
    }
    public void AppendFolder()
    {
        _user.Path.AppendLine("Folder");
    }
}

public class CommonBuilder<T, TNode> where TNode : INode<T>
{
    private readonly TNode _root;

    public CommonBuilder(TNode root)
    {
        _root = root;
    }

    public T Build()
    {
        return _root.Build();
    }

    public CommonBuilder<T, TNode> Group {
        get
        {
            _root.AppendGroup();
            return this;
        }
    }

    public CommonBuilder<T, TNode> Folder {
        get
        {
            _root.AppendFolder();
            return this;
        }
    }

}

public interface INode<out T>
{
    T Build();
    void AppendGroup();
    void AppendFolder();

}

public class Folder
{
    public StringBuilder Path { get; } = new StringBuilder();

    public override string ToString()
    {
        return Path.ToString();
    }
}

public class User
{
    public StringBuilder Path { get; } = new StringBuilder();

    public override string ToString()
    {
        return Path.ToString();
    }
}

Usage: 用法:

var user = new CommonBuilder<User, UserNode>(new UserNode()).Group.Group.Folder.Build();
var folder = new CommonBuilder<Folder, FolderNode>(new FolderNode()).Group.Folder.Group.Folder.Build();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM