终于有人将23种设计模式与七大设计原则整理明白了(一)!!!

简介: 这篇文章主要介绍23种设计模式以及七大设计原则

23种设计模式


1.设计模式概念


1.1 什么地方可以用到设计模式


面向对象(OO)=>功能模块[设计模式+算法(数据结构)]=>框架[使用多种设计模式]=>架构[服务器集群]
复制代码


1.2 使用设计模式的好处


  • 使用设计模式,软件具有很好的可扩展性(可以增加新的功能)
  • 使用开发模式,具有很好的维护性(可读性、规范性)


1.3 设计模式的目的


  • 设计模式是为了让程序,具有更好的代码重复性、可读性(编程规范性)、可扩展性(可维护性)、可靠性、是程序呈现高内聚,低耦合的特征。(模块内部逻辑关系非常紧密,模块与模块之间的关系非常的松散)
  • 分享金句:"懂了设计模式,你就懂了面向对象分析和面向对象设计(OOA/OOD)的精要"。
  • C++老手与C++新手的区别就是,前者手背上有很多的伤疤。


2.设计模式的七大原则


  • 设计模式原则,其实就是程序员在编译时,应当遵守的原则,也就是各种设计模式的基础(即:设计模式为什么这样设计的依据)

设计模式常用的七大原则:微信截图_20220609192617.png

网络异常,图片无法展示
|

  • 单一职责原则
  • 接口隔离原则
  • 依赖倒转(倒置)原则
  • 里氏替换原则
  • 开闭原则
  • 迪米特原则
  • 合成复用原则(在一些地方不写这个原则)


2.1 单一职责原则


  • 对于类来说,即一个类应该只负责一项职责。如果A类负责两个不同的职责:职责1、职责2。当职责1发生变化而改变A时,可能会对职责2造成影响使职责2运行错误,所以需要将类A的粒度分解为A1、A2。
  • 如果再类中没有满足单一职责原则,在一个类的方法中遵守单一职责原则也是可以的(交通工具)
  • 标准的单一职责原则,是在类的级别上进行拆分,而不是方法级别。
  • 通常情况下,我们要遵守单一职责原则,只有当逻辑足够简单,才可以在代码级别违反单一职责原则;只有类中的方法数量足够少,可以在方法级别保持单一职责原则。
  • 优秀的代码中使用类来区分多个分支,而不使用 if...else if()....else(耦合度高)


2.2 接口隔离原则


  • 客户端不应该依赖它不需要接口,即一个类对另一个类的依赖应该建立在最小的接口上。
  • 处理方式:将接口Interface拆分为独立的几个接口,类A与类C分别于他们需要的接口建立依赖关系。这就是使用的接口隔离原则。

没有使用接口隔离原则时的实现类图:(此时A、C要实现接口里的所有方法)

微信截图_20220609193131.png

使用接口隔离原则时的实现类图:(此时将接口进行了拆分,A此时只需要实现它要使用的方法对应的接口即可,而不用将接口中的方法全部实现)

微信截图_20220609193139.png

2.3 依赖倒转(倒置)原则


  • 在Java中,抽象是指接口或者抽象类,细节是指具体的实现类。
  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象(接口、抽象类)。
  • 抽象类不应该依赖细节,细节应该依赖抽象类。
  • 依赖倒倒转(倒置)的中心思想是面向接口编程。

依赖倒转原则是基于这样的设计理念:

  • 相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比细节为基础的架构要稳定的多。
  • 使用接口或者抽象类的目的是制定好规范,而不涉及任何具体的操作,把展示细节的任务交给他们的实现类去完成。

注意:在一个类文件中可以声明其他类、接口,只是这些都不能使用public修饰。但是声明的这些类和方法还是可以被其他的类继承或者实现的。

依赖关系传递的三种方式

  • 接口传递
  • 构造方法传递
  • setter方式传递

