将一个复杂对象的构件与它的表示分离,使得同样的构建过程可以创建不同的表述。

DotNet Framework源代码中的模式(八)——Builder(生成器模式) 

建造者(Builder)角色:给出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者(ConcreteBuilder)角色。具体建造者类必须实现这个接口所要求的方法:一个是建造方法,另一个是结果返还方法。

具体建造者(Concrete Builder)角色:担任这个角色的是于应用程序紧密相关的类,它们在应用程序调用下创建产品实例。这个角色主要完成的任务包括:实现Builder角色提供的接口,一步一步完成创建产品实例的过程;在建造过程完成后,提*品的实例。

指导者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。导演者并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者对象。

产品(Product)角色:产品便是建造中的复杂对象。

 

以以.NET Framework 2.0 System.Text. StringBuilder为例

DotNet Framework源代码中的模式(八)——Builder(生成器模式) 

System.Text. StringBuilder

 

 StringBuilder : ISerializable
{
    //DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)
    internal IntPtr m_currentThread = Thread.InternalGetCurrentThread();
    
internal int m_MaxCapacity = 0
    
internal volatile String m_StringValue = null;
    
//DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)
    public StringBuilder() : this(DefaultCapacity)
    {

    }
    
//DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)
    public StringBuilder Append(String value)
    {
        
//If the value being added is null, eat the null
        
//and return. 
        if (value == null)
        {
            
return this;
        }

        IntPtr tid;
        
// hand inlining of GetThreadSafeString 
        String currentString = m_StringValue;
        tid 
= Thread.InternalGetCurrentThread();
        
if (m_currentThread != tid)
            currentString 
= String.GetStringForStringBuilder(currentString, currentString.Capacity);

        
int currentLength = currentString.Length;

        
int requiredLength = currentLength + value.Length;

        
if (NeedsAllocation(currentString, requiredLength))
        {
            String newString 
= GetNewString(currentString, requiredLength);
            newString.AppendInPlace(value, currentLength);
            ReplaceString(tid, newString);
        }
        
else
        {
            currentString.AppendInPlace(value, currentLength);
            ReplaceString(tid, currentString);
        }

        
return this;
    }
    
//DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)
    public StringBuilder Append(int value)
    {
        
return Append(value.ToString(CultureInfo.CurrentCulture));
    }
    
//DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)
    public override String ToString()
    {
        
//
        
// We assume that their read of m_currentThread will always occur after read of m_StringValue.
        
// If these reads get re-ordered then it is possible to get a currentString owned by some other 
        
// (mutating) thread and yet think, according to currentThread, that such was not the case.
        
// This is acheived by marking m_StringValue as volatile. 
        
// 
        String currentString = m_StringValue;
        IntPtr currentThread 
= m_currentThread;

        
//
        
// Note calling ToString the second time or from a different thread will cause allocation of a new string.
        
// If we do not make a copy if currentThread is IntPtr.Zero, we will have following race: 
        
//
        
// (1) Thread T1 completes a mutation of the string and will become the owner. 
        
// T1 then starts another mutation operation and 
        
// A thread interleaving happens at this point.
        
// (2) Thread T2 starts a ToString operation.  T2 reads m_StringValue into its local currentString variable. 
        
// A thread interleaving happens at this point.
        
// (3) Thread T3 finshes a mutation of the string in the StringBuilder , performing the ReplaceString call.
        
// Thread T3 then starts a ToString operation.  Assuming the string is not wasting excessive space,
        
// T3 will proceeds to call ClearPostNullChar, registers NOBODY as the owner, and returns the string. 
        
// A thread interleaving happens at this point.
        
// (4) Thread T2 resumes execution.  T2 reads m_currentThread and sees that NOBODY is the registered owner 
        
//  Assuming its currentString is not wasting excessive space, T2 will return the same string that thread T1 is 
        
//  in the middle of mutating.
        
// 
        if (currentThread != Thread.InternalGetCurrentThread())
        {
            
return String.InternalCopy(currentString);
        }

        
if ((2 * currentString.Length) < currentString.ArrayLength)
        {
            
return String.InternalCopy(currentString);
        }

        currentString.ClearPostNullChar();
        m_currentThread 
= IntPtr.Zero;
        
return currentString;
    }
}

 

System.String

 String : IComparable, ICloneable, IConvertible, IEnumerable
{
    //DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)
    
// Creates a new string with the characters copied in from ptr. If 
    
// ptr is null, a string initialized to ";<;No Object>;"; (i.e., 
    
// String.NullString) is created.
    
// 
    [CLSCompliant(false), MethodImplAttribute(MethodImplOptions.InternalCall)]
    
unsafe public extern String(char* value);
    [CLSCompliant(
false), MethodImplAttribute(MethodImplOptions.InternalCall)]
    
unsafe public extern String(char* value, int startIndex, int length);

    [CLSCompliant(
false), MethodImplAttribute(MethodImplOptions.InternalCall)]
    
unsafe public extern String(sbyte* value);
    [CLSCompliant(
false), MethodImplAttribute(MethodImplOptions.InternalCall)]
    
unsafe public extern String(sbyte* value, int startIndex, int length);

    [CLSCompliant(
false), MethodImplAttribute(MethodImplOptions.InternalCall)]
    
unsafe public extern String(sbyte* value, int startIndex, int length, Encoding enc);
    
//DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)
    
// Creates a new string from the characters in a subarray.  The new string will
    
// be created from the characters in value between startIndex and
    
// startIndex + length - 1. 
    
//
    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    
public extern String(char[] value, int startIndex, int length);

    
// Creates a new string from the characters in a subarray.  The new string will be 
    
// created from the characters in value.
    
//

    [MethodImplAttribute(MethodImplOptions.InternalCall)]
    
public extern String(char[] value);
    
//DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)

    
internal unsafe void AppendInPlace(String value, int currentLength)
    {
        
int count = value.Length;
        
int newLength = currentLength + count;

        
//DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)

        
fixed (char* dest = &this.m_firstChar)
        {
            
fixed (char* src = &value.m_firstChar)
            {
                wstrcpy(dest 
+ currentLength, src, count);
            }
            dest[newLength] 
= '\0';
        }
        
this.m_stringLength = newLength;
    }

    
//DotNet Framework源代码中的模式(八)——Builder(生成器模式)DotNet Framework源代码中的模式(八)——Builder(生成器模式)

}

 

调用代码

 Client
{
    public static void Main()
    {
        StringBuilder sb 
= new StringBuilder();
        sb.Append(
"Hello");
        sb.Append(
" World");
        sb.Append(
"!");

        String s 
= sb.ToString();
     
        Console.Write(s);     
        Console.ReadLine();
    }
}

 

由于String类型代表的是一个不可变的字符串,所以BCL提供了另一个名为System.Text.StringBuilder的类型,它允许我们有效的对字符串的字符执行动态操作,以创建一个String。从逻辑上说,StringBuilder对象中包含一个字段,它引用由Char结构构成的一个数组。StringBuilder的成员允许我们操作这个字符,有效的缩减字符串的大小或者更改字符串中的字符。如果字符串变大,超过已经分配的字符的大小,StringBuilder就会自动的分配一个全新的、更大的数组,并开始使用新的数组,前一个数组会被垃圾回收器回收。用StringBuilder对象构建好字符串之后,为了将StringBuilder的字符“转换”成一个String,只需调用StringBuilderToString方法,在内部,该方法只是返回对StringBuilder内部维护的字符串的字段的一个引用,执行效率非常快,因为它不需要进行字符数组复制。

C#volatile 关键字指示一个字段可以由多个同时执行的线程修改。声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。这样可以确保该字段在任何时间呈现的都是最新的值。可变关键字仅可应用于类或结构字段。不能将局部变量声明为 volatile

StringBuilder既是具体建造者(Builder)又是指导者(Director),最终生成一个复杂的String对象作为产品(Product)。在具体建造者只有一个的情况下,如果抽象建造者角色已经被省略掉,那么还可以省略掉指导者角色。让Builder角色自己扮演指导者与建造者双重角色。

 

以下情况应当使用建造者模式:

1.需要生成的产品对象有复杂的内部结构。

2.需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。

3.在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。

 

分类:

技术点:

相关文章: