《模式——工程化实现及扩展》(设计模式C# 版)《创建者模式 Builder》——“自我检验" 参考答案

转自:《模式——工程化实现及扩展》(设计模式C# 版)
http://www.cnblogs.com/callwangxiang/

 

 

自我检验

 

大型项目中,对于复杂的、资源占用大的对象在其BuildUp()过程中一般需要增加一些控制措施,目的是希望能将一些非功能性逻辑横切到各个BuildPart()步骤之间,另外也能够对最终BuildUp()的结果在反馈给客户程序前进行额外的处理。

 

请改造本章介绍的那个配合BuildStepAttributeBuilderStepDiscovery完成BuildUp()过程的创建者,为其提供每个BuildPart()之间以及BuildUp()执行前后的横切控制措施,并用单元测试验证之。

 

提示:为了实现简便,可考虑通过事件方式提供切入点。

 

 参考实现

 

1、采用事件方式作为切入点,定义对应的EventArgs

2、 实现具体的Builder类型,在执行BuildUp()方法前后和每个BuildPart()前后均抛出事件供外部类型切入

3、设计使用BuildStepAttribute定义BuildUp()步骤的类型

 这里模拟一个可能在制造过程中出次品的汽车对象,每个部件都可能在装配过程中出现错误

4、 单元测试[TestInitialize]部分定义在执行BuildUp()方法前后和每个BuildPart()前后需要切入的额外控制手段

5、单元测试验证

 

 

1、

 

/// <summary>
/// BuildUp()适用的EventArgs
/// </summary>
/// <typeparam name="T"></typeparam>
class BuilderEventArgs<T> : EventArgs
{
    
/// <summary>
    
/// 还没有进行装配的对象实例
    
/// </summary>
    public T Target { getset; }
}

/// <summary>
/// BuildPart()适用的EventArgs
/// </summary>
/// <typeparam name="T"></typeparam>
class BuilderStepEventArgs<T> : BuilderEventArgs<T>
{
    
/// <summary>
    
/// 该BuildStep的操作信息
    
/// </summary>
    public B.BuildStep Step { getset; }

    
/// <summary>
    
/// 该BuildStep正在执行第几轮循环
    
/// </summary>
    public int CurrentLoop { getset; }
}

 

 2、

 

class Builder<T>  where T : classnew()
{
    
public event EventHandler<BuilderEventArgs<T>> BeforeBuildUp;
    
public event EventHandler<BuilderEventArgs<T>> AfterBuildUp;

    
public event EventHandler<BuilderStepEventArgs<T>> BeforeBuildStepExecuting;
    
public event EventHandler<BuilderStepEventArgs<T>> AfterBuildStepExecuted;

    
public virtual T BuildUp()
    {
        var steps 
= new B.BuilderStepDiscovery().DiscoveryBuildSteps(typeof(T));
        
if (steps == nullreturn new T(); // 没有BuildPart步骤,退化为Factory模式
        var target = new T();

        
//  BuildUp() 执行前的切入点
        if (BeforeBuildUp != null)
            BeforeBuildUp(
thisnew BuilderEventArgs<T>() { Target = target });

        
foreach (var step in steps)
            
for (var i = 0; i < step.Times; i++)
            {
                
//  BuildPart() 执行前的切入点
                if (BeforeBuildStepExecuting != null)
                    BeforeBuildStepExecuting(
this,
                                                
new BuilderStepEventArgs<T>()
                                                {
                                                    Target 
= target,
                                                    Step 
= step,
                                                    CurrentLoop 
= i
                                                });

                step.Method.Invoke(target, 
null);

                
//  BuildPart() 执行后的切入点
                if (AfterBuildStepExecuted != null)
                    AfterBuildStepExecuted(
this,
                                                
new BuilderStepEventArgs<T>()
                                                {
                                                    Target 
= target,
                                                    Step 
= step,
                                                    CurrentLoop 
= i
                                                });
            }

        
//  BuildUp() 执行后的切入点
        if (AfterBuildUp != null)
            AfterBuildUp(
thisnew BuilderEventArgs<T>() { Target = target });

        
return target;
    }
}

 

 3、

 

/// <summary>
/// 这里模拟一个可能制造出次品的汽车对象
/// 每个部件都可能在装配过程中出现错误
/// </summary>
class Car
{
    
public const string WheelItem = "wheel";
    
public const string EngineItem = "engine";
    
public const string BodyItem = "body";

    
const int WheelDefectiveCycle = 9;
    
const int BodyDefectiveCycle = 11;

    
static int wheelDefectiveIndexer;
    
static int bodyDefectiveIndexer;

    
public IList<string> Parts { getprivate set; }
    
public Car() { Parts = new List<string>(); }

    
/// <summary>
    
/// 为汽车添加轮胎
    
/// </summary>
    
/// <remarks>
    
///     Attributed Builder第二个执行的Setp
    
///     执行4次,即为每辆汽车装配增加4个轮胎
    
/// </remarks>
    [B.BuildStep(24)]
    
public void AddWheel()
    {
        wheelDefectiveIndexer
++;

        
//  模拟会出现装配错误的情况
        if (wheelDefectiveIndexer % WheelDefectiveCycle != 0)
            Parts.Add(WheelItem);
    }

    
/// <summary>
    
/// 为汽车装配引擎
    
/// </summary>
    
/// <remarks>
    
///     没有通过Attribute标注的内容,因此不会被Attributed Builder执行
    
/// </remarks>
    public void AddEngine()
    {
        Trace.WriteLine(EngineItem);
        Parts.Add(EngineItem);
    }

    
/// <summary>
    
/// 为汽车装配车身
    
/// </summary>
    
/// <remarks>
    
///     Attributed Builder第一个执行的Setp
    
///     执行1次,即为每辆汽车装配增加1个车身
    
/// </remarks>
    [B.BuildStep(1)]
    
public void AddBody()
    {
        bodyDefectiveIndexer
++;

        
//  模拟会出现装配错误的情况
        if (bodyDefectiveIndexer % BodyDefectiveCycle != 0)
            Parts.Add(BodyItem);
    }
}

 

 4、5

 

[TestClass]
public class AopAttributedBuilderFixture
{
    
int totalBuildUpCars;   //  装配车辆总数
    int qualifiedCars;      //  装配合格车辆总数
    int defectiveCars;      //  装配次品车辆总数
    int partsBeforeBuild;   //  当前BuildPart()执行前已经装配合格的零件数量

    Builder
<Car> builder;

    [TestInitialize]
    
public void Initialize()
    {
        totalBuildUpCars 
= 0;
        qualifiedCars 
= 0;
        defectiveCars 
= 0;

        builder 
= new Builder<Car>();

        
//  BuildUp()执行前记录总共装配车辆数量
        builder.BeforeBuildUp += (x, y) => { totalBuildUpCars++; };

        
//  BuildUp()执行后记录装配的产品是否合格
        builder.AfterBuildUp +=
            (x, y) 
=>
                {
                    
//  合格的车辆是4个轮子一个车身
                    if ((y.Target.Parts.Where(part => string.Equals(part, Car.WheelItem)).Count() == 4)
                        
&& (y.Target.Parts.Where(part => string.Equals(part, Car.BodyItem)).Count() == 1))
                    {
                        qualifiedCars
++;
                        Trace.WriteLine(
"qualified\n");
                    }
                    
else
                    {
                        defectiveCars
++;
                        Trace.WriteLine(
"defective\n");
                    }
                };

        
//  BuildPart()执行前,记录当前装配的工序信息
        
//  同时登记之前已经合格完成装配的零件数量
        builder.BeforeBuildStepExecuting +=
            (x, y) 
=>
                {
                    partsBeforeBuild 
= y.Target.Parts.Count;
                    
if (y.Step.Times > 1)
                        Trace.Write(
string.Format("step {0} sequence\t[{1}] current loop [{2}/{3}]\t",
                            y.Step.Method.Name,
                            y.Step.Sequence,
                            y.CurrentLoop 
+ 1,
                            y.Step.Times));
                    
else
                        Trace.Write(
string.Format("step {0} sequence\t[{1}] \t",
                                                    y.Step.Method.Name,
                                                    y.Step.Sequence));
                };

        
//  通过对比每个BuildPart()过程前后实际已装配的零件数量,可以记录缺陷具体出现在哪个步骤
        builder.AfterBuildStepExecuted +=
            (x, y) 
=>
                {
                    
if (y.Target.Parts.Count > partsBeforeBuild)
                        Trace.WriteLine(
"qualified");
                    
else
                        Trace.WriteLine(
"defective");
                };
    }

    
/// <summary>
    
/// 单元测试中对于整车和每个部件的检测工作通过AOP手段嵌入BuildUp()过程中
    
/// 合格车辆的标准是每个整车和每个部件(1个车身和4个轮子)都合格
    
/// </summary>
    [TestMethod]
    
public void TestAopDuringBuildUp()
    {
        
for (var i = 0; i < 7; i++ )
            builder.BuildUp();

        Trace.WriteLine(
"\n\n ---------------------------------------");
        Trace.WriteLine(
"total build up " + totalBuildUpCars);
        Trace.WriteLine(
"qualified " + qualifiedCars);
        Trace.WriteLine(
"defective " + defectiveCars);
    }
}

 

最后通过Output窗口的调试信息显示上述各横切手段可以得到执行。

 

------ Test started: Assembly: Builder.Tests.dll ------

step AddBody sequence    [
1]     qualified
step AddWheel sequence    [
2] current loop [1/4]    qualified
step AddWheel sequence    [
2] current loop [2/4]    qualified
step AddWheel sequence    [
2] current loop [3/4]    qualified
step AddWheel sequence    [
2] current loop [4/4]    qualified
qualified

step AddBody sequence    [
1]     qualified
step AddWheel sequence    [
2] current loop [1/4]    qualified
step AddWheel sequence    [
2] current loop [2/4]    qualified
step AddWheel sequence    [
2] current loop [3/4]    qualified
step AddWheel sequence    [
2] current loop [4/4]    qualified
qualified

step AddBody sequence    [
1]     qualified
step AddWheel sequence    [2] current loop [1/4]    defective
step AddWheel sequence    [
2] current loop [2/4]    qualified
step AddWheel sequence    [
2] current loop [3/4]    qualified
step AddWheel sequence    [
2] current loop [4/4]    qualified
defective

step AddBody sequence    [
1]     qualified
step AddWheel sequence    [
2] current loop [1/4]    qualified
step AddWheel sequence    [
2] current loop [2/4]    qualified
step AddWheel sequence    [
2] current loop [3/4]    qualified
step AddWheel sequence    [
2] current loop [4/4]    qualified
qualified

step AddBody sequence    [
1]     qualified
step AddWheel sequence    [
2] current loop [1/4]    qualified
step AddWheel sequence    [2] current loop [2/4]    defective
step AddWheel sequence    [
2] current loop [3/4]    qualified
step AddWheel sequence    [
2] current loop [4/4]    qualified
defective

step AddBody sequence    [
1]     qualified
step AddWheel sequence    [
2] current loop [1/4]    qualified
step AddWheel sequence    [
2] current loop [2/4]    qualified
step AddWheel sequence    [
2] current loop [3/4]    qualified
step AddWheel sequence    [
2] current loop [4/4]    qualified
qualified

step AddBody sequence    [
1]     qualified
step AddWheel sequence    [
2] current loop [1/4]    qualified
step AddWheel sequence    [
2] current loop [2/4]    qualified
step AddWheel sequence    [2] current loop [3/4]    defective
step AddWheel sequence    [
2] current loop [4/4]    qualified
defective



 ---------------------------------------
total build up 7
qualified 4
defective 3

1 passed, 0 failed, 0 skipped, took 0.93 seconds (MSTest 10.0).

 

 

posted @ 2011-05-18 21:17  蜡笔小王  阅读(1316)  评论(0编辑  收藏  举报