在校学生,闲来无事复习设计模式,写此以记录。
对于单一职责的原定义是:There should never be more than one reason for a class to change.很简单的一句英文,有且只有一个原因能引起类的改变。即使对于英语很渣的我来说也能看懂,但要想理解却也是不容易。
单一职责主要应用于三个方面,接口、类和方法。我下面给出一些示例来详细阐述我的看法。
Java本就是面向接口编程,接口的定义基本定下了后面实现类的走向。记得刚接触Java没多久的时候,根本无法体会到接口存在的意义,很多时候都不去写接口,即时写也都是随便糊弄那种。为此写过下面这段很蠢的代码
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: 接口方法 * @Date 2019/10/22-17:08 */ public interface UserInfo { //登录验证 boolean loginVerify(User user,String username,String password); //注册信息验证 boolean registerVerify(User user); }
在实现类中
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: 实现类 * @Date 2019/10/22-17:11 */ public class UserLogin implements UserInfo{ //登录验证 @Override public boolean loginVerify(User user, String username, String password) { /** * 登录验证方法 */ return false; } //注册信息验证 @Override public boolean registerVerify(User user) { return false; } }
各位应该都能看出这段代码写的是有多差劲,我在一个接口中同时将登录和注册都写了进去,而在写登录和注册的实体类的时候又分开写,两个实体类都需要继承UserInfo接口,但这就造成登录实体类根本不需要注册这部分方法但我不得不去实现它。虽然这里也可以将登录和注册的实体类写在一起,这样就可以避免这种情况,但这样就造成了这个类的复杂程度进一步提高,如果项目越做越大,后面的维护就变得相当困难。
为此不如看看下面的这种写法
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: 登录接口 * @Date 2019/10/22-17:31 */ public interface LoginVerifyInter { //登录验证 boolean loginVerify(User user,String username,String password); }
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: 注册接口 * @Date 2019/10/22-17:32 */ public interface RegisterVerifyInter { //注册信息验证 boolean registerVerify(User user); }
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: 实现类 * @Date 2019/10/22-17:11 */ public class UserLogin implements LoginVerifyInter { //登录验证 @Override public boolean loginVerify(User user, String username, String password) { return false; } }
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: * @Date 2019/10/22-17:38 */ public class UserRegister implements RegisterVerifyInter { //注册信息验证 @Override public boolean registerVerify(User user) { return false; } }
这种方式就是简单的实现了接口的单一职责。我的理解,接口的单一职责就是将一种功能或者某一类基本相似的功能放到一起。
对于类的单一职责,我感觉没有很严格的规范,需求不同,实现的方式也不同,下面依旧按照登录和注册的例子列举两种不同的写法。
对于一个用户来说,一个用户就是一个实体,一个实体就应该包括相应的操作。
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: * @Date 2019/10/22-17:56 */ public class UserInfo { //登录操作 public boolean login(User user){ /** * 登录验证 */ return true; } //注册验证 public boolean register(User user){ /** * 注册验证 */ return true; } //更新信息 public boolean update(User user){ /** *更新信息 */ return true; } }
但如果用户中每一步操作的话又可以将每步操作分开写
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: * @Date 2019/10/22-18:03 */ class Login { //登录操作 public boolean login(User user){ /** * 登录验证 */ return true; } } class Register{ //注册验证 public boolean register(User user) { /** * 注册验证 */ return true; } } class Update{ //更新信息 public boolean update(User user){ /** *更新信息 */ return true; } }
虽然在这里第二种写法显得有些累赘,但确实严格按照单一职责的原则。没有孰优孰劣,只有适不适合,如果一个大型项目中一个用户的操作有相当大量的代码要进行实现的时候采用第二种方法分开写也没有任何错误。
个人感觉,方法的单一职责可以参考接口,尽量避免一个方法中进行多种类型的操作,参考《设计模式之禅》写出下面的示例
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: * @Date 2019/10/22-18:03 */ public class ChangeMessage { public boolean changeInfo(String username,String password,String sex){ //更新用户名 //更新密码 //更新性别 return true; } }
在这个示例中,当用户提交修改账户信息的指令后service层调用changeInfo方法,直接对所有需要修改的账户信息进行修改,但这造成一个问题,changeInfo这个方法的职责太过笼统,如果后期需要添加或者修改这部分的代码,可能会造成较大的麻烦。
下面对代码进行优化
/** * @Author: Lemon * @Program: Single Responsibility Principle * @Description: * @Date 2019/10/22-18:03 */ public class ChangeMessage { //更新用户名 public boolean changeUsername(String username){ //更新用户名操作 return true; } //更新密码 public boolean changePassword(String password){ //更新密码操作 return true; } //更新性别 public boolean changeSex(String sex){ //更新性别操作 return true; } }
改进后每个修改部分都有对应的方法,后期如果需要进行大量的修改会更加清晰,维护也更加方便。
单一职责最重要的特点就是提高代码的可维护性,可读性和扩展性。但其中很多部分还要结合实际需求去考虑,采用最合适的方式。
参考资料:《设计模式之禅》《一故事一设计模式》
如哪里写的有不对的地方,欢迎大家指正。