单一职责原理讲解coding

单一职责原则是说一个类只有一个发生变更的原因,这个是在类层之上的,那在接口层次,方法层次,

也要遵循单一原则,那我们现在结合一些例子来说明一下,那单一职责的好处呢,首先降低类的复杂性,提高可读性,

提高维护性,最重要的是变更的时候风险率降低,那我们现在结合例子一起来体会一下,单一职责原则,首先呢我们

现在有一个类

这个类图还是非常非常简单的,其实是由Test创建这两个类,这个Bird类现在已经不用了,那职责在类的层次上,

还是比较清晰的,那刚刚说的是类的职责上,那我们再看一下在接口级别上,还是用我们的课程举例,ICourse是一个

接口

package com.learn.design.principle.singleresponsibility;

/**
 * 鸟主要的移动方式我们来学一下
 * 
 * @author Leon.Sun
 *
 */
public class Bird {
	/**
	 * 主要是用翅膀飞
	 * 这里面我们增加一个birdName
	 * 鸟的名称
	 * 
	 * 
	 * @param birdName
	 */
    public void mainMoveMode(String birdName){
    	/**
    	 * 这里要做一个判断
    	 * 这个就非常符合我们的日常开发
    	 * 一个需求来说
    	 * 我们在这里做一个修改
    	 * 其实是最快的方式
    	 * 我们在实际开发中还要考虑开发的成本
    	 * 时间,进度
    	 * 完全遵守单一原则
    	 * 有的时候还是要看实际情况的
    	 * 但是我们有一颗按照原则写代码的心
    	 * 条件允许的情况下
    	 * 还是要大家来遵守这些设计原则
    	 * 那需求现在增加了鸵鸟
    	 * 那我们在原有的类加一个判断
    	 * 
    	 * 
    	 */
        if("鸵鸟".equals(birdName)){
        	/**
        	 * 如果是鸵鸟的话
        	 * 用脚走
        	 * 鸵鸟用脚走
        	 * 
        	 * 这里面有bug了
        	 * System.out.println(birdName+"用翅膀飞");
        	 * 下一行又走到了这里
        	 * 这里就是一个典型的一个例子
        	 * 那我们在扩展的时候
        	 * 对于一些边界
        	 * 我们这里增加了if
        	 * 但是下边并没有放到else里
        	 * 这种情况也比较常见
        	 * 正确的写法应该是这样的
        	 * 如果继续扩展下来
        	 * 包括企鹅
        	 * 企鹅也是鸟
        	 * 企鹅也是用脚走
        	 * 那鸵鸟跟企鹅呢
        	 * 我们还可以细分一下
        	 * 那如果现在再传来一些特殊的鸟类
        	 * 那我们的这个方法还要继续扩展
        	 * 还记得我们说的
        	 * 单一职责原则其中有一个最重要的好处
        	 * 他就是变更时风险率降低
        	 * 那现在是不遵循单一原则的
        	 * 所以风险还是比较大
        	 * 当然我们看到的这个方法还比较简单
        	 * 那实际的业务比这个复杂
        	 * 边界比这个判断要 更多一些
        	 * 那我们现在从类的形式来把这个拆分开
        	 * 
        	 */
            System.out.println(birdName+"用脚走");
        }else{
            System.out.println(birdName+"用翅膀飞");
        }
    }
}
package com.learn.design.principle.singleresponsibility;

/**
 * 我们 创建一个类FlyBird
 * 
 * 
 * @author Leon.Sun
 *
 */
public class FlyBird {
    public void mainMoveMode(String birdName){
        System.out.println(birdName+"用翅膀飞");
    }
}
package com.learn.design.principle.singleresponsibility;

/**
 * 我们再创建一个类WalkBird
 * 走路的鸟
 * 
 * 
 * @author Leon.Sun
 *
 */
public class WalkBird {
    public void mainMoveMode(String birdName){
        System.out.println(birdName+"用脚走");
    }
}
package com.learn.design.principle.singleresponsibility;

