设计原则之旅(一):单一职责

这里写图片描述

简称:

单一职责原则的英文名称是Single Responsibility Principle, 简称 SRP。

定义:

SRP 的原话解释是: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 double 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 之旅”,查看更多相关博客

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值