对于大部分对象来说,我们只需要简单地调用一个构造函数即可完成实例化,但是有一些对象的创建涉及大量的步骤,或者参数列表非常长,例如说,我们组合几个零散的字符串到一个新的字符串,或者是一个构造函数有十个参数的类,这种情况下实例化一个对象就显得不那么高效了。所以,我们需要一个解决方案来允许用户一点一点地构造起整个对象,这就是建造者模式。
建造者模式就是当一个对象十分复杂需要分段构造时,我们应该提供一个接口来简单地构造这个对象。
var hello = "hello";
var sb = new StringBuilder();
sb.Append("<p>");
sb.Append(hello);
sb.Append("</p>");
Console.WriteLine(sb);
var words = new[] {"hello", "world"};
sb.Clear();
sb.Append("<ul>");
foreach (var word in words)
{
sb.Append("\n ");
sb.AppendFormat("<li>{0}</li>", word);
}
sb.Append("\n");
sb.Append("</ul>");
Console.WriteLine(sb);
<p>hello</p>
<ul>
<li>hello</li>
<li>world</li>
</ul>
如果要构建一个 html 文本,我们使用 string就会显得很麻烦,但是使用 StringBuilder 就非常简单,StringBuilder 提供了很多方便控制格式的方法,让我们构建字符串更加简单,这就是建造者模式。
上例中的 Html 文本似乎还不够简便,所以我们在使用 StringBuilder 的基础上封装一个 HtmlElement 和 HtmlBuilder 来帮助我们快速构建 Html 文本:
public class HtmlElement
{
public string Name { get; set; }
public string Text { get; set; }
public List<HtmlElement> Elements = new List<HtmlElement>();
private const int IndentSize = 4;
public HtmlElement()
{
}
public HtmlElement(string name, string text)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
Text = text ?? throw new ArgumentNullException(nameof(text));
}
private string ToStringImpl(int indent)
{
var sb = new StringBuilder();
var i = new string(' ', indent * IndentSize);
sb.AppendLine($"{i}<{Name}>");
if (!string.IsNullOrWhiteSpace(Text))
{
sb.Append(new string(' ', (indent + 1) * IndentSize));
sb.AppendLine(Text);
}
foreach (var element in Elements)
{
sb.Append(element.ToStringImpl(indent + 1));
}
sb.AppendLine($"{i}</{Name}>");
return sb.ToString();
}
public override string ToString()
{
return ToStringImpl(0);
}
}
public class HtmlBuilder
{
private readonly string _rootName;
private HtmlElement _root = new HtmlElement();
public HtmlBuilder(string rootName)
{
_rootName = rootName;
_root.Name = rootName;
}
public void AddChild(string childName, string ChildText)
{
var e = new HtmlElement(childName, ChildText);
_root.Elements.Add(e);
}
public override string ToString()
{
return _root.ToString();
}
public void Clear()
{
_root = new HtmlElement {Name = _rootName};
}
}
StringBuilder 类在构建字符串时还会返回自身,也就是说 StringBuilder 支持链式调用,我们也可以为我们的 HtmlBuidler 添加支持。
public HtmlBuilder AddChild(string childName, string ChildText)
{
var e = new HtmlElement(childName, ChildText);
_root.Elements.Add(e);
return this;
}
我们将void 改成 HtmlBuilder 并在执行结束时返回自身,这样我们就可以进行链式调用了: