贯穿设计模式第一话--单一职责原则

Posted 最爱吃鱼罐头

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贯穿设计模式第一话--单一职责原则相关的知识,希望对你有一定的参考价值。

🥳🥳🥳 茫茫人海千千万万,感谢这一刻你看到了我的文章,感谢观赏,大家好呀,我是最爱吃鱼罐头,大家可以叫鱼罐头呦~🥳🥳🥳

从今天开始,将开启一个专栏,【贯穿设计模式】,设计模式是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。为了能更好的设计出优雅的代码,为了能更好的提升自己的编程水准,为了能够更好的理解诸多技术的底层源码, 设计模式就是基石,万丈高楼平地起,一砖一瓦皆根基。 ✨✨欢迎订阅本专栏✨✨

🥺 本人不才,如果文章知识点有缺漏、错误的地方 🧐,也欢迎各位人才们评论批评指正!和大家一起学习,一起进步! 👀

❤️ 愿自己还有你在未来的日子,保持学习,保持进步,保持热爱,奔赴山海! ❤️

💬 最后,希望我的这篇文章能对你的有所帮助! 🍊 点赞 👍 收藏 ⭐留言 📝 都是我最大的动力!

⛹🏼 设计模式的七大原则

🤔 为了能够更好的进入到设计模式的学习过程中,在设计程序的过程中,我们应当遵守以下原则,这也是各种设计模式的基础(即:设计模式为什么这样设计的依据)

  • 单一职责原则
  • 开闭原则
  • 依赖倒转原则
  • 里氏替换原则
  • 接口隔离原则
  • 迪米特法则
  • 合成复用原则

🏋🏼‍♀️单一职责原则

今天我们首先学习的是单一职责原则,一个类应该只有一个职责。

🤾🏼概述

  • 该原则是指对一个类来说,应当只有一个引起它变化的原因,否则类应该被拆分,即一个类应该只有一个职责,其中的职责表示引起该类变化的原因。
  • 简单理解为一个类应该只负责一项职责,这就是所谓的单一职责原则。
  • 如一个A类它负责多个职责,那么应该将A类拆分为多个类,将类的负责的职责力度细分,拆分为A1、A2、A3类等等来分别执行某一个职责。
  • 在我们编写代码的过程中,总会一些需求功能的变更,而我们也会很顺手的在每个类上加上各种各样的功能。这样意味着,无论任何需求要来,你都需要更改这个类,这样其实是很糟糕的,维护麻烦,复用不可能,也缺乏灵活性。如果一个类承担的职责过多,就等于把这些职责耦合起来,一个职责变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭到很多意想不到的破坏。所以我们在对于一个复杂的功能实现中,也可以利用这种思想,将这个复杂功能进行细粒度拆分,逐个完成拆分后的任务,这样开发和设计起来更加高效,更不容易出错。

🤹🏼‍♀️特点

单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点

  • 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多;
  • 提高类的可读性和可维护性。复杂性降低,相对可读性和可维护性都会提高;
  • 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。
  • 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则

🤸🏼‍♀️问题引出

在学生时代上学时候中,我们都知道学生最主要就是来听讲老师上课的,接下来我们就一个学生上课的例子来讲解一番,先看以下代码:

package com.ygt.principle.single;

public class SingleResponsibility 

    public static void main(String[] args) 
        // 这里创建出来学生类
        Student student = new Student();

        // 学生中都会有一个功能,认真听讲上课
        // 分别创建张三和李四去认真听讲上课
        student.attendClass("张三");
        student.attendClass("李四");
    


// 学生类
class Student 
    // 上课方法
    public void attendClass(String student) 
        System.out.println(student + " 正在认真听讲老师的内容上课!");
    

上面代码运行结果为:

张三 正在认真听讲老师的内容上课!
李四 正在认真听讲老师的内容上课!

可事实上,你会发现在学校中,总不可能所有的学生都是会认真上课的,也是有分好学生或者坏学生的吧,好学生都会认真上课,坏学生总是在上课时候打瞌睡,那以我们现在的代码,貌似完成不了这种需求,现在的attendClass上课方法中,如果也负责坏学生上课的方法的话,就会导致违反了单一职责原则。接下来就看下上面的出现的问题如何解决。

🚵🏼‍♂️解决方案

解决方案1:

这里将学生类拆分为好学生类和坏学生类。

好学生类的上课方式是认真听讲上课,而坏学生类则是上课睡觉。

package com.ygt.principle.single;

public class SingleResponsibility2 
    public static void main(String[] args) 
        // 这里创建出来两个学生类,分别好好学生和坏学生
        GoodStudent goodStudent = new GoodStudent();
        BadStudent badStudent = new BadStudent();

        // 好学生中认真听讲上课,分别创建张三和李四去认真听讲上课
        goodStudent.attendClass("张三");
        goodStudent.attendClass("李四");

        // 坏学生中就不会认真听讲上课,只会上课睡觉,创建赵六这个老六去睡觉
        badStudent.attendClass("赵六");
    


// 好学生类
class GoodStudent 
    // 上课方法
    public void attendClass(String student) 
        System.out.println(student + " 正在认真听讲老师的内容上课!");
    


