定义:应该有且仅有一个原因引起类的变更。即单一指责原则要求一个借口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情。
优点:1.类的复杂性降低,实现什么职责都有清晰明确的定义
2.可读性提高,复杂性降低,那当然可读性提高了。
3.可维护性提高,可读性提高,那当然更容易维护了。
4.变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性,维护性都有非常大的帮助。
例:通常写用户管理都是一个接口 一个实现类
<<用户管理UML.vsdx>>
/**
*
* @author liang
* 用户信息管理
*/
public interface IUserInfo {
//设置用户的ID
public void setUserID(String userID);
//获得用户的ID
public String getUserID();
//设置用户的密码
/** */
public void setPassword(String password);
//获得用户的密码
public String getPassword();
//设置用户的名字
public void setUserName(String userName);
//获得用户的名字
public String getUserName();
//修改用户的密码
public boolean changePassword(String oldPassword);
//删除用户
public boolean deleteUser();
//用户映射
public void mapUser();
}
/**
*
* @author liang
* 用户管理的实现类
*/
public class UserInfo implements IUserInfo{
private String userName;
private String userID;
private String password;
public void setUserID(String userID) {
this.userID = userID;
}
public String getUserID() {
return userID;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
//修改用户密码
public boolean changePassword(String oldPassword){
System.out.println("密码修改成功...");
return true;
}
//删除用户
public boolean deleteUser(){
System.out.println("删除用户成功...");
return true;
}
//用户映射
public void mapUser()
System.out.println("用户映射成功...");
}
}
/**
*
* @author liang
*业务类调用
*/
public class Client {
public static void main(String[] args) {
IUserInfo userInfo = new UserInfo();
userInfo.changePassword("abc");
}
}
用户属性和用户行为没有分开是一个严重的错误!应该把用户的信息抽取程一个BO(Business Object业务对象),把行为抽取成一个Biz(Business Logic业务逻辑)。
<<修改后用户管理UML.vsdx>>
/**
*
* @author liang
* 用户的业务对象
*/
public interface IUserBO {
// 设置用户ID
void setUserID(String userID);
// 获取用户ID
String getUserID();
// 设置用户密码
void setPassword(String password);
// 获得用户密码
String getPassword();
// 设置用户姓名
void setUserName(String userName);
// 获得用户姓名
String getUserName();
}
/**
*
* @author liang
*用户信息管理
*/
public interface IUserBiz {
//修改用户密码
boolean changePassword(String oldPassword);
//删除用户
boolean deleteUser();
//用户映射
void mapUser();
//增加一个组织
boolean addOrg(int orgID);
//增加一个角色
boolean addRole(int roleID);
}
/**
*
* @author liang
*
*/
public interface IUserInfo extends IUserBiz,IUserBO{
}
/**
*
* @author liang
* 用户管理的实现类
*/
public class UserInfo implements IUserInfo {
private String userID;
private String password;
private String userName;
public void setUserID(String userID) {
this.userID = userID;
}
public String getUserID() {
return userID;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
// 修改用户密码
public boolean changePassword(String oldPassword) {
System.out.println("修改密码成功");
return true;
}
// 删除用户
public boolean deleteUser() {
System.out.println("删除成功");
return true;
}
// 用户映射
public void mapUser() {
System.out.println("映射成功");
}
// 增加一个组织
public boolean addOrg(int orgID) {
System.out.println("增加组织");
return true;
}
// 增加一个角色
public boolean addRole(int roleID) {
System.out.println("增加角色");
return true;
}
}
/**
*
* @author liang
*业务类调用
*/
public class Client {
public static void main(String[] args) {
IUserInfo userInfo = new UserInfo();
//我要复制了,我就认为它是一个纯粹的BO
IUserBO userBO = (IUserBO)userInfo;
userBO.setPassword("abc");
//我要执行动作了,我就认为是一个业务逻辑类
IUserBiz userBiz = (IUserBiz)userInfo;
userBiz.deleteUser();
}
}
从新拆分成两个接口,IUserBO负责用户属性,收集和反馈用户的属性信息,IUserBiz负责用户行为,完成用户信息的维护和变更。
电话通话的时候有4个过程发生:拨号,通话,回应,挂机。其类图如下:
<<电话类图.vsdx>>
public interface IPhone {
// 拨通电话
public void dial(String phoneNumber);
// 通话
public void chat(Object o);
// 通话完毕,挂电话
public void hangup();
}
单一责任原则要求一个借口或者类只有一个原因引起变化,也就是一个接口或类只能一个职责,它就负责一件事情。
IPhone这个接口可不是只有一个职责,它包含两个职责:一个是协议管理,一个是数据传输。Dial()和hangup()两个方法实现是协议管理,分别负责拨号接通和挂机,chat()实现的是数据的传输。协议接通的变化会引起这个接口或者实现类的变化,数据传送也会引起这样的变化,但是协议管理和数据传输这两个职责不相互影响。那就考虑拆分成两个接口。
<<职责分明的电话类图.vsdx>>这样是存在缺陷的,一个手机类要把ConnextionManager和DataTransfer组合在一起才能使用。组合是一种强耦合关系。这样的耦合还不如使用接口实现的方式。
<<简洁清晰职责分明的电话类图.vsdx>>
/**
*
* @author liang
* 协议管理类接口
*/
public interface IConnectionManager {
// 拨打电话
void dial(String phoneNumber);
// 挂断电话
void hangup();
}
/**
*
* @author liang
*数据传输类接口
*/
public interface IDataTransfer {
//输出传输
void DataTransfer(IConnectionManager cm);
}
/**
*
* @author liang
*实现类
*/
public class Phone implements IDataTransfer, IConnectionManager {
@Override
public void DataTransfer(IConnectionManager cm) {
System.out.println("正在通话");
}
@Override
public void dial(String phoneNumber) {
System.out.println("拨打电话");
}
@Override
public void hangup() {
System.out.println("挂断电话");
}
}
/**
*
* @author liang
*测试类
*/
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.dial("127XXXXXXXX");
phone.DataTransfer(phone);
phone.hangup();
}
}
这样的设计才是比较合理的,一个类实现了两个接口,把两个职责融合在一个类中。这个Phone由两个原因引起变化。如果真要实现类的单一职责,这个就必须使用上面的职责分明的电话类图的模式了,这样会引起类间耦合过重,类的数量增加等问题。
注意:单一职责原则提出了一个编写程序的标准,用"职责"和"变化原因"来衡量接口或类设计的是否优良。但是这两者都是不可度量的,因项目环境而已。
对于单一职责原则,建议是接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。
下面是电话职责的另外一种实现
/**
*
* @author liang
* 协议管理类接口
*/
public interface IConnectionManager {
// 拨打电话
void dial(String phoneNumber);
// 挂断电话
void hangup();
}
/**
*
* @author liang
*数据传输类接口
*/
public interface IDataTransfer {
//输出传输
void DataTransfer(IConnectionManager cm);
}
/**
*
* @author liang
*实现类
*/
public class Phone implements IDataTransfer {
@Override
public void DataTransfer(IConnectionManager cm) {
cm.dial("127XXXXXXXX");
System.out.println("正在通话");
cm.hangup();
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
IConnectionManager cm = new IConnectionManager() {
@Override
public void hangup() {
System.out.println("开始拨打电话");
}
@Override
public void dial(String phoneNumber) {
System.out.println("挂断电话");
}
};
phone.DataTransfer(cm);
}
}
|