(声明:本系列所用的模式都来自GOF23中,本系列并不是讲23种经典设计模式,而是如何去使用这些模式)
(由于篇幅有限,部分代码请看如何使用设计模式来构造系统--(1) )
上一篇我们分析了员工,工资,以及绩效奖金三个类,并且使用了Bridge和Stratege两种模式,对他们之间的组合和行为进行了设计,但是我们的设计并不完善。我们知道员工的基本工资可能每年都不一样,甚至有可能随时地根据公司的制度发生变化,而设计的根本意图就是去封装变化,让我们的系统更加的长寿,不会因为变化而大量的重造,我们怎么去避免工资变化时,员工类不改变呢??
先看一下,上次设计的员工类的代码:
Code
public abstract class AbstractPerson
{
protected string _personName; //员工姓名
protected Salary _personSalary; //员工工资
public string PersonName
{
get { return _personName; }
set { _personName = value; }
}
public Salary PersonSalary
{
get { return _personSalary; }
set { _personSalary = value; }
}
public abstract double GetShouldpaid(IPrize prize);
}
public class Staff : AbstractPerson
{
public override double GetShouldpaid(IPrize prize)
{
_personSalary = new SttafSalary(); //初始化正式员工的基本工资
_personSalary.Salaryprize = prize; //赋予绩效
return _personSalary.GetShouldpaid();
}
}
这里我们看到在初始化员工工资时出现了new SttafSalary(),这就使Person与SttafSalary产生了紧耦合,也就是说SttafSalary的变化会影响Person类的稳定。那么使用何种设计模式来封装这种创建时的变化呢?
GOF23中的Factory Method(工厂方法): 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
工厂方法的意图和我们要封装的变化意图相同,那么我们就用它来设计吧,看看类图:
我们使用两个同接口的工厂来封装创建时的变化,这样你在各个工厂中去添加或修改你的业务都不会影响到Person,当然原来的那种直接new的方法也不会改变,但是当你的员工类包含的工资类发生变化时(比方说现在InterShip的工资升级为正式员工的工资,而正式员工的工资重新定义),就不能应对变化了,但是将创建工作封装到Factory,就可以避免这种变化引起的Person类的修改,而只需要去添加Salary的子类,修改Factory的子类,就可以完成了。
我们来看代码。
工厂部分的实现:
Code
public abstract class SalaryFactory
{
public abstract Salary GetSalary(); //留给具体某种工资福利制度工厂去实现
}
public class StaffSalaryFactory:SalaryFactory
{
public override Salary GetSalary() //具体某种工资福利制度工厂
{
return new SttafSalary();
}
}
public class InternshipSalaryFactory:SalaryFactory
{
public override Salary GetSalary() //具体某种工资福利制度工厂
{
return new InternshipSalary();
}
}
Person类的实现:
Code
public abstract class AbstractPerson
{
#region 字段
protected string _personName; //员工姓名
protected Salary _personSalary; //员工工资
#endregion
#region 属性
public string PersonName
{
get { return _personName; }
set { _personName = value; }
}
public Salary PersonSalary
{
get { return _personSalary; }
set { _personSalary = value; }
}
#endregion
public abstract double GetShouldpaid(IPrize prize);
}
public class Staff : AbstractPerson
{
public override double GetShouldpaid(IPrize prize)
{
_personSalary = new StaffSalaryFactory().GetSalary();
_personSalary.Salaryprize = prize;
return _personSalary.GetShouldpaid();
}
}
public class Internship : AbstractPerson
{
public override double GetShouldpaid(IPrize prize)
{
_personSalary = new InternshipSalaryFactory().GetSalary();
_personSalary.Salaryprize = prize;
return _personSalary.GetShouldpaid();
}
}
OK,这样我们就完成了这种变化的封装。在实际项目中你可以把Person中创建具体那个工厂,在配置文件中存储,然后做个Switch ,就可以让他们的结合更松散了,这里就不多说了。
接下来我们还有一个问题,Prize绩效的制度,上一篇的设计,每一个员工的工资都会创建一个Prize,而Prize只是固定的制度,员工都是遵守和共享这个制度的,这样就造成了内存的浪费,怎么去解决这个问题呢,让系统中只存在我们想要的固定数目的Prize的实例呢?
Singleton(单件):保证一个类仅有一个实例,并提供一个访问它的全局访问点。和我们的意图一样,那就用它吧。
Code
public interface IPrize
{
double GetPrize(Salary salary);
}
public class BadPrize : IPrize //绩效不好的奖金比率
{
public static readonly BadPrize badPrize = new BadPrize(); //在这里使用静态只读,做到badPrize不可被修改
private BadPrize()//在这里使用私有构造,做到BadPrize类不可被实例化
{ }
public double GetPrize(Salary salary)
{
return salary.SalaryNum * 0.5; //干的不好奖金扣一半
}
}
public class GoodPrize : IPrize //绩效好的奖金比率
{
public static readonly GoodPrize goodPrize = new GoodPrize();
private GoodPrize()
{ }
public double GetPrize(Salary salary)
{
return salary.SalaryNum * 1; //干的好奖金全发,哈哈
}
}
这样我们就保证了,在系统运行时BadPrize和GoodPrize都只有一个实例,完成了我们的设计。
现在来看看调用程序:
Code
class Program
{
static void Main(string[] args)
{
Staff staff = new Staff();
staff.PersonName="涵舍愚人";
Console.Write(staff.PersonName + "本月实发工资为" + staff.GetShouldpaid(BadPrize.badPrize)); //由原先的new BadPrize(),变为现在的单件badPrize
Console.Read();
}
}
输出结果:
由于这个月干的不好,绩效工资被扣一半.....:(
我们使用了两种设计模式Factory Method和Singleton ,来封装了员工的工资创建和绩效工资的单件,好了这样我们就设计完成了用户的需求,下一篇中可恶的客户要添加需求了。。(在实际开发中这种事情的发生频率,地球上的程序员都知道啦。)
下一篇: 如何使用设计模式来构造系统--(3)