// 坏学生类
class BadStudent 
    // 上课方法
    public void attendClass(String student) 
        System.out.println(student + " 正在听讲老师的催眠曲睡大觉~~~");
    

上面代码运行结果为:

张三 正在认真听讲老师的内容上课!
李四 正在认真听讲老师的内容上课!
赵六 正在听讲老师的催眠曲睡大觉~~~

可是我们会发现如果这样修改花销是很大的,除了将原来的类分解之外,还需要修改main方法中代码。并且,如果又多一种其他类型的学生的话,类又需要重新拆分,main方法中的代码又得重新改一遍,所以这样虽然遵守了单一职责原则,但其实是不可取的。接下来就看下上面的出现的问题如何解决。

解决方案2:

这里就不拆分学生类了,而是将其中的上课方法进行拆分,拆分为好学生上课的方法和坏学生上课的方法。

package com.ygt.principle.single;

public class SingleResponsibility3 

    public static void main(String[] args) 
        // 这里创建出来学生类
        Student2 student = new Student2();

        // 调用好学生方法去执行认真听讲上课
        student.goodAttendClass("张三");
        student.goodAttendClass("李四");

        // 调用坏学生方法去执行认真睡觉
        student.badAttendClass("赵六");

    


// 学生类
class Student2 
    // 好学生上课方法
    public void goodAttendClass(String student) 
        System.out.println(student + " 正在认真听讲老师的内容上课!");
    

    // 坏学生上课方法
    public void badAttendClass(String student) 
        System.out.println(student + " 正在听讲老师的催眠曲睡大觉~");
    

上面代码运行结果为:

张三 正在认真听讲老师的内容上课!
李四 正在认真听讲老师的内容上课!
赵六 正在听讲老师的催眠曲睡大觉~

可以看到,这种修改方式没有对类进行一个改动,反而是在类中新加了一个方法,这样虽然也违背了单一职责原则,但在方法级别上却是符合单一职责原则的,这也体现了只负责一项职责的功能,并且对于的代码的改动相对会比较少。

这三种方式各有优缺点,那么在实际编程中,采用哪一中呢?其实这真的比较难说,需要根据实际情况来确定。我的原则是:只有逻辑足够简单,才可以在代码级别上违反单一职责原则;只有类中方法数量足够少,才可以在方法级别上违反单一职责原则;

🌸 完结

相信各位看官看到这里大致都对设计模式中的其中一个原则有了了解吧,单一职责原则实际上的定义就是一个类应该只负责一项职责,以此控制类的粒度大小、将对象解耦、提高其内聚性。

学好设计模式,让你感受一些机械化代码之外的程序设计魅力,也可以让你理解各个框架底层的实现原理。最后,祝大家跟自己能在程序员这条越走越远呀,祝大家人均架构师,我也在努力。接下来期待第二话:开闭原则。 💪💪💪

文章的最后来个小小的思维导图:

🧐 本人不才,如有什么缺漏、错误的地方,也欢迎各位人才们评论批评指正!🤞🤞🤞

🤩 当然如果这篇文章确定对你有点小小帮助的话,也请亲切可爱的人才们给个点赞、收藏下吧,非常感谢!🤗🤗🤗

🥂 虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!✨✨✨

💟 感谢各位看到这里!愿你韶华不负,青春无悔!让我们一起加油吧! 🌼🌼🌼

💖 学到这里,今天的世界打烊了,晚安!🌙🌙🌙

设计模式软件设计七大原则 ( 单一职责原则 | 代码示例 )





一、单一职责原则简介



单一职责原则 : 不要存在 多余一个 导致 类变更的原因 ;

假设有一个类 , 负责 2 2 2 个职责 , 职责 1 1 1 和 职责 2 2 2 ;
一旦 需求发生变更 , 如 职责 1 1 1 相关功能发生改变 ;
修改该类的 职责 1 1 1 功能时 , 有可能导致原本运行正常的职责 2 2 2 发生故障 ;

对于上述类 , 应该 分别针对 职责 1 1 1 和 职责 2 2 2 , 各自建立一个独立的类 , 这样就保证了系统的稳定性 ;
这样修改 职责 1 1 1 和 职责 2 2 2 中的任何一个功能 , 都不会影响另外一个职责的功能 ;


推荐的开发方法 : 使一个 类 / 接口 / 方法 只负责一项职责 ;


单一职责优点 : 提高 类的 可读性 , 提高 系统的 可维护性 , 降低 类的复杂度 , 降低 变更引起的风险 ;

类越简单 , 可读性越好 , 同时提高了可维护性 ;
一个类只负责一个职责 , 比负责多个职责 , 类要 简单得多 ;
变更是必然的 , 必须要接收变更 , 如果 单一职责原则遵守的好 , 当修改一个功能时 , 可以 显著降低对其它功能的影响 ;


单一职责原则 不只是 面向对象 设计中特有的职责 , 只要是模块化的系统 , 都适合使用单一职责原则 ;





二、单一职责原则代码示例 ( 反面示例 )




1、不遵循单一职责原则的类


