开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。

        开闭原则的核心是:对扩展开放,对修改关闭

        白话意思就是我们改变一个软件时(比如扩展其他功能),应该通过扩展的方式来达到软件的改变,而不应爱修改原有代码来实现变化。

        遵循开闭原则设计出的模块具有两个主要特征:
    (1)对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。
    (2)对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。
        开闭原则算是前5中原则的一个抽象总结,前五种是开闭原则的一些具体实现,所以如果使用开闭原则,其实有点虚,因为它没有一个固定的模式,但是最终保证的是提高程序的复用性、可维护性等要求。 

        要使用这一原则还需要结合着它的思想“对扩展开放,对修改关闭”与其他的五大设计原则根据经验来开发项目。

 

一、实现方法 
        实现开闭原则的关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。
        我们在软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。
关于系统可变的部分,还有一个更具体的对可变性封装原则(Principle of Encapsulation of Variation, EVP),它从软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。
        我们在实际开发过程的设计开始阶段,就要罗列出来系统所有可能的行为,并把这些行为加入到抽象底层,根本就是不可能的,这么去做也是不经济的。因此我们应该现实的接受修改拥抱变化,使我们的代码可以对扩展开发,对修改关闭。

        

二、代码实现:

 

定义一个接口,寻×××

packagecom.loulijun.chapter6; 
publicinterfaceIFindGirl { 
//年龄 
publicintgetAge(); 
//姓名 
publicString getName(); 
//长相 
publicString getFace(); 
//身材 
publicString getFigure(); 
}

实现这个接口

packagecom.loulijun.chapter6; 
publicclassFindGirl implementsIFindGirl { 
privateString name; 
privateintage; 
privateString face; 
privateString figure; 
publicFindGirl(String name, intage, String face, String figure) 
this.name = name; 
this.age = age; 
this.face = face; 
this.figure = figure; 
@Override
publicintgetAge() { 
returnage; 
@Override
publicString getFace() { 
returnface; 
@Override
publicString getFigure() { 
returnfigure; 
@Override
publicString getName() { 
returnname; 
}

场景:大街上

packagecom.loulijun.chapter6; 
importjava.text.NumberFormat; 
importjava.util.ArrayList; 
publicclassStreet { 
privatefinalstaticArrayList<IFindGirl> girls = newArrayList<IFindGirl>(); 
//静态初始化块 
static
girls.add(newFindGirl("张含韵",23,"可爱型","165cm/47kg")); 
girls.add(newFindGirl("高圆圆",33,"时尚型","165cm/48kg")); 
girls.add(newFindGirl("章泽天",19,"清纯型","168cm/47kg")); 
publicstaticvoidmain(String args[]) 
System.out.println("----------美女在这里----------"); 
for(IFindGirl girl:girls) 
System.out.println("姓名:"+girl.getName()+" 年龄:"+girl.getAge()+ 
"  长相:"+girl.getFace()+"  身材:"+girl.getFigure()); 
}

运行结果:

----------美女在这里----------
姓名:张含韵 年龄:23 长相:可爱型 身材:165cm/47kg
姓名:高圆圆 年龄:33 长相:时尚型 身材:165cm/48kg
姓名:章泽天 年龄:19 长相:清纯型 身材:168cm/47kg

但是如果想独立分出一个外国美女的类别的话(比如增加一个国籍),可以通过修改接口、修改实现类、通过扩展来实现。

如果修改接口,也就意味着修改实现类,这样对项目的变动太大了,所以不推荐

如果修改实现类,这样虽能解决问题,但是明显有些牵强,如果需要其他变动的时候会显得逻辑混乱

所以,通过扩展来实现是最简单的方式

如何扩展:

可以定义一个IForeigner接口继承自IFindGirl,在IForeigner接口中添加国籍属性getCountry(),然后实现这个接口即可,然后就只需要在场景类中做稍微修改就可以了

packagecom.loulijun.chapter6; 
publicinterfaceIForeigner extendsIFindGirl { 
//国籍 
publicString getCountry(); 
}

实现接口

packagecom.loulijun.chapter6; 
publicclassForeignerGirl implementsIForeigner { 
privateString name; 
privateintage; 
privateString country; 
privateString face; 
privateString figure; 
publicForeignerGirl(String name, intage, String country, String face, String figure) 
this.name = name; 
this.age = age; 
this.country = country; 
this.face =face; 
this.figure = figure; 
@Override
publicString getCountry() { 
// TODO Auto-generated method stub 
returncountry; 
@Override
publicintgetAge() { 
// TODO Auto-generated method stub 
returnage; 
@Override
publicString getFace() { 
// TODO Auto-generated method stub 
returnface; 
@Override
publicString getFigure() { 
// TODO Auto-generated method stub 
returnfigure; 
@Override
publicString getName() { 
// TODO Auto-generated method stub 
returnname; 
}

然后在场景类中只需要修改如下代码即可,其他不变

girls.add(newForeignerGirl("Avirl",28,"美国","性感型","160cm/45kg"));

不过这些设计原则到不是绝对的,而是根据项目需求,实际需求来定夺使用