设计模式六大原则(一)----单一职责原则(下)

简介: 设计模式六大原则(一)----单一职责原则(下)

正文


三. 单一职责原则应用的范围


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


3.1 【方法层面】单一职责原则的应用


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

第一种:


/**
 * 操作的类型
 */
public enum OperateEnum {
    UPDATE_USERNAME,
    UPDATE_PASSWORD;
}
public interface UserOperate {
    void updateUserInfo(OperateEnum type, UserInfo userInfo);
}
public class UserOperateImpl implements UserOperate{
    @Override
    public void updateUserInfo(OperateEnum type, UserInfo userInfo) {
        if (type == OperateEnum.UPDATE_PASSWORD) {
            // 修改密码
        } else if(type == OperateEnum.UPDATE_USERNAME) {
            // 修改用户名
        }
    }
}


第二种方法:


public interface UserOperate {
    void updateUserName(UserInfo userInfo);
    void updateUserPassword(UserInfo userInfo);
}
public class UserOperateImpl implements UserOperate {
    @Override
    public void updateUserName(UserInfo userInfo) {
        // 修改用户名逻辑
    }
    @Override
    public void updateUserPassword(UserInfo userInfo) {
        // 修改密码逻辑
    }
}


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


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


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

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


3.2 【接口层面】单一职责原则的应用


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

方式一


/**
 * 做家务
 */
public interface HouseWork {
    // 扫地
    void sweepFloor();
    // 购物
    void shopping();
}
public class Zhangsan implements HouseWork{
    @Override
    public void sweepFloor() {
        // 扫地
    }
    @Override
    public void shopping() {
    }
}
public class Lisi implements HouseWork{
    @Override
    public void sweepFloor() {
    }
    @Override
    public void shopping() {
        // 购物
    }
}


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


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


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


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


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


###方法二


/**
 * 做家务
 */
public interface Hoursework {
}
public interface Shopping extends Hoursework{
    // 购物
    void shopping();
}
public interface SweepFloor extends Hoursework{
    // 扫地
    void sweepFlooring();
}
public class Zhangsan implements SweepFloor{
    @Override
    public void sweepFlooring() {
        // 张三扫地
    }
}
public class Lisi implements Shopping{
    @Override
    public void shopping() {
        // 李四购物
    }
}


上面做家务不是定义成一个接口, 而是将扫地和做家务分开了. 张三扫地, 那么张三就实现扫地的接口. 李四购物, 李四就实现购物的接口. 后面李四要增加一个功能做饭. 那么就新增一个做饭接口, 这次只需要李四实现做饭接口就可以了.


public interface Cooking extends Hoursework{ 
    void cooking();
}
public class Lisi implements Shopping, Cooking{
    @Override
    public void shopping() {
        // 李四购物
    }
    @Override
    public void cooking() {
        // 李四做饭
    }
}


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


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


3.3 【类层面】单一职责原则的应用


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


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


public interface UserOperate {
    void login(UserInfo userInfo);
    void register(UserInfo userInfo);
    void logout(UserInfo userInfo);
}
public class UserOperateImpl implements UserOperate{
    @Override
    public void login(UserInfo userInfo) {
        // 用户登录
    }
    @Override
    public void register(UserInfo userInfo) {
        // 用户注册
    }
    @Override
    public void logout(UserInfo userInfo) {
        // 用户登出
    }
}


那如果按照单一职责原则拆分, 也可以拆分为下面的形式


public interface Register {
    void register();
}
public interface Login {
    void login();
}
public interface Logout {
    void logout();
}
public class RegisterImpl implements Register{
    @Override
    public void register() {
    }
}
public class LoginImpl implements Login{
    @Override
    public void login() {
        // 用户登录
    }
}
public class LogoutImpl implements Logout{
    @Override
    public void logout() {
    }
}


像上面这样写可不可以呢? 其实也可以, 就是类很多. 如果登录、注册、注销操作代码很多, 那么可以这么写.


四、如何遵守单一职责原则


4.1 合理的职责分解


