继承法是支持框架扩展点的简单方法。但是,开发者要实现抽象方法,必须要知道父类中有哪些可用的数据和方法,以及他们的相互关系,因此开发者需要详细了解框架内部结构才能使用它。(我操,绝对不是好设计!)
例如,在NewYorkBusiness类中,实现CalculateStateTax 和CalculateFedTax 这两个方法,看上去很简单,但却要求开发者必须知道,有个名为income的 保护型浮点变量,且必须在调用任何一个CalculateXxxTax方法之前为该变量赋值。 而且,父类向自雷暴露其内部细节,降低了父类的封装度, 这可能会导致开发者随意对类的内部状态做出超越其父类设计者意图的访问和修改, 下面是一段用继承法实现扩展点的代码。
public class NewYorkBusiness :BasicBusiness
{
protected override float CalculateStateTax()
{
return income*0.1F;
}
protected override float CalculateFedTax()
{
return income*0.2F;
}
}
随着方法变得越来越复杂,开发者也许要引用父类内部更多的数据和方法,并且需要了解,通过设置这些数据的值和调用这些方法而改变对象的整个状态之后,会有什么后果。
开发者必须要了解父类细节信息个要求,延长了框架的学习曲线, 加重了使用框架的负担,为了让开发者不必知道框架组件的内部细节,有个办法,就是将扩展点定义为一组接口(interface ), 让这组接口和框架进行妥善的交互, 开发者通过实现接口来创建组件, 然后把该组件插入到框架中定制它的行为。
可插入组件(Pluggable Component)
为了通过可插入组件来支持扩展点,应用框架必须首先为扩展点定义接口。接口描述了要与之兼容的类必须实现的一组方法。接口仅描述方法的外部特征,比如方法名,参数个数和参数类型, 它并不描述方法如何实现, 下面是用C#语言定义的一个接口的例子:
public interface ICalculateTax
{
float CalculateStateTax();
float CalculateFedTax();
float Income { get;set;}
}
然后,你通过创建一个具体类并实现接口中定义的每个方法来支持ICalculateTax接口,就像下面显示的NewYorkBusiness类一样。
public class NewYorkBusiness: ICalculateTax
{
private float income ;
public float Income
{
get{ return income; }
set{ income = value; }
}
public float CalculateStateTax()
{ return income*0.1F ; }
public float CalculateFedTax()
{ return income*0.2F; }
}
NewYorkBusiness类实现了 ICalculateTax接口,它现在已经兼容于,或者说可以插入到框架中与ICalculateTax接口协作的扩展点了,借助接口的帮助,应用开发者可以把可插入应用组件加载到框架的扩展点, 在框架中安排客户化的行为。
用组合法来支持扩展点,还要借助于另一个称为策略模式(strategy)的GOF设计模式, 我们修改一下纳税的例子,用组合法来支持扩展点。 首先,我们需要修改BasicBusiness组件,这次不把它写成抽象类,而是写成具体类,下面的代码片段显示了新的BasicBusiness组件:
public class BasicBusiness
{
public void ReportTax( ICalculateTax calculator )
{
float sTax = calculator.CalculateStateTax();
float fTax = calculator.CalculateFedTax();
bool ok = CheckBankBalance(sTax + fTax);
if( !ok )
{
FileBankruptcy();
}
else
{
SendMoneyToGov( sTax + fTax );
}
}
}
ReportTax 方法现在有了一个ICalculateTax类型的输入参数, 这个输入参数提供了客户化税款计算机制, 它就是BasicBusiness框架组件中的扩展点。 正如你知道的,通过插入客户化应用组件,我们就可以将特定应用的业务逻辑和知识“填充”到扩展点中去。
ICalcualteTax nyBusiness = new NewYorkBusiness();
ICalculateTax calBusiness = new CaliforniaBusiness();
BasicBusiness basic = newBasicBusiness();
basic.ReportTax (nyBusiness);
basic.ReportTax( calBusiness);
在上面的这个例子中,两个ReportTax方法将产生不同的结果,因为在两次调用中,框架组件绑定了不同的ICalculateTax组件。
为了让框架组件使用可插入对象,你要么使用显示创建对象,然后传递参数的方法,要么把应用组件的类型信息(Type Information) 保存在配置文件中 ,然后通过反射机制(Reflection )创建适当的组件,并动态插入到框架组件中。