-设计模式七大基本原则分析+实战+总结(详细)

Posted 栗子~~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了-设计模式七大基本原则分析+实战+总结(详细)相关的知识,希望对你有一定的参考价值。

文章目录

前言

  如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。
  而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!!


万字大章-设计模式七大基本原则分析+实战+总结(详细)

01 什么是设计模式,为什么要学设计模式,它有什么好处?

在说原则之前,我们先说一下什么是设计模式,还有为什么要学。
设计模式概念:
是对软件设计中普遍存在的各种问题,所提出的解决方案,即当遇到问题时,可以重用这些方案来解决。
为什么要学习设计模式?
当你考虑到
1、在增加需求,现有程序如何变动成本是最低的。
2、如何保证新增的功能,对原有的功能不受影响。
这些问题时,就要学习设计思维了。
设计模式的好处:
1、设计模式可以让你的程序体现出更好的重用性、可读性、可扩展性,达到高内聚、低耦合的目的。
【内聚】: 主要描述模块的内容
【高内聚】:一个模块内的元素、关联性非常强。
【强耦合】: 发货系统 直接调用 订单系统
【低耦合】:模块之间耦合性比较低,A模块出错、不会影响B模块
如 发货系统 -》 消息中间件 -》 订单系统,这样的话耦合性就比较低,因为订单系统出现问题,不会影响发货系统

2、设计模式可以让你看源码有非常大的帮助。
3、设计模式也是合理重构的指南针(不改变原来功能的基础之上,调节程序代码)。


02 七大设计原则简单总结

原则简单定义
开闭原则对扩展开放、对修改关闭(具有更好的扩展性)
单一原则一个类只负责一个功能领域的相应职责
依赖倒置原则依赖于抽象,不能依赖于具体实现( 面向接口编程)
里氏替换原则所有引用基类的地方必须能透明的使用子类的对象
接口隔离原则类之间的依赖关系应该建立在最小的接口上面
迪米特原则一个软件实体应尽可能少于其他实体发生相互作用
组合/聚合复用原则尽量使用组合/聚合,而不是通过继承达到复用的目的

03 开闭原则-分析实战

03::01 什么是开闭原则?

对扩展开放、对修改关闭(具有更好的扩展性)
扩展方式有哪些?
1、继承
2、组合

03::02 开闭原则分析

下面是一个违反开闭原则的一个实例。

/**
 * @description: TODO 电脑的接口类 开闭原则:  对扩展开发、对修改关闭(具有更好的扩展性)
 * @author yangzhenyu
 * @date 2022/2/15 17:47
 * @version 1.0
 */
 interface Computer 
    void work();
 

//戴尔
class Dell implements Computer
    @Override
    public void work() 
        System.out.println("戴尔笔记本 ***** 好评");
    

//联想
class Lenovo implements Computer
    @Override
    public void work() 
        System.out.println("联想笔记本 ***** 好评");
    

interface ComputerFactory
    //使用电脑类型
    Computer toComputer();

class Factory

    public Computer select(String code)
        Computer computer = null;
        if ("Dell".equals(code))
            return new Dell();
        else if ("Lenovo".equals(code))
            return  new Lenovo();
        
        return computer;
    


分析: 当新增新的电脑属性时,会修改已有的代码,违反开闭原则。

我们来改造一下代码:

class YangZhenYu implements  ComputerFactory
    @Override
    public Computer toComputer() 
        return new Dell();
    

class zhangSan implements ComputerFactory

    @Override
    public Computer toComputer() 
        return new Lenovo();
    

这样就可以了。
注:对于开闭原则来说,完全不修改代码是不可能的,只能尽可能的遵循开闭原则规范。


04 单一原则-分析实战

04::01 什么是单一原则?

简单定义:一个类只负责一个功能领域的相应职责。
大概意思是一个类只干一个业务,比方说:
一个类只负责下订单的业务,你不能将用户相关的业务放到这个类中。
即类的职责要单一,不能将太多的职责放在一个类中。

04::02 单一原则分析

下面是一个违反单一原则的实例,我们来看一下。
现有需求我们要改变其中一个方法的逻辑,将人类的行为改成动物的行为。

//人类
public interface Person 
    void work();

// 黄种人
class YellowPerson implements  Person
    @Override
    public void work() 
    

// 黑人
class BlackPerson implements Person

    @Override
    public void work() 

    

分析:
当YellowPerson.work()方法的需求有变动,要从人类领域改成动物领域
又因为Person 是人类领域 你将YellowPerson这个类进行修改的话,那么同样实现了Person的BlackPerson类要不要跟着修改 ,这很显然违反了单一原则的定义,所以是不行的。


05 依赖倒置原则-分析实战

05::01 什么是依赖倒置原则?

依赖于抽象,不能依赖于具体实现,即面向接口编程。
官方定义:
高模块不应该依赖于低模块,两者应依赖其抽象;
抽象不应该依赖细节,细节应该依赖抽象;
其中:
抽象就是接口或者抽象类,细节就是实现类。

通俗点就是针对抽象类层编辑,不要针对具体类进行编辑。

05::02 依赖倒置原则分析