Bird 类 , 代表了所有的鸟类 , 主要的移动方式是 用翅膀飞 , 和 用脚走 ;

如果要加入新的鸟 企鹅 , 就需要修改该类的代码 , 在 if … else … 上添加新的分支 , 一旦修改 , 就会导致当前类存在潜在风险 , 影响其它的功能 ;

package singleresponsibility;

public class Bird {
    public void mainMoveMode(String name) {
        if ("鸵鸟".equals(name)) {
            System.out.println(name + " 用翅膀飞");
        } else {
            System.out.println(name + " 用脚走");
        }
    }
}

2、测试类


package singleresponsibility;

public class Main {
    public static void main(String[] args) {
        Bird bird = new Bird();
        bird.mainMoveMode("大雁");
        bird.mainMoveMode("鸵鸟");
    }
}

执行结果 :

大雁 用脚走
鸵鸟 用翅膀飞




三、单一职责原则代码示例 ( 正面示例 | 类的单一职责 )



上面反面示例中 , Bird 类承担了 2 2 2 个职责 , 用翅膀飞的鸟 和 用脚走的鸟 的职责都在一个类中实现 , 这就导致了一旦修改一个职责 , 另外一个也有可能被影响 ;


1、用翅膀飞的鸟


package singleresponsibility;

/**
 * 用翅膀飞的鸟
 *      本类的职责单一 , 只负责用翅膀飞的鸟的移动
 */
public class FlyBird {
    public void mainMoveMode(String name) {
        System.out.println(name + " 用翅膀飞");
    }
}

2、用脚走的鸟


package singleresponsibility;

/**
 * 用脚走路的鸟
 *      本类的职责单一 , 只负责用脚走的鸟的移动
 */
public class WalkBird {
    public void mainMoveMode(String name) {
        System.out.println(name + " 用脚走");
    }
}

3、测试类


package singleresponsibility;

public class Main {
    public static void main(String[] args) {

        FlyBird flyBird = new FlyBird();
        flyBird.mainMoveMode("大雁");

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

执行结果 :

大雁 用脚走
鸵鸟 用翅膀飞




四、单一职责原则代码示例 ( 接口的单一职责 )




1、反面示例


该 IGood 接口负责两大块职责 , 获取商品信息 获取 名称 / 价格 , 管理商品 购买 / 退款 ;

这个接口设计 , 违背了单一职责原则 ;

package singleresponsibility;

/**
 * 该接口负责两大块职责
 *      获取商品信息 获取 名称 / 价格
 *      管理商品 购买 / 退款
 * 这样就违背了单一职责原则
 */
public interface IGood {
    /**
     * 获取商品名称
     * @return
     */
    String getName();

    /**
     * 获取价格
     * @return
     */
    double getPrice();

    /**
     * 购买商品
     */
    void buyGood();

    /**
     * 退款
     */
    void refund();
}

2、拆分出的接口 1


package singleresponsibility;

/**
 * 信息管理
 *      该接口负责 获取商品信息 获取 名称 / 价格
 */
public interface IGoodInfo {
    /**
     * 获取商品名称
     * @return
     */
    String getName();

    /**
     * 获取价格
     * @return
     */
    double getPrice();
}

3、拆分出的接口 2


package singleresponsibility;

/**
 * 商品管理
 *      负责商品的 购买 / 退款
 */
public interface IGoodManager {
    /**
     * 购买商品
     */
    void buyGood();

    /**
     * 退款
     */
    void refund();
}

4、接口实现类


package singleresponsibility;

/**
 * 商品实现类
 *      同时实现 IGoodManager 和 IGoodInfo 两个接口
 */
public class GoodImpl implements IGoodManager, IGoodInfo{
    @Override
    public String getName() {
        return null;
    }

    @Override
    public double getPrice() {
        return 0;
    }

    @Override
    public void buyGood() {

    }

    @Override
    public void refund() {

    }
}




五、单一职责原则代码示例 ( 方法的单一职责 )



如果一个方法中 , 存在了 大块的 if … else … 判定 , 说明应该将其拆分成两个不同的方法 ;


1、反面示例


package singleresponsibility;

public class Good {
    /**
     * 更新商品的 名称 和 价格信息
     *      该方法的职责就不是单一职责的
     *      如果要针对 价格 信息修改策略进行修改
     *      会连带这 另外一个 职责 修改名称 , 也存了未知风险
     * @param name
     * @param price
     */
    public void updateGoodInfo(String name, double price) {
    }
}

2、单一职责示例


package singleresponsibility;

public class Good {
    /**
     * 更新商品的名称
     * @param name
     */
    public void updateGoodName(String name) {
    }

    /**
     * 更新商品的价格
     * @param price
     */
    public void updateGoodPrice(double price) {
    }
}

以上是关于贯穿设计模式第一话--单一职责原则的主要内容,如果未能解决你的问题,请参考以下文章

设计模式六大原则

java设计模式六大原则

关于设计模式的六大原则

设计模式软件设计七大原则 ( 单一职责原则 | 代码示例 )

单一职责原则|SOLID as a rock

设计模式六大原则:单一职责原则