依赖原则要注意的地方

  • 底层模块尽量都要有抽象类和接口,或者两者都有,程序稳定性更好。
  • 变量的声明类型尽量是抽象类和接口,这样我们的变量引用个实际对象间,就曾在一个缓冲层,利于程序的扩展和优化。(就比如你和对象吵架,你先找丈母娘来劝说对象,而不是与对象直接沟通)
  • 继承时遵循里氏替换原则。


2.4 里氏替换原则


  • 使用继承的时候,父类会对子类进行约束。并且如果父类中的方法发生改变的时候,可能会对所有的子类造成影响。

里氏替换原则

  • 里氏替换原则是在1988年麻省理工学院的一个姓李的女士提出的。
  • 所有引用基类的地方必须先是透明的使用其子类的对象。
  • 在继承中,遵循里氏替换原则,在子类中尽量不在重写父类的方法。
  • 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合、组合、依赖来解决问题。

解决问题的办法

  • 原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合、组合等关系替代。


2.5 开闭原则(ocp原则)


  • 开闭原则是编程中最基础、最重要的设计原则。
  • 一个软件的实体如类、模块和函数应该对扩展开放(针对提供方),对修改关闭(对使用者)。
  • 用抽象构建架构,用实现扩展细节。
  • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现。
  • 编程中遵循其他原则,以及使用设计模式的的目的就是遵循开闭原则。


2.6 迪米特法则


  • 一个对象应该对其他对象保持最少的了解。
  • 类与类关系越密切,耦合度越大。
  • 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于依赖的类不管多么的复杂,都尽量将逻辑封装在类的内部。对外除了了提供public 方法,不对外泄露任何信息。
  • 迪米特法则还有个人更简单的定义:只与直接的朋友通信。
  • 直接的朋友: 每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式有很多,依赖、关联、组合、聚合等。其中 ,我们称出现在成员变量、方法参数、方法的返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类不要以局部变量的形式出现在类的内部。

迪米特法则

  • 核心:降低类之间的耦合
  • 注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系。


2.7 合成复用原则


  • 基本介绍:尽量使用合成/聚合的方式,而不是使用继承。(依赖、聚合、组合)

微信截图_20220609195141.png

  • 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
  • 针对接口编程,而不是针对实现编程。
  • 为了交互对象间的松耦合设计而努力


4.设计模式


4.1 设计模式概念和分类


  • 概念:设计模式的本质提高软件的维护性、通用性和扩展性,并降低软件的复杂度。
  • 《设计模式》是经典的书,作者俗称“四人组 GOF”。
  • 设计模式并不是局限于某种语言的,Java、C++、PHP都有设计模式。


4.2 设计模式的分类


  • 创建型模式:单列模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
  • 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
  • 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interperter模式)、状态模式、策略模式、职责链模式(责任链模式)。

注意:不同书籍对哦分类和名称略有差别。


5. 单例模式


  • 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

微信截图_20220609195238.png微信截图_20220609195247.png

  • 以上是使用单例模式实现的步骤。


5.1 单例(静态常量饿汉式)推荐使用


  • 之所以叫饿汉式,不论用不用这个类的对象,只要加载类的时候就会创建出来这个类的一个对象。
  • 饿汉式是线程安全的。
  • 使用饿汉式对象在类加载的时候就会被创建。
  • 使用类名.静态方法 获取到类的唯一对象。

微信截图_20220609195347.png微信截图_20220609195405.png

  • 这种单例模式可能会造成内存的浪费,因为这个对象在类加载的时候就创建出来了,如果在主方法中没有用到这个对象,就相当于白创建了这个对象,此时会造成内存的浪费。


5.2 单列(静态代码块饿汉式)推荐使用

微信截图_20220609195405.png

  • 这种写法和上边的一样,可能会造成内存浪费。


