SOLID, GRASP和其他面向对象设计的基本原则(二)
学习面向对象的设计原则,并牢牢掌握SOLID和GRASP规则背后的思想
原文 By Muhammad Umair · Feb. 13, 17 · Web Dev Zone
04
Interface Segregation Principle(接口隔离原则)
接口隔离原则(ISP)描述为:
客户端不应该被强迫依赖他们不使用的接口
还是考虑前一个例子:
Public Interface IDevice{
Void Open();
Void Read();
Void Close();
}
这个接口有三个实现类,USB设备、网络设备和PCID设备。这个接口对于网络和PCID设备来说够用,但是USB设备还需要另一个函数(Refresh())才能运行正常。
和USB设备一样,也许还有另外的设备也需要这个函数来支持工作。为此,接口被更新如下:
Public Interface IDevice{
Void Open();
Void Refresh();
Void Read();
Void Close();
}
那么问题来了,任何一个实现该接口的类都需要去实现Refresh函数。
例如为了满足以上的设计,必须对网络设备和PCID设备添加下面的代码:
public void Refresh(){
// Yes nothing here… just a useless blank function
}
因此,这个设备基类被视为一个胖接口(过多的函数)。这种设计违反了接口隔离原则,因为它造成了客户端不必要的依赖。
有很多方法可以解决这个问题,不过我将在预定义的面向对象的解决方案范畴内处理这个问题。
我们知道在open操作之后就会直接调用refresh函数,因此,我改变逻辑将设备客户端的refresh函数迁移至具体的实现类内部。在本例中我将调用refresh的逻辑移动到USB设备的具体实现类中:
Public Interface IDevice{
Void Open();
Void Read();
Void Close();
}
Public class USBDevice:IDevice{
Public void Open{
// open the device here…
// refresh the device
this.Refresh();
}
Private void Refresh(){
// make the USb Device Refresh
}
}
通过这个方式,我减少了基类中的函数数目,让它变得更轻了。
05
Dependency Inversion Principle(依赖反转)
这条原则是对上面所讨论的原则的一个概述。
在我们给出DIP的书面定义之前,请让我介绍一个与此紧密相连的一条原则,以帮助我们理解DIP。
这条原则是:面向接口编程,而不是面向实现编程。
考虑下面这个简单的例子:
Class PCIDevice{
Void open(){}
Void close(){}
}
Static void Main(){
PCIDevice aDevice = new PCIDevice();
aDevice.open();
//do some work
aDevice.close();
}
上面的示例代码违背了面向接口编程,因为我们正在操作的引用是具体的类对象PCIDevice,下面的代码则遵循了这个原则:
Interface IDevice{
Void open();
Void close();
}
Class PCIDevice implements IDevice{
Void open(){ // PCI device opening code }
Void close(){ // PCI Device closing code }
}
Static void Main(){
IDevice aDevice = new PCIDevice();
aDevice.open();
//do some work
aDevice.close();
}
所以这条原则很容易去遵循。依赖反转与此类似,但需要我们做的更多。
依赖反转:高级模块不应该依赖低级模块。二者应该依赖于抽象。
你可以把"两者都应该依赖于抽象"简单的理解成面向接口编程。那么什么是高级模块和低级模块?
为了这条原则的第一部分,我们需要理解高级模块和低级模块实际上是什么。看如下代码:
Class TransferManager{
public void TransferData(USBExternalDevice usbExternalDeviceObj,SSDDrive ssdDriveObj){
Byte[] dataBytes = usbExternalDeviceObj.readData();
// work on dataBytes e.g compress, encrypt etc..
ssdDriveObj.WrtieData(dataBytes);
}
}
Class USBExternalDevice{
Public byte[] readData(){
}
}
Class SSDDrive{
Public void WriteData(byte[] data){
}
}
上面的代码有三个类,TransferManager代表高级模块,因为它在一个方法中用了其它两个类。因此其他两个类则是低级模块。
上面的代码中,高级模块是直接使用低级模块的(没有任何抽象),因此违背了依赖反转原则。
违背了依赖反转这条原则会让你的软件系统变得难以更改。比如,如果你想增加其他的外部设备,你将不得不
改变高级模块。因此你的高级模块将会依赖于低级模块,依赖会让代码变得难以改变。
如果你理解了上面的原则"面向接口编程",那么就很容易解决这个问题:
Class USBExternalDevice implements IExternalDevice{
Public byte[] readData(){
}
}
Class SSDDrive implements IInternalDevice{
Public void WriteData(byte[] data){
}
}
Class TransferManager implements ITransferManager{
public void Transfer(IExternalDevice externalDeviceObj, IInternalDevice internalDeviceObj){
Byte[] dataBytes = externalDeviceObj.readData();
// work on dataBytes e.g compress, encrypt etc..
internalDeviceObj.WrtieData(dataBytes);
}
}
Interface IExternalDevice{
Public byte[] readData();
}
Interfce IInternalDevice{
Public void WriteData(byte[] data);
}
Interface ITransferManager {
public void Transfer(IExternalDevice usbExternalDeviceObj,SSDDrive IInternalDevice);
}
在上面的代码中,高级模块和低级模块都依赖于抽象,符合了依赖反转原则。
06
Hollywood(好莱坞原则)
这条原则和依赖反转原则类似:不要调用我们,我们会给你。
这意味着高级组件可以以一种互不依赖的方式去支配低级组件。
这条原则可以防止依赖恶化。依赖恶化发生在每个组件都依赖于其他各个组件。换句话说,依赖恶化是让依赖发生在各个方向(向上,横向,向下)。
Hollywood原则可以让我们时依赖只向一个方向。
DIP和Hollywood之间的差异给了我们一条通用原则:无论是高级组件还是低级组件,都要依赖于抽象而不是具体的类。另一方面,Hollywood原则
强调了高级组件和低级组件应该以不产生依赖的方式交互。
07
Polymorphism(多态)
什么?多态也是设计原则?没错,多态是任何面向对象语言都要提供的基础特征,它可以让父类的引用指向子类。
它同时也是GRASP的设计原则之一。这条原则为你在面向对象设计中提供了知道方针。
这条原则严格限制了运行时类型信息的使用(RTTI)。在C#中,我们用如下方式实现RTTI:
if(aDevice.GetType() == typeof(USBDevice)){
//This type is of USBDEvice
}
在java中,RTTI由getClass()或者instanceOf()完成:
if(aDevice.getClass() == USBDevice.class){
// Implement USBDevice
Byte[] data = USBDeviceObj.ReadUART32();
}
如果你曾在你的项目中编写了类似的代码,那么现在是时候重构你的代码以符合多态原则,看如下例图:
在此我在接口中生成了read方法,然后委托他们的实现类去实现该方法,现在,我只用方法Read:
//RefactoreCode
IDevice aDevice = dm.getDeviceObject();
aDevice.Read();
getDeviceObject()的实现来自哪里?这是我们即将要讨论的创造者原则和信息专家原则。
下期预告
1. Information Expert(信息专家原则)
2. Creator(创造者原则)
3. Pure Fabrication(纯虚构模式)
4. Controller(控制器原则)
5. Favor Composition Over Inheritance(组合优于继承原则)
6. Indirection(间接原则)

浙公网安备 33010602011771号