对象类应该是开放的以便于扩展,又要是封闭的以利于修改。我们应该可以不改动原有类的基础上,就能够在系统中增加新的功能。
OCP中的一个原则就是减少类之间的耦合,在抽象层次建立类之间的关联。不要在两个具体类之间建立关系,而在具体类和抽象类之间建立关系,或者按Java中的说法,在具体类和接口之间建立关系。
OCP是所有关于类的原理中最重要的一个。实际上其它关于类的原理都是从OCP演化而来的。
2、Liskov替代原理(Liskov Substitution Principle,LSP)
LSP可以看作是OCP的一种扩充,它是Barbara Liskov的贡献,从Bertrand Meyer的契约式设计(Design by Contract)演化而来。
OCP以抽象关联为中心。LSP虽然也很倚重于抽象关联,但也依赖于先验条件(precondition)和后验条件(postcondition),先验条件和后验条件的概念在约定式设计中得到形式化,这就是LSP与约定式设计之间的关系。
先验条件是关于在方法被调用之前所必须满足的条件的约定。后验条件是关于在方法被调用后所必须达到的条件的约定。如果先验条件不满足,就不应该调用该方法;如果后验条件不满足,方法就不应该返回。
在Java中,为了遵从LSP,我们应该确保开发者对基类的每一个方法都定义了其前提条件和事后条件。在定义子类时,我们必须遵从这些前提条件和事后条件。
public abstract deposit(int amt) throws InvalidAmountException
public abstract deposit(int amt) throws Exception
3、依赖性倒置原理(Dependency Inversion Principle,DIP)
依赖于抽象类,不要依赖于具体类。
DIP形式化了抽象耦合的概念,清楚地表述了我们应该在抽象层耦合,而不要在具体层耦合。
从编程上来说,当我们不确切是否类的实现在将来会改变时,都应该应用这一原理。
4、接口分离原理(Interface Segregation Principle,ISP)
多个专门的接口优于一个单一的通用接口。
任何接口都应具有高内聚性。在Java中,接口是一种引用数据类型,其上可以定义方法,但不能有实现。实质上,接口就是所有方法都是抽象方法的抽象类。
在定义接口时,明确接口在应用程序中所扮演的角色很重要。实际上,接口提供了灵活性:它可以使对象呈现为一种接口对象类型。因此,接口就是对象在其生命期中在某些时刻所扮演的某种简单的角色。
我们在设计接口及接口上的操作时,应当遵循不要让接口具有多重角色。一个接口应该保证实现该接口的类的实例对象可以只呈现为单一的角色。
5、构成重用原理(Composite Reuse Principle,CRP)
在面向对象的系统中,最具灾难性的错误就是把继承作为重用的主要机制, CRP可以避免我们犯这样的错误。
继承可以看作是一种在特殊性中概括出一般性的关系——就是说,类树中级别高的类是所派生类的一种概括性版本。换句话说,基类总是需要定义一组缺省的特性,这些特性必须能够应用于从它派生出的任何类。实际上,任何时候如果出现了必须对基类的方法进行重载的话,都说明,我们的基类不是一个对所有其派生类的绝好的概括,它太具体化了,在这里不应该做为基类。因此,如果我们要在基类中定义缺省的行为的话,一定要保证这个行为适用于所有的派生类。 对象构成物的多态性优于继承。
6、最少知识原理(Principle of Least Knowledge,PLK)
PLK也称为Demeter法则,其基本思想是避免调用那些调用其它对象的方法所返回的对象上的任何方法。
在一个类上的操作中,只有类本身、操作的参数对象、操作中创建的对象和类包含的实例对象等的操作可以被调用。
PLK原理建议我们尽量调用所包含的对象的方法,不要从中获得其它对象的引用。其主要好处在于,我们不需要了解被调用方法的对象的任何内部组成结构。 试做比较:Sample2更为妥当。
public class Sample1{
public void lawTest(AnObjet o)
{
AnotherObject ao = o.get();
ao.doSomething();
}
}
public class Sample2{
public void lawTest(AnObjet o)
{
o.doSomething();
}
}
PLK当然也有会带来一些明显的不足,因为我们不得不创建许多方法用来把调用传递给内部对象。这可能会使我们定义出巨大的笨重的公共接口。
当然我们可以用另一种方式来遵从PLK,那就是在任何需要通过方法返回对象引用时,都要保证方法返回的类型是接口数据类型。因为在这里我们没有直接依赖于一些复杂对象的具体实现,而是依赖于组成这个复杂对象的抽象类。
实际上,Java中的许多类也是用这种方式解决这个问题的。