设计模式和原则索引
二、基于接口,而不是实现编程
让类型间的依赖基于接口,来最小化类型间的依赖。在代码变化的可能性较大时,这种设计方法非常适合。>应用接口还是抽象类?要根据具体情况判断,如果你希望接口附带一些行为,就是用抽象类,否则使用接口,DotNet不支持多重继承,使用interface则基类还有选择的余地。
接口可以是.Net中的Interface也可以是一个基类。
基于接口的核心在于多态,可以延迟实现,也可以运行时动态设定
三、(尽量)使用对象组合,而不是类型继承
GOF中提出了两种重用的方式:白盒重用和黑盒重用。
白盒重用既是继承,在类型继承中,派生类不仅继承了父类的元素和功能,还继承了父类型的上下文,对派生类的重写可能会破坏父类的上下文,而且父类的修改也会破坏到派生类,这是继承带来的问题。
黑盒重用就是对象组合,通过创建一个新的类型,包含基类的实例来实现,如下:
namespace fofo.DesignPattern
{
public class MyClassExtension
{
private MyClass myObj = null;
public MyClassExtension()
{
myObj = new MyClass();
}
public object DoWork()
{
object data = myObj.DoWork();
// process data
return data;
}
// oth code
}
}
这样MyClassExtension就以黑盒的方式通过定义的契约使用了MyClass,而且MyClassExtension无法访问MyClass的内部成员,也不能修改其行为,MyClassExtension将具体的功能交给MyClass实现,并进行功能上的增强。
那么什么时候使用继承呢,一般来说,派生类需要为原类型添加一个新的功能,或者完全重写一个基类的某个功能时才使用继承,
而且组合无法实现多态,系统需要多态的情况下,使用继承。
四、开放/封闭原则(OCP)
系统应该对扩展开放,对修改关闭。
满足这个原则的系统,可以更灵活的应对需求变化。在系统设计中,OCP建议在发生变化时,不要修改现有系统的代码,而是通过增加新的代码来增强现有类型的功能(这意味着使用继承或组合)
五、派生类可以代替父类使用
在系统设计时,某个派生类应该可以适用于任何可以使用其父类的地方。
这条和前面的OCP紧密相关,违反此条的也必定违反了OCP。如下代码:
namespace fofo.DesignPattern
{
public class MyClass
{
private int nStatus = 0;
public virtual object DoWork()
{
object objRet = null;
if (nStatus == 0)
{
//...
}
else
{
//...
}
return objRet;
}
}
}
这个类的派生类无法访问父类的private成员,不能代替父类使用。
在设计虚方法时不应该访问私有成员,因为私有成员的访问无法在派生方法中实现。一般来说,派生类重写的虚方法应该和父类完成同样的功能,二者的前置条件和后置条件都应该相同。
不满足此原则必定会破坏多态性。
六、控制反转(依赖倒置原则 DIP)
面向对象中,系统是对现实的抽象表示,这个过程应该是自上而下的,先对系统进行高层次的抽象,逐步产生一系列的模块、类型和方法,
如下代码,一个View可简单拆分为DataLoader(获取数据)和Renderer(数据展现):
namespace fofo.DesignPattern
{
public class MyView
{
public void Show()
{
IDataLoader loader = ResolveDataLoader();
DataContext data = loader.GetViewData();
IRenderer renderer = ResolveRenderer();
renderer.RenderView(data);
}
//... other code
}
}
View的Show方法并不依赖于DataLoader和Rederer的实现,他们在固定的接口下隐藏了实现的细节。
也即依赖导致原则:高层次的组件(如View)不应该依赖于低层次的组件(如DataLoader和Renderer),二者应依赖于接口,抽象不依赖于细节,细节依赖于抽象。
名字里的“倒置”表示在设计过程中采用自上而下的方式,且更应该关注高层次的工作流,而不是低层次的具体实现。
完善下上面的例子,用注入实现:
namespace fofo.DesignPattern
{
public class MyView
{
private IDataLoader _loader = null;
private IRenderer _renderer = null;
public MyView(IDataLoader loader, IRenderer renderer)
{
_loader = loader;
_renderer = renderer;
}
public void Show()
{
DataContext data = loader.GetViewData();
renderer.RenderView(data);
}
//... other code
}
}