相同的职责放到一起,不同的职责分解到不同的接口和实现中去,这个是最容易也是最难运用的原则,关键还是要从业务出发,从需求出发,识别出同一种类型的职责。


例子:人的行为分析,包括了生活和工作等行为的分析,生活行为包括吃、跑、睡等行为,工作行为包括上下班,开会等行为,如下图所示:


人类的行为分成了两个接口:生活行为接口、工作行为接口,以及两个实现类。如果都用一个实现类来承担这两个接口的职责,就会导致代码臃肿,不易维护,如果以后再加上其他行为,例如学习行为接口,将会产生变更风险(这里还用到了组合模式)。


4.2 来看看简单的代码实现


第一步: 定义一个行为接口


/**
 * 人的行为
 * 人的行为包括两种: 生活行为, 工作行为
 */
public interface IBehavior {
}


这里面定义了一个空的接口, 行为接口. 具体这个行为接口下面有哪些接口呢?有生活和工作两方面的行为.


第二步: 定义生活和工作接口, 并且他们都是行为接口的子类

生活行为接口:


public interface LivingBehavior extends IBehavior{
    /** 吃饭 */
    void eat();
    /** 跑步 */
    void running();
    /** 睡觉 */
    void sleeping();
}


工作行为接口:


public interface WorkingBehavior extends IBehavior{
    /** 上班 */
    void goToWork();
    /** 下班 */
    void goOffWork();
    /** 开会 */
    void meeting();
}


第三步: 定义工作行为接口和生活行为接口的实现类


生活行为接口实现类:


public class LivingBehaviorImpl implements LivingBehavior{
    @Override
    public void eat() {
        System.out.println("吃饭");
    }
    @Override
    public void running() {
        System.out.println("跑步");
    }
    @Override
    public void sleeping() {
        System.out.println("睡觉");
    }
}


工作行为接口实现类:


public class WorkingBehaviorImpl implements WorkingBehavior{
    @Override
    public void goToWork() {
        System.out.println("上班");
    }
    @Override
    public void goOffWork() {
        System.out.println("下班");
    }
    @Override
    public void meeting() {
        System.out.println("开会");
    }
}


第四步: 行为组合调用.


行为接口定义好了. 接下来会定义一个行为集合. 不同的用户拥有的行为是不一样 , 有的用户只用生活行为, 有的用户既有生活行为又有工作行为


我们并不知道具体用户到底会有哪些行为, 所以,通常使用一个集合来接收用户的行为. 用户有哪些行为, 就往里面添加哪些行为.


行为组合接口BehaviorComposer


public interface BehaviorComposer {
    void add(IBehavior behavior);
}


行为组合接口实现类IBehaviorComposerImpl


public class IBehaviorComposerImpl implements BehaviorComposer {
    private List<IBehavior> behaviors = new ArrayList<>();
    @Override
    public void add(IBehavior behavior) {
        System.out.println("添加行为");
        behaviors.add(behavior);
    }
    public void doSomeThing() {
        behaviors.forEach(b->{
            if(b instanceof LivingBehavior) {
                LivingBehavior li = (LivingBehavior)b;
                // 处理生活行为
            } else if(b instanceof WorkingBehavior) {
                WorkingBehavior wb = (WorkingBehavior) b;
                // 处理工作行为
            }
        });
    }
}


第五步: 客户端调用


用户在调用的时候, 根据实际情况调用就可以了, 比如下面的代码: 张三是全职妈妈, 只有生活行为, 李四是职场妈妈, 既有生活行为又有工作行为.


public static void main(String[] args) {
        //  张三--全职妈妈
        LivingBehavior zslivingBehavior = new LivingBehaviorImpl();
        BehaviorComposer zsBehaviorComposer = new IBehaviorComposerImpl();
        zsBehaviorComposer.add(zslivingBehavior);
        // 李四--职场妈妈
        LivingBehavior lsLivingBehavior = new LivingBehaviorImpl();
        WorkingBehavior lsWorkingBehavior = new WorkingBehaviorImpl();
        BehaviorComposer lsBehaviorComposer = new IBehaviorComposerImpl();
        lsBehaviorComposer.add(lsLivingBehavior);
        lsBehaviorComposer.add(lsWorkingBehavior);
    }