5.3 单例(线程不安全懒汉式)


  • 之所以叫懒加载是因为只有用到的时候才会加载,不用到的时候不会去加载这个对象。
  • 使用懒汉式可以解决内存浪费问题,只有调用getInstance() 方法的时候才会创建对象,并且在第二次调用方法的时候,不会在从新创建一个新的对象,而是返回第一次创建的对象,保证单例模式。

微信截图_20220609201214.png

  • 以上编写的代码,虽然起到了懒汉式的作用,但是只能在单线程之下使用
  • 如果在多线程下,一个线程刚刚进入到 if(instance == null) 判断语句块,还没来得及往下执行,也就是还没有创建出一个对象,另一个线程也通过了这个判断语句,这会便会产生多个实例对象。所以在多线程下是不安全的。
  • 在实际开发中,不要使用这种方式。


5.4 单例(懒汉式:线程安全,同步方法)


  • 将创建对象的方法使用 synchronized关键字来修饰,这样就可以保证线程安全,此时多个线程调用getInstance()方法的时候需要排队,等待上一线程结束才可以进行下一个线程的调用,因为此时上一个线程已创建出一个对象,此时就不会在创建出一个新的对象。不仅保证了线程安全,还可以实现单例模式。

微信截图_20220609201308.png

  • 以上这种方式实现的效率太低,因为每次调用方法的时候都需要排队。


5.5 单例(懒汉式:线程安全,同步代码块)


  • 此时将 synchronized 写到 if() 条件判断中,只要多个线程都进入if()判断中,一定是线程不安全的,在实际开发种不可以使用这种方式。

微信截图_20220609201325.png

微信截图_20220609201418.png

5.6 双重检查(线程安全,效率高,懒汉式模式)推荐使用


  • 在实际开发中,推荐使用这种方式。

微信截图_20220609201453.png微信截图_20220609201502.png

5.7 单例(静态内部类)推荐使用


  • 推荐使用。
  • JVM在装载类的时候是线程安全的。
  • 可以保证线程安全、实现了懒加载、保证了效率。
  • 这里保证只创建一个实例对象,使用的机制是静态内部类只会加载一次,所以只执行一次对象的创建。
  • 这里保证懒加载是因为在加载 Singleton 类的时候不会创建对象,只有调用 getInstance() 方法的时候会使用静态内部类来创建对象。

微信截图_20220609201542.png微信截图_20220609201551.png

5.8 单例(枚举)推荐使用

微信截图_20220609201638.png微信截图_20220609201649.png

  • 在JDK的RunTime的源码中使用到了单例模式。
  • 单例模式的特点之一:不是new出来的,而是使用方法调用出来的。


5.9 JDK中源码分析


  • 在 Java.lang.Runtime 中使用到了单例模式。

6.工厂模式


6.1 简单工厂模式(静态工厂模式)


  • 简单工厂模式属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类型的实例。 简单工厂模式是工厂模式家族中最简单实用的模式。
  • 简单工厂模式:定义一个创建对象的类,由这个类来 来封装实例化对象的行为(代码)
  • 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。

实现一个需求:客户可以点任意口味的披萨,奶酪披萨、胡椒等等。

实现原理:

  • 我们创建一个 SimpleFactory 工厂类,这个类负责创建出实例对象,用户给这个工厂传递需求(比如需要创建的对象),这个工厂类会通过自己类中封装的代码(行为)来创建出对应的实例对象

微信截图_20220609202344.png

  • 以下就是这个简单工厂类,将这个工厂使用聚合或者组合的方式为每一个小店铺添加。这样的话不需要改变小店铺中的代码,只需要改变工厂类中的行为即可。

微信截图_20220609202356.png

  • 简单工厂模式要比静态工厂模式灵活,简单工厂模式可以任意的改变不同对象的不同行为,而静态工厂模式中行为是一样的,每个对象使用的都是这几个行为。

微信截图_20220609202413.png

6.2 工厂方法模式

