资讯专栏INFORMATION COLUMN

嘻哈说:设计模式之单一职责原则

Leo_chen / 1142人阅读

摘要:定义首先呢,我们来看一下单一职责原则的定义。只负责一项职责,这就是单一职责原则。这时候就涉及到平衡的问题,平衡单一职责原则与修改造成的开销。嘻哈说接下来,请您欣赏单一职责原则的原创歌曲。

1、定义

首先呢,我们来看一下单一职责原则的定义。

就一个类而言,应该只有一个引起它变化的原因

这个说法不是很好懂,有一些抽象,不过呢,我们依旧可以尝试着理解一下。

就一个类而言,只有一个引起它变化的原因,也就是说,除此之外,不能有其它引起变化的原因。

这样就需要一个前提,这个类只能负责一项职责,而不能负责其他的职责,不然,其他的职责就会存在其他变化的原因了。

通俗的说,即一个类只负责一项职责

懒人就比较喜欢这种通俗地定义,一目了然。

懒人曾经总结过:通俗的定义,浅显易懂;理论的定义,大脑一懵

有同感的小伙伴请双击666。

2、场景

餐馆聚餐,通过服务员点餐

这是一个比较常见的场景,比如懒人撸了五天的代码,身心疲惫,周末的时候呢,就约上三五个好友,去餐馆(番茄餐厅)happy一下(非常单纯的吃饭)。我们刚刚坐下,就来了一位很漂亮的服务员为我们点餐。

这样一个服务员为我们点餐的场景,一般都是什么样的流程?

第一步:客人点餐

懒人:咱呢,不但要吃饱,还要吃好!服务员,先来一份西红柿炒鸡蛋,再来一份酸辣土豆丝!!!

好友:脸呢。。。说好的脸呢。。。

服务员:你是顾客,你是上帝,你说啥就是啥,不过,你刚才说的是啥。。。

第二步:烹饪美食

西红柿炒鸡蛋,先炒鸡蛋,再炒西红柿。。。ok,出锅。

第三步:上餐

服务员:这是您点的西红柿炒鸡蛋,请您慢用。

3、实现

不废话,撸代码。

package com.fanqiekt.principle.single;

/**
* 服务员
* @Author: 番茄课堂-懒人
*/
public class Waiter {

    /**
    * 下单
    * @param dishName 菜名
    */
    public void order(String dishName){
        System.out.println("客人点餐:" + dishName);

        System.out.println("开始烹饪:" + dishName);
        //菜品不同,做法不同。
        switch (dishName){
            case "西红柿炒鸡蛋":
                System.out.println("先炒鸡蛋");
                System.out.println("再炒西红柿");
                System.out.println("...");
                break;
            case "酸辣土豆丝":
                System.out.println("先放葱姜蒜");
                System.out.println("再放土豆丝");
                System.out.println("...");
            break;
        }
        System.out.println(dishName + "出锅");

        System.out.println(dishName + "上桌啦,请您品尝");
    }
}

服务员这个类比较简单,就一个下单的方法。

为了更好的理解,懒人进行了细节的优化(主要是很多细节懒人压根不了解)。

package com.fanqiekt.principle.single;

/**
 * 客户端
 * @Author: 番茄课堂-懒人
 */
public class Client {
    public static void main(String[] args){
        Waiter waiter = new Waiter();
        waiter.order("西红柿炒鸡蛋");
        System.out.println("-------");
        waiter.order("酸辣土豆丝");
    }
}

客户端这个类就相当于客人,客人负责通过服务员点餐。

客人一共点了两道大餐,西红柿炒鸡蛋、酸辣土豆丝,我们来运行一下,看看结果。

客人点餐:西红柿炒鸡蛋
开始烹饪:西红柿炒鸡蛋
先炒鸡蛋
再炒西红柿
...
西红柿炒鸡蛋出锅
西红柿炒鸡蛋上桌啦,请您品尝
-------
客人点餐:酸辣土豆丝
开始烹饪:酸辣土豆丝
先放葱姜蒜
再放土豆丝
...
酸辣土豆丝出锅
酸辣土豆丝上桌啦,请您品尝

OK,两个热气腾腾的饭菜就做好了。

我们回过头来看一下waiter类,大家觉得这个类好不好?

肯定是不好了,那...不好在哪里?

这就好比一个小作坊,老板既负责点餐又负责下单,就跟waiter类一样。

我们一般在小作坊吃饭,感受会怎么样?

乱,非同一般的杂乱。上菜需要等半天,点餐的时候找不到人。

