简体   繁体   中英

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 .

Where must I place Build() method, which must be always called at the end of a chain, which returns Root of a builder?

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

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. 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.

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

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:

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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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