当前位置 博文首页 > bug_product的博客:真的很干!这篇4万字超长干货把“设计模式六

    bug_product的博客:真的很干!这篇4万字超长干货把“设计模式六

    作者:[db:作者] 时间:2021-09-07 13:20

    今天整理更新一篇关于设计模式六大原则的文章,主要是学习路径类的。
    以后会找机会再更面试类的文章。所以,点关注,不迷路,可以最先看到我的更新呦!

    壹 | 超 级 干 货

    一、单一职责原则

    1、定义

    首先,我们来看一看单一职责的定义。

    单一职责原则,全称Single Responsibility Principle, 简称SRP.
    A class should have only one reason to change
    一个类发生更改的原因应该只有一个

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

    如果一个类有一个以上的职责,这些职责就耦合在了一起。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这会导致脆弱的设计。

    当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。想要避免这种现象的发生,就要尽可能的遵守单一职责原则。

    2、为什么要遵守单一职责原则

    通常 , 我们做事情都要知道为什么要这么做,才会去做.。做的也有底气,,那么为什么我们要使用单一职责原则呢?

    (1)提高类的可维护性和可读写性

    一个类的职责少了,复杂度降低了,代码就少了,可读性也就好了,可维护性自然就高了。

    (2)提高系统的可维护性

    系统是由类组成的,每个类的可维护性高,相对来讲整个系统的可维护性就高。当然,前提是系统的架构没有问题。

    (3)降低变更的风险

    一个类的职责越多,变更的可能性就越大,变更带来的风险也就越大

    如果在一个类中可能会有多个发生变化的东西,这样的设计会带来风险, 我们尽量保证只有一个可以变化,其他变化的就放在其他类中,这样的好处就是提高内聚,降低耦合

    3、单一职责原则应用的范围

    单一职责原则适用的范围有接口、方法、类。按大家的说法,接口和方法必须保证单一职责,类就不必保证,只要符合业务就行。

    3.1 单一职责原则的应用(方法层面)

    现在有一个场景, 需要修改用户的用户名和密码. 就针对这个功能我们可以有多种实现。

    第一种:

    /**
     * 操作的类型
     */
    public enum OperateEnum {
        UPDATE_USERNAME,
        UPDATE_PASSWORD;
    }
    
    public interface UserOperate {
        void updateUserInfo(OperateEnum type, UserInfo userInfo);
    }
    
    public class UserOperateImpl implements UserOperate{
        <a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="3">@Override
        public void updateUserInfo(OperateEnum type, UserInfo userInfo) {
            if (type == OperateEnum.UPDATE_PASSWORD) {
                // 修改密码
            } else if(type == OperateEnum.UPDATE_USERNAME) {
                // 修改用户名
            }
        }
    }</a>
    

    第二种:

    public interface UserOperate {
        void updateUserName(UserInfo userInfo);
    
        void updateUserPassword(UserInfo userInfo);
    }
    
    public class UserOperateImpl implements UserOperate {
        <a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="4">@Override
        public void updateUserName(UserInfo userInfo) {
            // 修改用户名逻辑
        }
    
        </a><a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="5">@Override
        public void updateUserPassword(UserInfo userInfo) {
            // 修改密码逻辑
        }
    }</a>
    

    来看看这两种实现的区别:

    第一种实现是根据操作类型进行区分, 不同类型执行不同的逻辑. 把修改用户名和修改密码这两件事耦合在一起了. 如果客户端在操作的时候传错了类型, 那么就会发生错误。

    第二种实现是我们推荐的实现方式. 修改用户名和修改密码逻辑分开. 各自执行各自的职责, 互不干扰. 功能清晰明了。

    由此可见, 第二种设计是符合单一职责原则的. 这是在方法层面实现单一职责原则。

    3.2 单一职责原则的应用(接口层面)

    我们假设一个场景, 大家一起做家务, 张三扫地, 李四买菜. 李四买完菜回来还得做饭. 这个逻辑怎么实现呢?

    方式一:

    /**
     * 做家务
     */
    public interface HouseWork {
        // 扫地
        void sweepFloor();
    
        // 购物
        void shopping();
    }
    
    public class Zhangsan implements HouseWork{
        <a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="6">@Override
        public void sweepFloor() {
            // 扫地
        }
    
        </a><a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="7">@Override
        public void shopping() {
    
        }
    }
    
    public class Lisi implements HouseWork{
        </a><a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="8">@Override
        public void sweepFloor() {
    
        }
    
        </a><a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="9">@Override
        public void shopping() {
            // 购物
        }
    }
    </a>
    

    首先定义了一个做家务的接口, 定义两个方法扫地和买菜. 张三扫地, 就实现扫地接口. 李四买菜, 就实现买菜接口. 然后李四买完菜回来还要做饭, 于是就要在接口类中增加一个方法cooking. 张三和李四都重写这个方法, 但只有李四有具体实现。

    这样的设计本身就是不合理的。

    首先,张三只扫地, 但是他需要重写买菜方法, 李四不需要扫地, 但是李四也要重写扫地方法。

    第二,这也不符合开闭原则,增加一种类型做饭,要修改3个类。这样当逻辑很复杂的时候, 很容易引起意外错误。

    上面的这种设计不符合单一职责原则,修改一个地方,影响了其他不需要修改的地方。

    方法二:

    /**
     * 做家务
     */
    public interface Hoursework {
    }
    
    public interface Shopping extends Hoursework{
        // 购物
        void shopping();
    }
    
    public interface SweepFloor extends Hoursework{
        // 扫地
        void sweepFlooring();
    }
    
    public class Zhangsan implements SweepFloor{
    
        <a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="10">@Override
        public void sweepFlooring() {
            // 张三扫地
        }
    }
    
    public class Lisi implements Shopping{
        </a><a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="11">@Override
        public void shopping() {
            // 李四购物
        }
    }</a>
    

    上面做家务不是定义成一个接口, 而是将扫地和做家务分开了。

    张三扫地, 那么张三就实现扫地的接口; 李四购物, 李四就实现购物的接口; 后面李四要增加一个功能做饭。

    那么就新增一个做饭接口,这次只需要李四实现做饭接口就可以了。

    public interface Cooking extends Hoursework{ 
        void cooking();
    }
    
    public class Lisi implements Shopping, Cooking{
        <a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="12">@Override
        public void shopping() {
            // 李四购物
        }
    
        </a><a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="13">@Override
        public void cooking() {
            // 李四做饭
        }
    }</a>
    

    如上,我们看到张三没有实现多余的接口, 李四也没有。而且当新增功能的时候, 只影响了李四, 并没有影响张三。

    这就是符合单一职责原则。一个类只做一件事, 并且他的修改不会带来其他的变化。

    3.3 单一职责原则的应用(类层面)

    从类的层面来讲,没有办法完全按照单一职责原来来拆分。换种说法,类的职责可大可小,不想接口那样可以很明确的按照单一职责原则拆分,只要符合逻辑有道理即可。

    比如, 我们在网站首页可以注册, 登录, 微信登录,注册登录等操作.,我们通常的做法是:

    public interface UserOperate {
    
        void login(UserInfo userInfo);
    
        void register(UserInfo userInfo);
    
        void logout(UserInfo userInfo);
    }
    
    public class UserOperateImpl implements UserOperate{
        <a href="/profile/992988" data-card-uid="992988" class="" target="_blank" from-niu="default" data-card-index="14">@Override
        public void login(UserInfo userInfo) {
            // 用户登录
        }
    
        </a><a href=