/**
 * 我们写一个测试类
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Test {
    public static void main(String[] args) {
    	  /**
    	   * new一个bird
    	   * 我们传进去一个大雁
    	   * 大雁用翅膀飞
    	   * 我现在再传一个鸵鸟
    	   * 那鸵鸟用翅膀飞
    	   * 就不对
    	   * 因为鸵鸟飞不起来
    	   * 那我们再看一下这个类
    	   * 
    	   * 可以看到大雁没有问题
    	   * 用翅膀飞
    	   * 那鸵鸟用脚走
    	   * 鸵鸟用翅膀飞
    	   * 
    	   * 
    	   */
//        Bird bird = new Bird();
//        bird.mainMoveMode("大雁");
//        bird.mainMoveMode("鸵鸟");

    	/**
    	 * 那我们飞的鸟和走的鸟都区分一下
    	 * 这块还是比较简单的
    	 * 我们应用层来判断这个逻辑
    	 * 如果是大雁
    	 * 我们就用FlyBird
    	 * 如果是鸵鸟我们就用行走的Bird
    	 * 那这个就是类的单一原则的体现
    	 * 我们把一个类进行拆分
    	 * 这样我们就使每个类的方法职责单一的
    	 * 比较简单
    	 * 也不至于引入的时候出现新的问题
    	 * 那我们来看一下类图
    	 * 
    	 * 
    	 */
        FlyBird flyBird = new FlyBird();
        flyBird.mainMoveMode("大雁");

        WalkBird walkBird = new WalkBird();
        walkBird.mainMoveMode("鸵鸟");

    }
}

课程类实现两个接口,我们可以通过实现一个接口或者多个接口,来组合出这个实现类的一个实现,但是我也可以实现

一个接口,也就是我们这个实现类实现什么职责呢,都是有清楚明确的定义,复杂性也是降低了,复杂性降低了可读性也就提高了,

可读性提高了也就更容易维护了,可读性也就提高了,同时变更引起的影响降低了,一个接口的更改只对相应的实现类有影响,

与其他的接口无关,这一点对项目的帮助是非常大的,这个就是从接口级别上来讲,单一职责,刚刚类,接口,我们再来看看

单一职责
package com.learn.design.principle.singleresponsibility;

/**
 * 这个是一个接口
 * 我们来看一下这个接口
 * 对于课程来说
 * 获得课程的名字
 * 获得课程的视频
 * 那在接口的基础上做单一原则的话
 * 也就是ICourse这个接口
 * 可不只有一个职责
 * 首先一个大的职责是获得课程的信息
 * 比如名称和视频字节流
 * 另外一个职责是管理课程
 * 和课程内容无关
 * 例如学习课程
 * 那如果学习课程需要获取name
 * 视频字节流
 * 如果退了这个课程呢
 * 可能就获取不了这个名字和字节流了
 * 因为这个课程已经被我退掉了
 * 那退会影响获取内容的变化
 * 这两个职责是互相影响的
 * 现在我们退了这个课程
 * 那获取课程信息的时候
 * 我们这个实现就获取不到
 * 就可以了
 * 那整体来看
 * 这个接口两个职责
 * 获取课程相关信息
 * 还有课程管理上的相关处理
 * 那我们就可以把这个接口拆成两个接口
 * 一个是获取课程信息的接口
 * 另外是课程管理的接口
 * 我们来尝试一下
 * 
 * 
 * @author Leon.Sun
 *
 */
public interface ICourse {
	/**
	 * 我是可以获得课程的名称的
	 * 
	 * 
	 * @return
	 */
    String getCourseName();
    /**
     * 我还可以获得一个视频
     * 拿到一个字节流
     * 
     * @return
     */
    byte[] getCourseVideo();

    /**
     * 学习课程
     * 
     */
    void studyCourse();
    /**
     * 退款
     * 
     */
    void refundCourse();

}
package com.learn.design.principle.singleresponsibility;

public interface ICourseManager {
	/**
	 * 然后把这个拿到这边
	 * 
	 */
    void studyCourse();
    void refundCourse();
}
package com.learn.design.principle.singleresponsibility;

/**
 * 注意这个关键字是interface
 * 
 * @author Leon.Sun
 *
 */
public interface ICourseContent {
	/**
	 * 我们把这两个拿到这里边
	 * 
	 * @return
	 */
    String getCourseName();
    byte[] getCourseVideo();
}
package com.learn.design.principle.singleresponsibility;

/**
 * 我们现在来实现一个Course的实现类
 * 他来实现ICourseManager这个接口
 * 同时来实现ICourseContent这个接口
 * CourseImpl实现类实现这两个接口
 * 我们来看一下类图
 * 
 * 
 * @author Leon.Sun
 *
 */
