简称:
单一职责原则的英文名称是Single Responsibility Principle, 简称RSP。
定义:
RSP 的原话解释是:There should never be more than one reason for a class to change. ( 不要存在多于一个导致类变更的原因 ) 。通俗的说,即一个类只负责一项职责,而不应该同时负责多个职责。
问题由来:
类 T 负责两个不同的职责:职责 P1,职责 P2。当由于职责 P1 需求发生改变而需要修改类 T 时,有可能会导致原本运行正常的职责 P2 功能发生故障。
解决方案:
遵循单一职责原则。分别建立两个类 T1、T2,使 T1 完成职责 P1 功能,T2 完成职责 P2 功能。这样,当修改类 T1 时,不会使职责 P2 发生故障风险;同理,当修改 T2 时,也不会使职责 P1 发生故障风险。
举例说明
需求:实现一个购物车功能,购物车主要用于存放图书,根据书的价格,计算所有图书的总价格。
示例代码:
图书类:
//商品类
public class Book {
private String name; //商品名称
private double price; //商品价格
public Book(String name, double price) {
super();
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
购物车类:
//购物车
public class ShoppingCart { //存放添加的图书
private List<Book> list = new ArrayList<Book>(); //添加图书
public void addBook(Book book){
list.add(book);
};
//删除图书
public void removeBook(Book book){
list.remove(book);
};
//结账
public int checkOut(){
double total = 0;
for(Book book : list){
total = total+book.getPrice();
}
return total;
}
}
客户端代码:
//客户端代码
public class Client {
public static void main(String[] args) {
Book book1 = new Book("红楼梦",50);
Book book2 = new Book("三国演义",40);
Book book3 = new Book("西游记",30);
Book book4 = new Book("水浒传",20);
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.addBook(book1);
shoppingCart.addBook(book2);
shoppingCart.addBook(book3);
shoppingCart.addBook(book4);
double total = shoppingCart.checkOut();
System.out.println("所有图书价格为:"+total);
}
}
输出结果:
OK,以上代码“ 完美 ”解决客户需求。
当然,所谓的 完美 ”是建立在需求不变的情况下。很遗憾,客户的需求总是在不断变化的,程序上线以后,客户发现没有打折功能,现在,客户要求对所有的“ 红楼梦 ”图书打 8 折促销,那我们的程序改如何修改呢?
有些小伙伴应该可能会这么修改:修改 ShoppingCart 类中的 checkOut 方法,快速实现客户需求.
//购物车
public class ShoppingCart { //存放添加的图书
private List<Book> list = new ArrayList<Book>(); //添加图书
public void addBook(Book book){
list.add(book);
};
//删除图书
public void removeBook(Book book){
list.remove(book);
};
//结账
public double checkOut(){
double total = 0;
for(Book book : list){
if("红楼梦".equals(book.getName())){
total = total+book.getPrice()*0.8;
}else{
total = total+book.getPrice();
}
}
return total;
}
}
输出结果满足客户需求
过来几天,客户需求又有了新的变化,对所有的“ 红楼梦 ”图书打 8 折促销,所有的“ 西游记 ”打 6 折。按照之前的修改逻辑,如下:
//购物车
public class ShoppingCart { //存放添加的图书
private List<Book> list = new ArrayList<Book>(); //添加图书
public void addBook(Book book){
list.add(book);
};
//删除图书
public void removeBook(Book book){
list.remove(book);
};
//结账
public double checkOut(){
double total = 0;
for(Book book : list){
if("红楼梦".equals(book.getName())){
total = total+book.getPrice()*0.8;
}if("西游记".equals(book.getName())){
total = total+book.getPrice()*0.6;
}else{
total = total+book.getPrice();
}
}
return total;
}
}
同样快速满足客户需求
示例代码分析
上边的代码,初看起来很像也没太多问题,而且还能够快速满足客户端需求,对某些项目来说,快速满足客户需求是第一要素,但是,这样写存在什么隐患呢?
客户的需求总是的在不停的变化的,按照上边的设计,客户的每一次需求变动,我们多需要修改我们的 ShoppingCart 类,客户的需求变化频率越高,需求越复杂,ShoppingCart 类也改动也频繁, 复杂度也是成倍增加,而且代码的复用性很差,如果客户需要改回原来的选取,我们同样还是需要修改我们的ShoppingCart 类, 改来改去之后,我们会发现我们之前设计的 ShoppingCart 类的 checkOut 方法已经变的非常臃肿了,以至于没法维护了。
隐患分析:
上述代码之所以会存在那么多的隐患,根本的原因在于 ShoppingCart 的设计违背的“单一职责原则”与“ 开闭原则 ”(后期会讲解),checkOut 方法即承担了统计总价职责,又承担了打折优惠职责,以至于优惠职责需求变动的时候,代码统计总价的逻辑也受到相应的影响。
解决方案:
遵循单一职责原则,拆分 ShoppingCart 职责,ShoppingCart 的职责是收集图书,输出所有图书的结账时的总价格,至于打折优惠职责交给专门的类去处理。考虑的到打折优惠是一个变化的需求,所有,我们应该将这种行为提取处来,做为一个抽象接口,具体实现交给实现类去处理。
示例代码:
提取打折策略抽象接口:
//打折策略
public interface DiscountStrategy { //打折抽象方法
public double discount(List<Book> list);
}
实现默认打折策略:
//默认打折策略:不打折
public class DefalutDiscountStrategy implements DiscountStrategy{
@Override
public double discount(List<Book> list) {
double total = 0;
for(Book book : list){
total = total+book.getPrice();
}
return total;
}
}
购物车与打折实体通过打折策略抽象接口建立关系:
//购物车
public class ShoppingCart {
//存放添加的图书
private List<Book> list = new ArrayList<Book>();
private DiscountStrategy discountStrategy; //添加图书
public void addBook(Book book){
list.add(book);
};
//删除图书
public void removeBook(Book book){
list.remove(book);
};
//设置打折策略
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
//结账
public double checkOut(){
if(discountStrategy== null){
//客户没有设置打折策略,使用默认打折策略
discountStrategy = new DefalutDiscountStrategy();
}
double total = discountStrategy.discount(list); return total;
}
}
客户端,根据业务需求,自定义打折优惠策略
//红楼梦打 8 折策略
public class FirstDiscountStrategy implements DiscountStrategy{
@Override
public double discount(List<Book> list) {
double total = 0;
for(Book book : list){
if("红楼梦".equals(book.getName())){
total = total+book.getPrice()*0.8;
}else{
total = total+book.getPrice();
}
}
return total;
}
}
客户端代码:
//客户端代码
public class Client {
public static void main(String[] args) {
Book book1 = new Book("红楼梦",50);
Book book2 = new Book("三国演义",40);
Book book3 = new Book("西游记",30);
Book book4 = new Book("水浒传",20);
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.addBook(book1);
shoppingCart.addBook(book2);
shoppingCart.addBook(book3);
shoppingCart.addBook(book4); /
/客户设置打折策略
DiscountStrategy discountStrategy = new FirstDiscountStrategy();
shoppingCart.setDiscountStrategy(discountStrategy);
double total = shoppingCart.checkOut();
System.out.println("所有图书价格为:"+total);
}
}
后期,客户业务需求如果变化,只需要实现 DiscountStrategy 接口,自己定义属于自己的策略,而我们的 ShoppingCart 类则无需做任何修改,ShoppingCart 的职责也非常清晰。
总结分析:
遵循单一职责原则优点:
减低类的复杂度,一个类只负责一项职责了,其业务逻辑自然就变简单了。
提示类的可读性,提升系统的维护性。
减低需求变化代理的风险,如果遵循了单一职责,类之间的耦合性也就减低了,内聚性增强了,需求变换带来的风险自然就降低了。
欢迎关注本人公众号:“Android 之旅”,查看更多相关博客