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

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

 


 


分析

1、由于需要同时从管理内容和人员类型两方面进行调整,所有需要借助.NET平台解耦双因素依赖关系

2、为了实现简便采用LINQ + 委托方式

3、为了便于随着应用项目维护进行修改,将所有人员类型和管理内容以配置方式动态载入

4、为了简化众多内容,提供统一的Facade接口,协调众多人员类型和管理内容间的行为匹配

 

 

参考实现

 1、人员类型定义

#region Staff

abstract class Person
{
    
public string Name { getset; }
    
public virtual double Income { getset;}
    
public virtual object Clone() {return MemberwiseClone();}
}

/// <summary>
/// 外聘专家
/// </summary>
class Expert : Person
{
    
public Expert(double cost)
    {
        Cost 
= cost;
        Income 
= Cost;
    }
    
public double Cost { getset; }
}

abstract class Employee : Person { }

class GeneralEmployee : Employee
{
    
public GeneralEmployee(double basicSalary, int workingYears)
    {
        BasicSalary 
= basicSalary;
        WorkingYears 
= workingYears;
    }
    
public double BasicSalary { getset; }
    
public double ExtraSalary { getset; }
    
public int WorkingYears { getset; }
    
public int VacationDays { getset; }

    
public override double Income{get{return BasicSalary + ExtraSalary;}}
}

class Temporary : Employee
{
    
public double Wage { getset; }
    
public override double Income{get{return Wage;}}
}

class Mananger : GeneralEmployee
{
    
public Mananger(double basicSalary, int workingYears, string department)
        : 
base(basicSalary, workingYears)
    {
        Department 
= department;
    }
    
public string Department { getprivate set; }
}

#endregion

 

 2、访问接口和访问过程定义

