面向对象设计原则之单一职责原则
一、什么是单一职责原则
- 定义:应该有且仅有一个原因引起类的变更。简单点说,一个类,最好只负责一件事,只有一个引起它变化的原因。
- 理解:在OOP里面,高内聚、低耦合是软件设计追求的目标,单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。
- 难点:职责的划分。
- 在不同情景和生产环境下我们对职责的细化是不同的(职责单一的相对性)
- 单一职责原则提出的是一个评价接口是否优良的标准,但是职责和变化原因是不可度量的,因项目而异,因环境而异(不可度量性)
- 优点:
- 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
- 提高类的可读性,提高系统的可维护性;
- 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
二、实例
写一个生产手机必须要实现的接口,接口里面定义了一些手机的属性和行为,手机生产商如果要生产手机,必须要实现这些接口。
1、以手机为单一职责设计接口:
手机接口
public interface IMobile {
//手机壳样式
void setShell(String shell);
String getShell();
//手机屏幕尺寸
void setSize(int size);
int getSize();
//手机型号
void setType(String type);
String getType();
//打电话
void ringUp();
//接电话
void answer();
//上网
void surfInternet();
}
具体手机类
public class Moblie implements IMobile {
String shell;
int size;
String type;
@Override
public void setShell(String shell) {
this.shell=shell;
}
@Override
public String getShell() {
return shell;
}
@Override
public void setSize(int size) {
this.size=size;
}
@Override
public int getSize() {
return size;
}
@Override
public void setType(String type) {
this.type=type;
}
@Override
public String getType() {
return type;
}
@Override
public void ringUp() {
System.out.println("正在打电话");
}
@Override
public void answer() {
System.out.println("正在接电话");
}
@Override
public void surfInternet() {
System.out.println("正在上网");
}
}
这种设计很有争议,单一职责原则要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情,原则上来说,我们以手机作为单一职责去设计,也是有一定的道理的,因为我们接口里面都是定义的手机相关属性和行为,引起接口变化的原因只可能是手机的属性或者行为发生变化,从这方面考虑,这种设计是有它的合理性的,如果你能保证需求不会变化或者变化的可能性比较小,那么这种设计就是合理的。但是现实生活中的手机总在更新着,比如给手机增加一个存储内存的属性,我们的接口和实现类都得改。在比如,给手机增加一个拍照的功能,我们的接口和实现类就又得改,变得很麻烦。
2、接口细化
手机属性接口
public interface IMobileProperty {
//手机壳样式
void setShell(String shell);
String getShell();
//手机屏幕尺寸
void setSize(int size);
int getSize();
//手机型号
void setType(String type);
String getType();
}
手机功能接口
public interface IMobileFunction {
//打电话
void ringUp();
//接电话
void answer();
//上网
void surfInternet();
}
手机属性实现类
public class MobileProperty implements IMobileProperty {
String shell;
int size;
String type;
@Override
public void setShell(String shell) {
this.shell=shell;
}
@Override
public String getShell() {
return shell;
}
@Override
public void setSize(int size) {
this.size=size;
}
@Override
public int getSize() {
return size;
}
@Override
public void setType(String type) {
this.type=type;
}
@Override
public String getType() {
return type;
}
}
手机功能实现类
public class MobileFunction implements IMobileFunction {
@Override
public void ringUp() {
System.out.println("正在打电话");
}
@Override
public void answer() {
System.out.println("正在接电话");
}
@Override
public void surfInternet() {
System.out.println("正在上网");
}
}
具体手机类
public class XiaoMiMobile {
private IMobileProperty mobileProperty;
private IMobileFunction mobileFunction;
public XiaoMiMobile(IMobileProperty mobileProperty, IMobileFunction mobileFunction) {
this.mobileProperty = mobileProperty;
this.mobileFunction = mobileFunction;
}
public IMobileProperty getMobileProperty() {
return mobileProperty;
}
public void setMobileProperty(IMobileProperty mobileProperty) {
this.mobileProperty = mobileProperty;
}
public IMobileFunction getMobileFunction() {
return mobileFunction;
}
public void setMobileFunction(IMobileFunction mobileFunction) {
this.mobileFunction = mobileFunction;
}
}
这种设计如果只是增加属性就修改IMobileProperty和MobileProperty;如果增加功能就修改IMobileFunction 和MobileFunction 。原则上,我们将手机的属性和功能分开了,使得职责更加明确,所有的属性都由IMobileProperty接口负责,所有的功能都由IMobileFunction 接口负责,如果是需求的粒度仅仅到了属性和功能这一级,这种设计确实是比较好的。但是,如果需求的力度再小一些,比如:老人机只需要一些基础的功能。比如它只需要打电话和接电话的功能,但是它也要实现IMobileFunction 接口,上网的方法也要实现。再比如某一天我们给手机增加一个玩游戏的功能,就要在IMobileFunction 接口增加一个方法playGame()。但是老人机不用这个功能,可是我们实现了接口就要实现这个方法。所以我们可以在把接口继续细化。
3、最终细化
手机基础属性接口
public interface IMboileBaseProperty {
//手机屏幕尺寸
void setSize(int size);
int getSize();
//手机型号
void setType(String type);
String getType();
}
手机扩展属性接口
public interface IMobileExtensionProperty {
//手机壳样式
void setShell(String shell);
String getShell();
}
手机基础功能接口
public interface IMobileBaseFunction {
//打电话
void ringUp();
//接电话
void answer();
}
手机扩展功能接口
public interface IMobileExtensionFunction {
//上网
void surfInternet();
}
手机基础属性实现类
public class MobileBaseProperty implements IMboileBaseProperty {
int size;
String type;
@Override
public void setSize(int size) {
this.size=size;
}
@Override
public int getSize() {
return size;
}
@Override
public void setType(String type) {
this.type=type;
}
@Override
public String getType() {
return type;
}
}
手机扩展属性实现类
public class MobileExtensionProperty implements IMobileExtensionProperty {
String shell;
@Override
public void setShell(String shell) {
this.shell=shell;
}
@Override
public String getShell() {
return shell;
}
}
手机基础功能实现类
public class MobileBaseFunction implements IMobileBaseFunction {
@Override
public void ringUp() {
System.out.println("正在打电话");
}
@Override
public void answer() {
System.out.println("正在接电话");
}
}
手机扩展功能实现类
public class MobileExtensionFunction implements IMobileExtensionFunction {
@Override
public void surfInternet() {
System.out.println("正在上网");
}
}
三、总结
理解单一职责原则,最重要的就是理解职责的划分,职责划分的粒度取决于需求的粒度。没有最好的设计,只有最适合的设计。