举例需求: 现有一个客服,每天要接受微信上的消息。
代码实现:

//客服
class Worker 
    //接受消息
    public void getMessage( Wx w)
        w.setMessage();
    

//微信
class Wx
    public void setMessage()
        System.out.println("您好?");
    

public class Demo 
    public static void main(String[] args) 
        //举例需求: 现有一个客服,每天要接受微信上的消息,后续需求可能会新增钉钉消息、QQ消息
        new Worker().getMessage(new Wx());
    

这样的代码能实现,但是如果后续需求在新增钉钉消息、QQ消息会怎么样,那是不是就不太好了,

我们这么改造一下,看一下效果:

// 引入接口 指定规范-消息的规范
interface Message
    void setMessage();

//客服
class Worker1 
    //接受消息
    public void getMessage( Message w)
        w.setMessage();
    

//微信
class Wx1 implements Message
    public void setMessage()
        System.out.println("您好?");
    

//QQ
class Qq implements Message
    public void setMessage()
        System.out.println("哈喽!");
    


public class Demo1 
    public static void main(String[] args) 
        Message message = new Qq();
        new Worker1().getMessage(message);
    


这样是不是好多了,这就是接口编程。
面向接口编程定义:
在系统分析和架构中,分清层次和依赖关系,每个层次不是直接向其上层提供服务(即不是直接实例化在上层中),而是通过定义一组接口,仅向上层暴露其接口功能,上层对于下层仅仅是接口依赖,而不依赖具体类。

总结: 依赖倒置原则本质上就是通过抽象(抽象和接口) 使得各个类或者模块,实现彼此独立,互不影响,【实现模块间松耦合】,因此我们拿到需求后,要先【从顶层在细节的方式】来进行代码设计。


06 里氏替换原则-分析实战

06::01 什么是里氏替换原则?

简单定义:所有引用基类的地方必须能透明的使用子类的对象。

06::02 里氏替换原则分析

下面是一个违反里氏替换原则的一个例子:

class Arithmetic 
    public int test(int a,int b)
        return a +b;
    


class NodeArithmetic extends Arithmetic
    public int test(int a,int b)
        return a - b;
    

我们能从上面看出子类已经改变父类原本的规则了,这明显违反了里氏替换原则。
因为父类 的一个方法完成的是一个加法的操作,而子类重写父类的该方法,重写成一个减法的操作,这种操作违反了里氏替换原则。

》》因为子类覆盖父类的时候,不能修改父类原有的功能,即子类可以扩展父类的功能,但不能改变原有父类的功能。


07 接口隔离原则-分析实战

07::01 什么是接口隔离原则?

类之间的依赖关系应该建立在最小的接口上面。
关注点:最小的接口上面。
通俗点说使用多个专门的接口来取代一个统一的接口。

简单点来说,设计的时候接口里面的方法太臃肿了,而当前场景下要使用的方法没有那么多,
但实现类继承该接口时必须将所有的方法都实现,这就造成了浪费,违反了最小接口这项规定,也就是违反了接口隔离原则。

07::02 接口隔离分析

下面是一个违反接口隔离的例子,我们来看一下:

public interface A 
    void method1();
    void method2();
    void method3();
    void method4();
    void method5();

//实际场景中使用的方法
class B
    public void method1(A  a)
        a.method1();
    
    public void method2(A  a)
        a.method2();
    
    public void method3(A  a)
        a.method3();
    

class C implements A
    @Override
    public void method1() 
        System.out.println("method1");
    
    @Override
    public void method2() 
        System.out.println("method2");
    
    @Override
    public void method3() 
        System.out.println("method3");
    
    @Override
    public void method4()     
    @Override
    public void method5()     

我们观察后,可以得出实现A接口方法的实现类,必须要将1、2、3、4、5个方法都实现
//而实际场景中根本就没有用到4、5 方法,所以这个接口A设计的时候方法就有臃肿,就不是最小接口,因此违反原则。

我们来改造一下,即将A接口拆分成两个接口,根据实际需求,选择对应的那个接口进行传参,
这样实现类就可以避免实现多余的方法,符合接口隔离原则
:

interface A1
    void method1();
    void method2();
    void method3();

interface A2
    void method3();
    void method4();

//使用的时候,直接使用 A1接口即可,如下:
class D 
    public void method1(A1  a)
        a.method1();
    
    public void method2(A1  a)
        a.method2();
    
    public void method3(A1  a)
        a.method3();
    


//这样实现类,继承A1即可,不会造成多余的浪费
class E implements A1
    @Override
    public void method1() 
        System.out.println("method1");
    
    @Override
    public void method2() 
        System.out.println("method2");
    
    @Override
    public void method3() 
        System.out.println("method3");
    

这样就不会有上述的问题了。


08 迪米特原则-分析实战

08::01 什么是迪米特原则?

简单定义:一个软件实体应尽可能少于其他实体发生相互作用,【核心目的就是降低耦合性】。
官方定义:

  1. 一个对象应该对其他对象有最少的了解,所以迪米特原则又叫最少知识原则;
    也就是一个类对于自己依赖的类知道的越少越好。
  2. 只与直接的朋友进行通信;