还有一个弊端,我修改了做饭的流程,会影响下单的业务,增加修改的风险,为什么这么说呢?

客人A:老板,给我来一份酸辣土豆丝。

老板:好嘞,您稍等。

懒人:老板,我刚才点的西红柿鸡蛋要少放盐啊。

老板:好的,我放盐的时候用小点的勺子。

客人A:老板,我的菜做了吗?我的同伴都吃完了,没做我就不要了!

老板:您的菜已经做了,马上就要出锅了。(内心:我勒个去,刚才用小勺放盐的时候把这哥们点的单给忘了,这就尴尬了。。。)

不难看出,当功能冗杂到一个对象中,这样修改就会增加风险。那我们该如何避免呢?

一般比较完善的餐馆,还至少会有一名厨师。

厨师做饭,服务员点餐,这样做,有什么好处呢?

一来,结构清晰了,各司其职,一目了然。二来,风险降低了,我修改做饭的流程,不会影响下单的业务。

只负责一项职责,这就是单一职责原则。

那我们尝试着增加一个厨师类。

package com.fanqiekt.principle.single;

/**
 * 厨师
 *
 * @author 番茄课堂-懒人
 */
public class Chef {

    /**
     * 做饭
     * @param dishName 下单的菜名
     */
    public void cooking(String dishName) {
        System.out.println("开始烹饪:"+dishName);
        switch (dishName){
            case "西红柿炒鸡蛋":
                System.out.println("先炒鸡蛋");
                System.out.println("再炒西红柿");
                System.out.println("...");
                break;
            case "酸辣土豆丝":
                System.out.println("先放葱姜蒜");
                System.out.println("再放土豆丝");
                System.out.println("...");
                break;
        }
        System.out.println(dishName + "出锅");
    }
}

厨师类,只负责了一项职责:做饭。

这就是类的单一职责原则。

Chef类只有一个cooking方法,cooking方法是根据下单的菜品名称去烹饪不同的菜,以及炒西红柿鸡蛋以及酸辣土豆丝的具体烹饪过程。这样做合适吗?

不合适的,cooking方法应该只有菜品分发这一项职责,而炒西红柿鸡蛋以及酸辣土豆丝这两件事显然易见与分发没有任何关系,所以拆分出来效果会更好。

我们将厨师类再优化下。

package com.fanqiekt.principle.single;

/**
 * 厨师
 *
 * @author 番茄课堂-懒人
 */
public class Chef {

    /**
     * 做饭
     * 方法的单一职责原则
     * @param dishName 下单的菜名
     */
    public void cooking(String dishName) {
        System.out.println("开始烹饪:"+dishName);

        switch (dishName){
            case "西红柿炒鸡蛋":
                cookingTomato();
                break;
            case "酸辣土豆丝":
                cookingPotato();
                break;
        }

        System.out.println(dishName + "出锅");
    }

    /**
     * 炒西红柿鸡蛋
     */
    private void cookingTomato() {
        System.out.println("先炒鸡蛋");
        System.out.println("再炒西红柿");
        System.out.println("...");
    }

    /**
     * 炒酸辣土豆丝
     */
    private void cookingPotato() {
        System.out.println("先放葱姜蒜");
        System.out.println("再放土豆丝");
        System.out.println("...");
    }
}

优化后Chef类有三个方法。

cooking方法是根据下单的菜品名称去烹饪不同的菜。

cookingTomato方法是炒西红柿鸡蛋。

cookingPotato方法是炒酸辣土豆丝。

每个方法只负责一项职责,这就是方法的单一职责原则。

遵守方法单一职责原则的类,是不是更加的直观?修改各自的方法是不是也没有影响到其他的方法?

接下来,我们再优化下Waiter类,让他遵循类的单一职责原则。

package com.fanqiekt.principle.single;

/**
 * 单一职责原则的服务员
 *
 * @author 番茄课堂-懒人
 */
public class Waiter {
    private Chef chef = new Chef();

    /**
     * 点餐
     * @param dishName 餐名
     */
    public void order(String dishName) {
        System.out.println("客人点餐:"+dishName);

        chef.cooking(dishName);

        System.out.println(dishName+"上桌啦,请您品尝!");
    }
}

优化后SingleWaiter类有只负责点餐、上餐这些与服务员相关的职责,而做饭的这些无关的职责则交给了Chef。

遵守类单一职责原则的项目,是不是更加的直观?修改各自的类是不是也没有影响到其他的类?

接下来,我们把Client运行一下。