可以看出单一职责的好处.


五、单一职责原则的优缺点


类的复杂性降低: 一个类实现什么职责都有清晰明确的定义了, 复杂性自然就降低了


可读性提高: 复杂性降低了,可读性自然就提高了


可维护性提高: 可读性提高了,代码就更容易维护了


变更引起的风险降低: 变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口和类无影响,这对系统的扩展性、维护性都有非常大的帮助

相关文章
|
1月前
|
设计模式 PHP
PHP中的设计模式:单一职责原则在软件开发中的应用
【10月更文挑战第8天】 在软件开发中,设计模式是解决常见问题的经验总结,而单一职责原则作为面向对象设计的基本原则之一,强调一个类应该只有一个引起变化的原因。本文将探讨单一职责原则在PHP中的应用,通过实际代码示例展示如何运用该原则来提高代码的可维护性和可扩展性。
33 1
|
3月前
|
设计模式
设计模式七大原则
这篇文章介绍了设计模式中的七大原则,特别强调了单一职责原则,即一个类应该只有一个引起其行为变化的原因,以确保类功能的高内聚和低耦合。
|
3月前
|
设计模式 存储 前端开发
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
|
1月前
|
设计模式 存储 测试技术
PHP中的设计模式:单一职责原则在维护性提升中的应用
【10月更文挑战第3天】 在软件开发中,设计模式是解决常见问题的高效方案。本文聚焦于PHP开发,探讨如何运用单一职责原则优化代码结构,提高系统可维护性。通过分析实际案例,本文展示了单一职责原则在降低代码复杂性、增强代码可读性和促进团队协作方面的显著效果。此外,文章还将讨论在实际项目中实施单一职责原则时可能遇到的挑战及应对策略,旨在为PHP开发者提供实用的指导和启示。
29 2
|
2月前
|
设计模式 Java 关系型数据库
设计模式——设计模式简介和七大原则
设计模式的目的和核心原则、单一职责原则、接口隔离原则、依赖倒转原则、里氏替换原则、开闭原则、迪米特法则、合成复用原则
设计模式——设计模式简介和七大原则
|
2月前
|
设计模式 数据管理 测试技术
PHP中的设计模式:单一职责原则在实战项目中的应用
在软件开发中,设计模式是解决问题的最佳实践。本文通过分析单一职责原则(SRP),探讨了如何运用这一原则来提升PHP项目的可维护性和扩展性。我们将从实际案例出发,展示单一职责原则在业务逻辑分离、代码解耦和提高测试效率方面的应用。无论是新手还是经验丰富的开发者,都能从中获益,进而编写出更健壮、更灵活的PHP代码。
39 5
|
2月前
|
设计模式 安全 PHP
PHP中的设计模式:单一职责原则在实战中的应用
在软件开发中,设计模式是解决常见问题的成熟方案。本文将通过分析单一职责原则这一设计原则,探讨如何在PHP应用程序中应用这一原则来提高代码的可维护性、扩展性和灵活性。我们将从实际案例出发,展示单一职责原则的具体应用方法,并解释其对项目开发周期和质量的积极影响。无论你是PHP初学者还是经验丰富的开发者,都能从中获益,提升你的编程实践水平。
30 4
|
2月前
|
设计模式 存储 测试技术
PHP中的设计模式:单一职责原则深度解析
在软件开发的广袤天地中,设计模式如同璀璨星辰,指引着我们穿越复杂系统的迷雾。本文聚焦于PHP环境,深入探讨“单一职责原则”(SRP),这一面向对象设计的基石。不同于常规摘要的简短概述,本文将引导您逐步揭开SRP的神秘面纱,从理论精髓到实践路径,再到其在PHP中的应用实例,为您呈现一场关于代码清晰性、可维护性和扩展性的深度之旅。
|
3月前
|
设计模式 算法 开发者
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
|
3月前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    54
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    38
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    62
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    57
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    41
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    50
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    106
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    78