public class CourseImpl implements ICourseManager,ICourseContent {
    @Override
    public void studyCourse() {

    }

    @Override
    public void refundCourse() {

    }

    @Override
    public String getCourseName() {
        return null;
    }

    @Override
    public byte[] getCourseVideo() {
        return new byte[0];
    }
}
package com.learn.design.principle.singleresponsibility;

/**
 * 那我们简单的总结一下
 * 类的单一职责原则和接口方法的单一职责是一样的
 * 但是我们在实际的项目开发中
 * 我们在创建类的时候
 * 包括依赖组合聚合
 * 受很多因素的影响
 * 包括我们项目的规模
 * 还有项目的周期
 * 技术人员的水平
 * 还有对进度的把控
 * 这个都是一个平衡的因素
 * 那另外有一个考虑
 * 也就是我们在扩展的时候
 * 如果我们没有面向接口的编程
 * 而又非常遵循单一职责原则
 * 可能引起类的一个爆炸
 * 类的数量会比较多
 * 所以我们在总结起来
 * 就是说在实际的开发中
 * 我们的接口和方法
 * 一定要做到单一职责
 * 这个其实还是蛮好的
 * 对于我们维护起来也会比较方便
 * 而且成本也非常低
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Method {
	/**
	 * 我们现在有一个方法叫updateUserInfo
	 * 这里面可以更新用户名称
	 * 还可以更新地址
	 * 其实这个方法就是同时更新userName和地址
	 * 
	 * 
	 * @param userName
	 * @param address
	 */
    private void updateUserInfo(String userName,String address){
    	/**
    	 * 这里是一个伪代码
    	 * 大家认为是一个更新的过程就可以了
    	 * 
    	 * 
    	 */
        userName = "geely";
        address = "beijing";
    }

    /**
     * 如果我们还有一种写法
     * 如果是一个可变长度的参数
     * 还有其他的properties
     * 其他的属性
     * 那这个方法的职责就更多了
     * 不一定更新什么
     * 这里是一个可变长的参数
     * 可变长的参数肯定是放在方法最后一个位置
     * 声明才可以
     * 这里面可能包括各种信息
     * 例如说他的体重
     * 那这个方法从命名上
     * 包括里面的实现上
     * 职责就不是单一的
     * 
     * 
     * @param userName
     * @param properties
     */
    private void updateUserInfo(String userName,String... properties){
        userName = "geely";
//        address = "beijing";
    }

    /**
     * 那更好的方式应该是这样的
     * 大家可以理解他的一个变化
     * 只更新名字
     * 这个就叫做updateUsername
     * 需要更新name的话
     * 就调用这个方法
     * 这两个方法的职责是非常的单一且清晰的
     * 那我们在写方法的时候
     * 经常还会碰到这个场景
     * 
     * 
     * @param userName
     */
    private void updateUsername(String userName){
        userName = "geely";
    }
    
    /**
     * 这个就叫做updateUserAddress
     * 我们把之前的那个方法拆分成两个方法
     * 如果我们需要更新地址的话
     * 那就调用这个方法
     * 
     * 
     * @param address
     */
    private void updateUserAddress(String address){
        address = "beijing";
    }

    /**
     * 后面多了一个布尔
     * 这个方法里面传了一个布尔类型
     * 这就有意思了
     * 我们看一下
     * 
     * 
     * @param userName
     * @param address
     * @param bool
     */
    private void updateUserInfo(String userName,String address,boolean bool){
    	/**
    	 * 我们一般会这么写
    	 * 也就是布尔类型要么true要么false
    	 * 其实这个方法的职责要么todo something1
    	 * 还有todo something2
    	 * 那这种情况我们就应该把这个方法拆开
    	 * 因为这个方法很明显的就是两个职责
    	 * 如果你传进来的布尔你没有使用
    	 * 如果使用的就拆开
    	 * 有布尔的存在这个方法就不会有单一的职责
    	 * 这样开发起来简单
    	 * 维护起来也容易
    	 * 
    	 * 
    	 */
        if(bool){
            //todo something1
        }else{
            //todo something2
        }


        userName = "geely";
        address = "beijing";
    }


}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值