定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。开闭原则可能是设计模式六项原则中定义最模糊的一个了,它只告诉我们对扩展开放,对修改关闭,可是到底如何才能做到对扩展开放,对修改关闭,并没有明确的告诉我们。以前,如果有人告诉我“你进行设计的时候一定要遵守开闭原则”,我会觉的他什么都没说,但貌似又什么都说了。因为开闭原则真的太虚了。
例子
定义一个接口,寻找美女
package com.loulijun.chapter6;
public interface IFindGirl
{
//年龄
public int getAge();
//姓名
public String getName();
//长相
public String getFace();
//身材
public String getFigure();
}
实现这个接口
package com.loulijun.chapter6;
public class FindGirl implements IFindGirl
{
private String name;
private int age;
private String face;
private String figure;
public FindGirl(String name, int age, String face, String figure) {
this.name = name;
this.age = age;
this.face = face;
this.figure = figure;
}
@Override
public int getAge() {
return age;
}
@Override
public String getFace() {
return face;
}
@Override
public String getFigure() {
return figure;
}
@Override
public String getName() {
return name;
}
}
场景:大街上
package com.loulijun.chapter6;
import java.text.NumberFormat;
import java.util.ArrayList;
public class Street
{
private final static ArrayList<IFindGirl> girls = new ArrayList<IFindGirl>();
//静态初始化块
static
{
girls.add(new FindGirl("张含韵",23,"可爱型","165cm/47kg"));
girls.add(new FindGirl("高圆圆",33,"时尚型","165cm/48kg"));
girls.add(new FindGirl("章泽天",19,"清纯型","168cm/47kg"));
}
public static void main(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(),然后实现这个接口即可,然后就只需要在场景类中做稍微修改就可以了
package com.loulijun.chapter6;
public interface IForeigner extends IFindGirl {
//国籍
public String getCountry();
}
实现接口
package com.loulijun.chapter6;
public class ForeignerGirl
implements IForeigner
{
private String name;
private int age;
private String country;
private String face;
private String figure;
public ForeignerGirl(String name, int age, String country, String face, String figure) {
this.name = name;
this.age = age;
this.country = country;
this.face = face;
this.figure = figure;
}
@Override
public String getCountry() {
// TODO Auto-generated method stub
return country;
}
@Override
public int getAge() {
// TODO Auto-generated method stub
return age;
}
@Override
public String getFace() {
// TODO Auto-generated method stub
return face;
}
@Override
public String getFigure() {
// TODO Auto-generated method stub
return figure;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return name;
}
}
然后在场景类中只需要修改如下代码即可,其他不变
girls.add(new ForeignerGirl("Avirl",28,"美国","性感型","160cm/45kg"));
不过这些设计原则到不是绝对的,而是根据项目需求,实际需求来定夺使用