客人点餐:西红柿炒鸡蛋
开始烹饪:西红柿炒鸡蛋
先炒鸡蛋
再炒西红柿
...
西红柿炒鸡蛋出锅
西红柿炒鸡蛋上桌啦,请您品尝
-------
客人点餐:酸辣土豆丝
开始烹饪:酸辣土豆丝
先放葱姜蒜
再放土豆丝
...
酸辣土豆丝出锅
酸辣土豆丝上桌啦,请您品尝

结果与原来一致。

4、优点

撸过代码后,我们发现单一职责原则的几个优点。

提高类的可读性

符合单一职责原则的方法、类,结构会更加的清晰,类的可读性也就提高了。

降低类的复杂性
一个类只负责一项职责,一个方法也只负责一项职责。肯定要比功能冗杂到一个方法,一个类中要简单得多。

降低风险

修改其中的一个业务,不会影响到业务。

5、总结

我们必须要意识到,一味的遵守单一职责原则,不停的分拆类所付出的开销是很大的。

这时候就涉及到平衡的问题,平衡单一职责原则与修改造成的开销。

懒人的观点是如果一个方法逻辑不复杂的情况下,可以修改方法实现,否则要拆分为两个方法,遵循方法级别的单一职责原则。

如果一个类方法不多的情况下,可以只增加方法,而不用分拆为多个类,否则要拆分为多个类,遵循类级别的单一职责原则。

6、嘻哈说

接下来,请您欣赏单一职责原则的原创歌曲

嘻哈说:单一职责原则
作曲:懒人
作词:懒人
Rapper:懒人

周末约上了好友去熟悉的餐馆聚餐
只负责点餐的漂亮服务员保持笑容已经成为习惯
只负责做饭的帅气厨师一直待在了烟雾弥漫了几遍的厨房里面
每个人有自己负责的地盘
就像单一职责
一个类只有一个职责 好体面
它降低了类的复杂性
它提高了类的可读性
那风险被降低代表着单一职责没毛病

试听请点击这里

闲来无事听听曲,知识已填脑中去;

学习复习新方式,头戴耳机不小觑。

番茄课堂,学习也要酷。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77274.html

相关文章

  • 单一职责原则模式的黄金法则

    摘要:今天说一下,单一职责原则。比如,接口的地址本来已经很完美了,但是你的是处女座最讨厌处女座非要给路由添加几个以保证后台数据的安全。为了过年,我会选择使用,因为不知道处女座以后会做出什么傻事来。此时的使用动态织入后,可以完美的解决处女座。 在设计模式中,有着几条视为黄金原则,设计模式都是围绕黄金原则,对代码或者说是架构设计做出一些相应的调整,久而久之,GoF 4人组,发现其实有些设计思想可...

    GeekQiaQia 评论0 收藏0
  • 深入理解JavaScript系列6:S.O.L.I.D五大原则单一职责

    摘要:,开始我们的第一篇单一职责。通过解耦可以让每个职责工更加有弹性地变化。关于本文本文转自大叔的深入理解系列。深入理解系列文章,包括了原创,翻译,转载,整理等各类型文章,原文是大叔的一个非常不错的专题,现将其重新整理发布。 前言 Bob大叔提出并发扬了S.O.L.I.D五大原则,用来更好地进行面向对象编程,五大原则分别是: The Single Responsibility Princi...

    walterrwu 评论0 收藏0
  • 初探面向对象编程oop与设计模式

    摘要:为什么要采用面向对象编程解决问题更容易设计计算机程序就是为了解决人类的问题。面向对象编程需要对业务及代码的架构是有一定的要求的。 1. 编程方式 我们目前的编程方式大体可以有以下三种编程方式: 顺序编程 过程式编程 面向对象编程 在讲面向对象编程时先讲一下什么是顺序编程,什么是过程式编程,什么是面向对象编程: 顺序编程: 就是只用一个单线程去执行一段代码,执行过程根据代码依次从上...

    BingqiChen 评论0 收藏0
  • JS设计模式代理模式

    摘要:什么是代理模式代理模式,类似于明星的经纪人,想要拜访明星,需要先通过经纪人的沟通。不同于装饰器,那种动态加载一个对象,可以说在代理模式当中,代理是早已既定的。又称单一功能原则,面向对象五个基本原则之一。 什么是代理模式 代理模式,类似于明星的经纪人,想要拜访明星,需要先通过经纪人的沟通。而在JS当中,如果想访问一个类,需要通过另一个类来间接访问 。不同于装饰器,那种动态加载一个对象,可...

    widuu 评论0 收藏0

发表评论

0条评论

Leo_chen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<