也就是只要两个类有依赖关系,那么他们就是朋友关系,而直接的朋友就是:

  1. 一个类是以【成员变量】的身份出现在这个类上;
  2. 一个类是以【方法的参数类型】的身份出现在这个类上;
  3. 一个类是以【方法的返回值类型】的身份出现在这个类上;

即一个软件实体对其他实体的引用越少越好,或者说如果两个类不必彼此直接通信,
那么这两个类就不应该直接发生相互作用,而是通过引入一个第三者发生间接交互。

那什么是迪米特原则?
迪米特原则玩的就是类的关系,分析的就是类的依赖关系,凡是类中用到了对方,就说明两个类就具有依赖关系

迪米特原则注意事项(重点):

  1. 迪米特原则的核心目的就是降低耦合性;
  2. 从被依赖者的角度说,尽量将逻辑封装在类的内部,避免对外泄漏,对外除了提供public方法,不泄露任何信息;
  3. 从依赖者的角度说,只依赖应该依赖的对象;
  4. 切忌为了用而用

08::02 迪米特原则分析

下面是一个违反迪米特原则的实例:
需求:有一个学校,下面有各个学院和总部,现要求打印出总部的员工id和学院员工的id?

/**
* @description: TODO 学院员工 基类
* @author yangzhenyu
* @date 2022/2/18 18:50
* @version 1.0
*/
public class CollegeEmployee 
    private String id;
    public CollegeEmployee(String id) 
        this.id = id;
    
    public String getId() 
        return id;
    
    public void setId(String id) 
        this.id = id;
    

/**
* @description: TODO 学院员工 管理类
* @author yangzhenyu
* @date 2022/2/18 18:52
* @version 1.0
*/
public class CollegeManager 
    public List<CollegeEmployee> getList()
        ArrayList<CollegeEmployee> collegeEmployees = new ArrayList<>();
        for (int i =0;i<3;i++)
            collegeEmployees.add(new CollegeEmployee("学院员工 id:"+i));
        
        return collegeEmployees;
    

/**
* @description: TODO 学校总部员工的基类
* @author yangzhenyu
* @date 2022/2/18 18:48
* @version 1.0
*/
public class SchoolEmployee 
    private String id;

    public SchoolEmployee(String id) 
        this.id = id;
    

    public String getId() 
        return id;
    

    public void setId(String id) 
        this.id = id;
    

/**
* @description: TODO 学校总部 管理类 (违反迪米特原则实例)
* @author yangzhenyu
* @date 2022/2/18 18:51
* @version 1.0
*/
public class SchoolManager 
    public List<SchoolEmployee> getList()
        ArrayList<SchoolEmployee> schoolEmployee = new ArrayList<>();
        for (int i =0;i<3;i++)
            schoolEmployee.add(new SchoolEmployee("学校总部员工 id:"+i));
        
        return schoolEmployee;
    
    public void print(CollegeManager manager)
        //打印总部员工
        this.getList().forEach(v->
            System.out.println(v.getId());
        );
        System.out.println("===================================");
        // 打印学院员工
        List<CollegeEmployee> collegeEmployees = manager.getList();
        collegeEmployees.forEach(v->
            System.out.println(v.getId());
        );
    

public class Demo 
    public static void main(String[] args) 
        new SchoolManager().print(new CollegeManager());
    

功能实现是没有问题的,但是对于迪米特原则来说SchoolManager 这个类中用了非直接朋友关系的对象,下面的是分析的代码:

public class SchoolManager 
    public List<SchoolEmployee> getList()
        //SchoolEmployee  作为SchoolManager的方法的返回值类型,所以它们是直接的朋友关系
        ArrayList<SchoolEmployee> schoolEmployee = new ArrayList<>();
        for (int i =0;i<3;i++)
            schoolEmployee.add(new SchoolEmployee("学校总部员工 id:"+i));
        
        return schoolEmployee;
    

    //CollegeManager 作为SchoolManager的方法参数类型 ,所以它们是直接的朋友关系
    public void print(CollegeManager manager)
        //打印总部员工
        this.getList().forEach(v->
            System.out.println(v.getId());
        );
        System.out.println("===================================");
        // 打印学院员工
        //CollegeEmployee 作为SchoolManager的方法内部出现 ,所以它们不是直接的朋友关系,
        // 不符合迪米特原则的第二个规范,只与直接的朋友进行通信,避免非直接朋友的耦合关系
        List<CollegeEmployee> collegeEmployees = manager.getList();
        collegeEmployees.forEach(v->
            System.out.println(v.getId());
        );
    

问题就出现在CollegeEmployee 作为SchoolManager的方法

以上是关于-设计模式七大基本原则分析+实战+总结(详细)的主要内容,如果未能解决你的问题,请参考以下文章

七大面向对象设计原则

超详细总结基于比较的七大经典 排序 -- 不会的童鞋快进来补习

设计模式七大原则-总结

一句话总结软件设计七大原则

Java 中 23 种设计模式详解:七大结构型模式详细分析

GOF 的23种JAVA常用设计模式总结 03 面向对象七大设计原则