微信截图_20220609202644.png


  • 工厂方法模式设计方案: 将披萨项目的实例化功能抽象成方法,在不同的口味点餐子类中具体实现。
  • 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

实现原理

  1. 将OrderPizza类声明为抽象类。BJOrderPizza和LDOrderPizza定义为实现这个抽象类的子类,在这个子类中文成披萨的创建。

微信截图_20220609202656.png

  1. 定义不同地区不同类型的披萨。(定义四个类,按照以下格式依此类推)
    微信截图_20220609202740.png
  2. 以下就是创建的这个抽象的父类,这个类来声明创建披萨对象的方法,使用其子类具体实现这个工厂类。

微信截图_20220609203019.png

  • 让子类继承这个抽象工厂类,在这个子类中创建出关于北京不同的披萨类型。

微信截图_20220609203029.png微信截图_20220609203039.png

  • 实现顾客选定披萨


6.3 抽象工厂模式


  • 抽象工厂模式:定义了一个interface用于创建相关或者有依赖关系的对象簇,而无需指明具体的类。
  • 抽象工厂类可以将 简单工厂模式工厂方法模式进行整合。
  • 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)
  • 将工厂抽象为两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样讲单个的加单工厂类变成了工厂簇。更利于代码的维护和扩展。

微信截图_20220609203142.png

  • 以上的四个披萨类和上边的简单工厂是一样的。
  • 以下是抽象工厂类。

微信截图_20220609203151.png

  • 以下是两个工厂子类,分别实现不同地区的披萨。

微信截图_20220609203159.png微信截图_20220609203212.png


  • 以下是一个实现选择披萨的类。

微信截图_20220609203337.png微信截图_20220609203347.png

  • 以下是最终实现用户选择披萨的类。

微信截图_20220609203355.png

工厂模式小结:

  • 工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系解耦。从而提高项目的扩展性和维护性。
  • 三种工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)

工厂模式使用了设计模式的依赖抽象原则:

  • 创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。
  • 不要让类继承具体类,而是继承抽象类或者是实现interface(接口)。
  • 不要覆盖基类中已实现的方法。


6.4 JDK中源码分析


  • Java中的 Calendar类 使用到了工厂模式


相关文章
|
设计模式 关系型数据库
【设计模式——学习笔记】设计模式简介+七大设计原则介绍(下)
【设计模式——学习笔记】设计模式简介+七大设计原则介绍
139 0
|
设计模式 前端开发 算法
设计模式之设计原则
程序设计的要遵循的一些理论,也可以理解为程序设计的一种要求和目标,是面向对象程序设计的基石,也是面向对象程序设计的质量保障和依据。
90 0
|
3月前
|
设计模式 Java 测试技术
Java设计模式-UML与设计原则(1)
Java设计模式-UML与设计原则(1)
|
4月前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
|
5月前
|
设计模式 算法
交易链路设计原则&模式问题之中介者(Mediator)方法设计模式是什么,如何解决
交易链路设计原则&模式问题之中介者(Mediator)方法设计模式是什么,如何解决
|
7月前
|
设计模式 算法 Java
【设计模式系列笔记】设计模式与设计原则
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 设计原则是一些通用的设计指导方针,它们提供了如何设计一个优秀的软件系统的基本思想和规则。指导着设计者如何组织代码以实现高内聚、低耦合、易扩展和易维护的软件系统。
92 4
|
设计模式 Java 关系型数据库
Java设计模式中的设计原则 2
Java设计模式中的设计原则
106 0
|
7月前
|
设计模式 Java 数据安全/隐私保护
设计模式之六大设计原则
设计模式之六大设计原则
78 0
|
7月前
|
设计模式 关系型数据库 程序员
【设计模式】设计原则
【1月更文挑战第12天】【设计模式】设计原则
|
设计模式 Java 程序员
【设计模式——学习笔记】设计模式简介+七大设计原则介绍(上)
【设计模式——学习笔记】设计模式简介+七大设计原则介绍
59 2