class HrRuntime
{
    
/// <summary>
    
/// 所有Element接受Visitor的统一入口
    
/// </summary>
    
/// <remarks>
    
///     Type    表示适用的人员类别
    
///     Action  表示Visitor扩展的功能
    
/// </remarks>
    public IEnumerable<KeyValuePair<Type, Action<Person>>> Registry { getset; }

    
/// <summary>
    
/// 所有Element
    
/// </summary>
    public List<Person> Staff { getset; }

    
/// <summary>
    
/// 调用各个适用的Visitor
    
/// </summary>
    public void Visit()
    {
        
if(Registry.Count() == 0return;
        
if(Staff.Count() == 0return;

        
//  动态生成传统Visit各个接口的过程
        
//      VisitGeneralExpert()
        
//      VisitGeneralTemporary()
        
//      VisitGeneralGeneralEmployee()
        
//      VisitGeneralManager()
        var visitorsByType = Registry.GroupBy(x => x.Key);

        
//  动态执行Visit()过程
        Staff.ForEach(x => Registry.Where(a => a.Key == x.GetType()).ToList().ForEach(a => a.Value(x)));
    }

    
public T FindClone<T>(string name)
        
where T : Person
    {
        
return (T)(Staff.FirstOrDefault(x=>string.Equals(x.Name, name)).Clone());
    }
}

 

3、单元测试验证各种管理措施与各类人员间的访问效果

 

HrRuntime runtime;
const double MaxMistake = 0.1;

[TestInitialize]
public void Initialize()
{
    Action
<Person> workingYearsSalaryHandler =
        (x) 
=>
            {
                ((GeneralEmployee) x).ExtraSalary 
+=
                    ((GeneralEmployee) x).WorkingYears
*50;
            };
    Action
<Person> calculatePensionAndHousingFundHandler =
        (x) 
=>
            {
                ((GeneralEmployee) x).ExtraSalary 
-= ((GeneralEmployee) x).Income*(0.15 + 0.05);
            };
    Action
<Person> calculateAnualLeave =
        (x) 
=>
            {
                ((GeneralEmployee) x).VacationDays 
= ((((GeneralEmployee) x).WorkingYears - 1 )/5 + 1)*5;
            };
                                                                             

    runtime 
=
        
new HrRuntime()
        {
            Registry 
= new List<KeyValuePair<Type, Action<Person>>>()
            {
                
//  给普通员工和经理分别加薪15%和10%
                new KeyValuePair<Type, Action<Person>>(typeof (GeneralEmployee), x =>{((GeneralEmployee) x).BasicSalary *= 1.15;}),
                
new KeyValuePair<Type, Action<Person>>(typeof (Mananger), x =>{((Mananger)x).BasicSalary *= 1.1;}),

                
//  司龄工资:每年增加50元
                new KeyValuePair<Type, Action<Person>>(typeof (GeneralEmployee), workingYearsSalaryHandler),
                
new KeyValuePair<Type, Action<Person>>(typeof (Mananger), workingYearsSalaryHandler),

                
//  保险和公积金:为了简化示例,养老保险和住房公积金分别按最后应发收入合计的5%和15%扣除
                new KeyValuePair<Type, Action<Person>>(typeof (Temporary), x =>{((Temporary) x).Wage *=(1 -0.15 -0.05);}),
                
new KeyValuePair<Type, Action<Person>>(typeof(GeneralEmployee), calculatePensionAndHousingFundHandler),
                
new KeyValuePair<Type, Action<Person>>(typeof(Mananger), calculatePensionAndHousingFundHandler),

                
//  年休假:所有员工(普通员工、经理)按照司龄从每年5天起,每满5年年度增加5天逐级递增
                new KeyValuePair<Type, Action<Person>>(typeof(GeneralEmployee), calculateAnualLeave),
                
new KeyValuePair<Type, Action<Person>>(typeof(Mananger), calculateAnualLeave),

                
//  岗位增休假:计划在现有休假基础上,普通员工每年增加3天,经理每年增加5天
                new KeyValuePair<Type, Action<Person>>(typeof(GeneralEmployee), (x)=>((GeneralEmployee)x).VacationDays += 3),
                
new KeyValuePair<Type, Action<Person>>(typeof(Mananger), (x)=>((Mananger)x).VacationDays += 5),
            },
            Staff 
= new List<Person>()
            {
                
new Expert(2000){Name = "E1"},
                
new Expert(3000){Name = "E2"},
                
new Temporary(){Name = "T1", Wage = 1200},
                
new Temporary(){Name = "T2", Wage = 1100},
                
new GeneralEmployee(40003){Name = "G1"},
                
new GeneralEmployee(600010){Name = "G2"},
                
new GeneralEmployee(40003){Name = "G3"},
                
new Mananger(110002"sales"){Name = "M1"},
                
new Mananger(1500012"administration"){Name = "M2"}
            }
        };
}


/// <summary>
/// 外聘专家
/// </summary>
[TestMethod]
public void TestExpert()
{
    var e1 
= runtime.FindClone<Expert>("E1");
    var e2 
= runtime.FindClone<Expert>("E2");

    runtime.Visit();

    
//  外聘专家没有加薪、加休的内容
    Assert.AreEqual<double>(e1.Income, runtime.FindClone<Expert>("E1").Income);
    Assert.AreEqual
<double>(e2.Income, runtime.FindClone<Expert>("E2").Income);
}

/// <summary>
/// 临时员工
/// </summary>
[TestMethod]
public void TestTemporary()
{
    var t1 
= runtime.FindClone<Temporary>("T1");
    var t2 
= runtime.FindClone<Temporary>("T2");

    runtime.Visit();

    
//  仅适用 =>  保险和公积金:为了简化示例,养老保险和住房公积金分别按最后应发收入合计的5%和15%扣除
    Assert.AreEqual(t1.Income * 0.8, runtime.FindClone<Temporary>("T1").Income, MaxMistake);
    Assert.AreEqual(t2.Income 
* 0.8, runtime.FindClone<Temporary>("T2").Income, MaxMistake);
}

/// <summary>
/// 临时普通员工
/// </summary>
[TestMethod]
public void TestGeneralEmployee()
{
    var g1 
= runtime.FindClone<GeneralEmployee>("G1");
    var g2 
= runtime.FindClone<GeneralEmployee>("G2");
    var g3 
= runtime.FindClone<GeneralEmployee>("G3");

    runtime.Visit();

    
//  收入适用 =>  
    
//      给普通员工和经理分别加薪15%和10%
    
//      司龄工资:每年增加50元
    
//      保险和公积金:为了简化示例,养老保险和住房公积金分别按最后应发收入合计的5%和15%扣除
    Func<GeneralEmployee, double> incomeHandler =
        (x) 
=>
            {
                var extraSalary 
= x.WorkingYears*50;
                var basicSalary 
= x.BasicSalary;
                basicSalary 
*= 1.15;
                var pensionAndHousingFund 
= (extraSalary + basicSalary)*0.2;
                
return extraSalary + basicSalary - pensionAndHousingFund;
            };
    Assert.AreEqual(incomeHandler(g1), runtime.FindClone
<GeneralEmployee>("G1").Income );
    Assert.AreEqual(incomeHandler(g2), runtime.FindClone
<GeneralEmployee>("G2").Income);
    Assert.AreEqual(incomeHandler(g3), runtime.FindClone
<GeneralEmployee>("G3").Income);


    
//  休假适用 =>  
    
//      年休假:所有员工(普通员工、经理)按照司龄从每年5天起,每满5年年度增加5天逐级递增
    
//      岗位增休假:计划在现有休假基础上,普通员工每年增加3天,经理每年增加5天
    Func<GeneralEmployee, int> vacationHandler =
    (x) 
=>
    {
        var workingYears 
= x.WorkingYears;
        var vacation 
= ((workingYears - 1 )/5 + 1)*5;
        
return vacation + 3;
    };
    Assert.AreEqual(vacationHandler(g1), runtime.FindClone
<GeneralEmployee>("G1").VacationDays);
    Assert.AreEqual(vacationHandler(g2), runtime.FindClone
<GeneralEmployee>("G2").VacationDays);
    Assert.AreEqual(vacationHandler(g3), runtime.FindClone
<GeneralEmployee>("G3").VacationDays);
}

/// <summary>
/// 临时普通员工
/// </summary>
[TestMethod]
public void TestManager()
{
    var m1 
= runtime.FindClone<Mananger>("M1");
    var m2 
= runtime.FindClone<Mananger>("M2");

    runtime.Visit();

    
//  收入适用 =>  
    
//      给普通员工和经理分别加薪15%和10%
    
//      司龄工资:每年增加50元
    
//      保险和公积金:为了简化示例,养老保险和住房公积金分别按最后应发收入合计的5%和15%扣除
    Func<GeneralEmployee, double> incomeHandler =
        (x) 
=>
        {
            var extraSalary 
= x.WorkingYears * 50;
            var basicSalary 
= x.BasicSalary;
            basicSalary 
*= 1.1;
            var pensionAndHousingFund 
= (extraSalary + basicSalary) * 0.2;
            
return extraSalary + basicSalary - pensionAndHousingFund;
        };
    Assert.AreEqual(incomeHandler(m1), runtime.FindClone
<Mananger>("M1").Income);
    Assert.AreEqual(incomeHandler(m2), runtime.FindClone
<Mananger>("M2").Income);


    
//  休假适用 =>  
    
//      年休假:所有员工(普通员工、经理)按照司龄从每年5天起,每满5年年度增加5天逐级递增
    
//      岗位增休假:计划在现有休假基础上,普通员工每年增加3天,经理每年增加5天
    Func<GeneralEmployee, int> vacationHandler =
    (x) 
=>
    {
        var workingYears 
= x.WorkingYears;
        var vacation 
= ((workingYears - 1/ 5 + 1* 5;
        
return vacation + 5;
    };
    Assert.AreEqual(vacationHandler(m1), runtime.FindClone
<Mananger>("M1").VacationDays);
    Assert.AreEqual(vacationHandler(m2), runtime.FindClone
<Mananger>("M2").VacationDays);
}

 

 

 

posted @ 2011-05-26 20:24  蜡笔小王  阅读(1446)  评论(0编辑  收藏  举报