面向对象设计原则
接触面向对象也有两三年了,但是对于面向对象的一些原则却还没有掌握,最近复习了一下设计模式和面向对象的原则,所以写下一些笔记以及自己对这些原则的看法。
1.Open Close Principle (对可变性的封装)
设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。
其实这里所说的不被修改是指的重要的抽象层的模块不会被修改,这也就是使变化中的软件系统有一定的稳定性。当系统要扩展或者添加新的行为的时候只需要添加另外实现的模块即可。由于新添加的模块继承于抽象层,所以实现了其不变性。
比如在策略模式中,OCP就得到了很好的体现,算法的不同实现其实就是对扩展的支持,而算法抽象类是对系统的不变性的支持,环境类包装了对于环境变化的控制与所采用算法的选择,当采用其他算法的时候只需要扩展算法类即可。
也就是说关键在于抽象,抽象出来的东西是不变的,具体的实现继承于抽象,所以保证了对修改的Close,而抽象的实现方式有多种,可以随需添加,当然这也就是对扩展的Open。 另外要求的是技术包括:多态 Polymorphism,接口 Interface,继承 Inheritance.
另外在对可变性进行封装的时候也应该注意以下几点:
1.识别系统有可能变化的地方。
2.不要将一种可变形散布在多处代码,而应该封装起来。
3.不要将一种可变性与另外一种可变性混在一起。
几乎所有的模式都是对不同可变形的封装,使其符合OCP。
2.Liskov Substitution Principle
一个软件实体如果使用基类的话,那么一定适用于其子类,而且他根本不能察觉出基类对象和子类对象的区别[1]。 其实力求符合这一设计原则是为了让我们理解继承,不要滥用继承。客户程序依赖的行为是基类,也就是我们抽象出来的类,所以继承该类的类就必须保证能够满足客户程序的要求,即能够代替基类。
如在策略模式中所有的算法都可以互换,只要子算法遵循超类中定义的接口,所以说符合LSP。
另外在我们的编码中也可以看出来,如果我们有一个超类SuperClass以及一个具体的子类SubClass,在超类中有一个public method这时如果你想在子类重写该方法并且把方法的访问级别调低,这编译都不会通过,因为违反了LSP原则。因为在这里如果客户程序用了引用了超类,很明显,用这个SubClass是无法替换SuperClass的。在这个例子中编译器做了检查,但是实际编码中有很多是编译器检查不出来的,如经典的正方形长方形问题。
3.Dependence Inversion Principle
DIP的原则是要依赖于抽象而不是依赖于具体,高层模块不应该依赖与低层模块,两者都应该依赖于抽象,这样不至于使得以来关系的影响扩大,对依赖对象的修改不会波及面太广,这种耦合应该是属于面向对象系统里的抽象耦合。也就是说我们最好使用这样的描述
人 object=new 男人();
而不是
男人 object=new 男人();
其实面向接口编程的思想也是基于DIP的思想的,抽象方法不实现是为了更灵活的实现。
在状态模式的运用中,Context类中为什么要引用State,而不是具体的状态类原因也在此:)
Interface Segregation Principle,Single Responsibility Principle,Composite/Aggregate Reuse Principle原则等下次学明白了再写:(
[1]引自《JAVA与模式》P70