开发原则与设计模式

文章目录

  • 1 七大开发原则
    • 1.1 单一职责原则SRP(Single Responsibility Principle)
    • 1.2 开放封闭原则OCP(Open-Close Principle)
    • 1.3 里式替换原则LSP(the Liskov Substitution Principle LSP)
    • 1.4 依赖倒置原则DIP(the Dependency Inversion Principle DIP)
    • 1.5 接口分离原则ISP(the Interface Segregation Principle ISP)
    • 1.6 迪米特法则(最少知识原则)(Law Of Demeter)
    • 1.7 合成复用原则(Composite/Aggregate Reuse Principle CARP)
  • 2 设计模式
    • 2.1 创建型模式
    • 2.2 结构型模式
    • 2.3 行为模式

1 七大开发原则

1.1 单一职责原则SRP(Single Responsibility Principle)

简介:类的功能要单一,不能包罗万象,跟杂货铺似的。
说明:对类来说的,即一个类应该只负责一项职责(不是说一个类里只能有一个方法,指的是一项职责,比如这个类管订单了,就不要去管物流。),如类A负责两个不同职责: 职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为 A1,A2

单一职责原则注意事项和细节

  • (1)降低类的复杂度,一个类只负责一项职责。
  • (2)提高类的可读性,可维护性
  • (3)降低变更引起的风险
  • (4)通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一-职责原则

例子:

// 方式1
// 1.在方式1的run方法中,违反了单一职责原则
// 2. 解决的方案非常的简单,根据交通工具运行方法不同,分解成不同类即可
class Vehicle {
    public void run(String vehicle)
        System.out.println(vehicle+"在公路上运行..");
    }
}
 
// 方案2的分析
// 1.遵守单一职责原则
// 2.但是这样做的改动很大,即将类分解,同时修改客户端
// 改进:直接修改Vehicle类,改动的代码会比较少 =>方案3
class WaterVelhjicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "水中运行");
    }
}
 
class AirVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "天空运行");
    }
}
 
 
// 方式3
// 1.这种修改方法没有对原来的类做大的修改,只是增加方法
// 2. 这里虽然没有在类这个级别上遵守单一 职责原则,但是在方法级别上,仍然是遵守单一职责
class Vehicle2 {
    public void run(String vehicle) {
        //处理
 
        System.out.println(vehicle+"在公路上运行..");
    }
 
    public void runAir(String vehicle) {
        System.out.println(vehicle+"在天空上运行...");
    }
}

1.2 开放封闭原则OCP(Open-Close Principle)

简介:一个模块对于拓展是开放的,对于修改是封闭的,想要增加功能热烈欢迎,想要修改,哼,一万个不乐意。

基本概念:

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

例子: 看一个画图形的功能
开发原则与设计模式_第1张图片
问题代码:

public class Test {
    public static void main(String[] args) {}
}
 
class GraphicEditor {
    public void drawShape(Shape s){
        if (s.m_ _type== 1)
            drawRectangle(s);
        elseif(s.m_ type== 2)
            drawCircle(s);
    }
    public void drawRectangle(Shape r) {
        System.out.println("矩形");}
    public void drawCircle(Shaper) {
        System.out.println("圆形");
    }
}
 
class Shape {
    int m_ _type;
}
 
class Rectangle extends Shape {
    Rectangle(){
    super.m_ type = 1;
}}
 
class Circle extends Shape {
    Circle(){
        super.m_ type= 2;
    }
}
  1. 优点是比较好理解,简单易操作。
  2. 缺点是违反了设计模式的ocp原则,即对扩展开放(提供方),对修改关闭(使用方)。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码.
  3. 比如我们这时要新增加一一个图形种类,我们需要修改的地方较多

改进思路分析:
  思路: 把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现即可,这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可,使用方的代码就不需要修。满足了开闭原则

1.3 里式替换原则LSP(the Liskov Substitution Principle LSP)

简介:子类可以替换父类出现在父类能够出现的任何地方。比如你能代表你爸去你姥姥家干活。哈哈~~

背景——OO中的继承性的思考和说明:

  • (1)继承包含这样一层含义:
    父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
  • (2) 继承在给程序设计带来便利的同时,也带来了弊端。
    比如使用继承会给程序带来侵入性,降低程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
  • (3)问题提出:在编程中,如何正确的使用继承?
    =>里氏替换原则

里氏替换原则:

  • (1) 里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院的以为姓里的女士提出的。
  • (2) 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象
  • (3)在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
  • (4)里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题

例子:
开发原则与设计模式_第2张图片

  1. 我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法。造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候
  2. 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替.

1.4 依赖倒置原则DIP(the Dependency Inversion Principle DIP)

简介:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。就是你出国要说你是中国人,而不能说你是哪个村子的。比如说中国人是抽象的,下面有具体的xx省,xx市,xx县。你要依赖的抽象是中国人,而不是你是xx村的。
说明: 依赖倒转原则(Dependence Inversion Principle)是指:

  • (1)高层模块不应该依赖低层模块,二者都应该依赖其抽象
  • (2)抽象不应该依赖细节,细节应该依赖抽象
  • (3)依赖倒转(倒置)的中心思想是面向接口编程
  • (4)依赖倒转原则是基于这样的设计理念: 相对于细节的多变性,抽象的东西要稳定的多。在java中, 抽象指的是接口或抽象类,细节就是具体的实现类
  • (5)使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

问题代码:
开发原则与设计模式_第3张图片
这种代码的

  • 好处:简单。
  • 坏处:person不应该依赖email类,如果后期接收消息变成微信、短信,该怎么处理?
  • 解决思路:引入抽象的接口IReceiver,表示接收者。使得person与IReceiver接口发生依赖。细节要依赖抽象,而不是抽象依赖细节。

修正代码:
开发原则与设计模式_第4张图片
依赖关系传递的三种方式和应用案例:
开发原则与设计模式_第5张图片
开发原则与设计模式_第6张图片
开发原则与设计模式_第7张图片
依赖倒转原则的注意细节:

  • (1)底层模块尽量都有抽象类或接口,或者两者都有。
  • (2)变量的声明类型尽量是抽象类或接口。这样我们的变量引用和实际对象间,就存在一个缓冲层,有利于程序扩展和优化。
  • (3)继承时遵循里氏替换原则

1.5 接口分离原则ISP(the Interface Segregation Principle ISP)

简介:客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
说明:设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。就比如一个手机拥有打电话,看视频,玩游戏等功能,把这几个功能拆分成不同的接口,比在一个接口里要好的多。
例子:
下图是我们编码中遇到的常见问题:
开发原则与设计模式_第8张图片
类A通过接口Interface1依赖类B,类c通过接口Interface1依赖类D,如果接口Interface1对于类A和类c来说不是最小接口,那么类B和类D必须去实现他们不需要的方法。

//接口
interface Interface1 {
    void operation1();
    void operation2();
    void operation3();
    void operation4();
    void operation5();
}
 
class B implements Interface1 {
    void operation1() {
        System. out. println();
    }
    void operation2(){
        System. out. println();
    }
    void operation3(){
        System. out. println();
    }
    void operation4(){
        System. out. println();
    }
    void operation5() {
        System. out. println();
    }
}
 
class D implements Interface1 {
    void operation1() {
        System. out. println();
    }
    void operation2(){
        System. out. println();
    }
    void operation3(){
        System. out. println();
    }
    void operation4(){
        System. out. println();
    }
    void operation5() {
        System. out. println();
    }
}
 
class A { //A 类通过接口Interface1依赖(使用) B类,但是只会用到1,2,3方法
    public void depend1(Interface1 i) {
        i. operation1();
    }
    public void depend2(Interface1 i) {
        i. operation2();
    }
    public void depend3(Interface1 i) {
        i. operation3();
    }
}
 
class D { //D 类通过接口Interface1依赖(使用) D类,但是只会用到1,4,5方法
    public void depend1(Interface1 i) {
        i. operation1();
    }
    public void depend2(Interface1 i) {
        i. operation4();
    }
    public void depend3(Interface1 i) {
        i. operation5();
    }
}

按隔离原则应当这样处理:
将接口Interface1 拆分为独立的几个接口,类A和类c分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则
开发原则与设计模式_第9张图片

interface Interface1 {
    void operation1();
}
//接口2
interface Interface2 {
    void operation2();
    void operation3();
}
//接口3
interface Interface3 {
    void operation4();
    void operation5();
}
 
class A { 
    public void depend1(Interface1 i) {
        i.operation1();
    }
    public void depend2(Interface2 i) {
        i. operation2();
    }
    public void depend3(Interface2 i) {
        i. operation3();
    }
}
 
class C { 
    public void depend1(Interface1 i) {
        i . operation1();
    }
    public void depend4(Interface3 i) {
        i. operation4();
    }
    public void depend5(Interface3| i) {
        i. operation5();
    }
}

1.6 迪米特法则(最少知识原则)(Law Of Demeter)

简介:一个软件实体应当尽可能少的与其他实体发生相互作用
基本概念:

  • 一个对象应该对其他对象保持最少的了解
  • 类与类关系越密切,耦合度越大
  • 对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息
  • 迪米特法则还有个更简单的定义:只与直接的朋友通信

直接的朋友:

每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。

耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部

迪米特法则注意事项和细节:

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

1.7 合成复用原则(Composite/Aggregate Reuse Principle CARP)

简介:尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象
例子:
B类需要使用A类的方法,可以使用继承。但是使用继承会让B与A的耦合性增强。
开发原则与设计模式_第10张图片
改进,将A作为参数传入B类的方法中(即,依赖关系),B类依然可以使用到A类的方法。
开发原则与设计模式_第11张图片
或者使用组合关系
开发原则与设计模式_第12张图片

设计原则的核心思想

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

依赖——a依赖b,说明a使用b (dependency)

姐(接口隔离原则)和(合成复用原则)弟(迪米特法则)依(依赖倒置原则)理(里氏替换原则)开(开闭原则)战(单一职责原则)。

2 设计模式

身为程序员,我们不需要华丽的编程技巧,高深的代码,对于我们来说只需要熟练的掌握业界给予我们总结的23种设计模式,才是最重要的。因为掌握这23种设计模式,你就可以成为不一般的开发人员了。

模式: 在一定环境中解决某一问题的方案,包括三个基本元素–问题,解决方案和环境。大白话:在一定环境下,用固定套路解决问题。

设计模式(Design pattern): 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码变成真正工程化。

设计模式的分类:
创建型模式: 通常和对象的创建有关,涉及到对象实例化的方式。(共 5 种模式)
结构型模式: 描述的是如何组合类和对象以获得更大的结构。(共 7 种模式)
行为型模式: 用来对类或对象怎样交互和怎样分配职责进行描述。(共 11 种模式)

2.1 创建型模式

该模式用来处理对象的创建过程,主要包含以下五种设计模式:(单原建抽象工厂)
1, 工厂方法模式(Factory Method Pattern)的用意是定义一个创建产品对象的工厂接口, 将实际创建工作推迟到子类中。

2,抽象工厂模式(Abstract Factory Pattern)的意图是提供一个创建一系列相关或者相互依赖的接口,而无需指定它们具体的类。

3,建造者模式(Builder Pattern)的意图是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

4,原型模式(Prototype Pattern)是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

5,单例模式(Singleton Pattern)是保证一个类仅有一个实例,并提供一个访问它的全局访问点。

创建型模式有五种:工厂方法模式 、抽象工厂模式 、单例模式 、建造者模式 、原型模式
口诀:原来的建设工人单独抽奖
解释:原(原型模式)来的建(建造者模式)设工(工厂方法模式)人单(单例模式)独抽(抽象方法模式)奖。

2.2 结构型模式

6,代理模式(Proxy Pattern)就是为其他对象提供一种代理以控制对这个对象的访问。

7,装饰者模式(Decorator Pattern)动态的给一个对象添加一些额外的职责。就增加功能来说,此模式比生成子类更为灵活。

8,适配器模式(Adapter Pattern)是将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

9,桥接模式(Bridge Pattern)是将抽象部分与实际部分分离,使它们都可以独立的变化。

10,组合模式(Composite Pattern)是将对象组合成树形结构以表示“部分–整体”的层次结 构。使得用户对单个对象和组合对象的使用具有一致性。

11,外观模式(Facade Pattern)是为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

12,享元模式(Flyweight Pattern)是以共享的方式高效的支持大量的细粒度的对象。

结构型模式有七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
口诀:经过小元代表的装饰、适配和组合,大桥的外观很nice。
解释:经过小元(享元模式)代表(代理模式)的装饰(装饰器模式)、适配(适配器模式)和组合(组合模式),大桥(桥接模式)的外观(外观模式)很nice。

2.3 行为模式

13,模板方法模式(Template Method Pattern)使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

14,命令模式(Command Pattern)是将一个请求封装为一个对象,从而使你可用不同的请求对客户端进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

15,责任链模式(Chain of Responsibility Pattern),在该模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。

16,策略模式(Strategy Pattern)就是准备一组算法,并将每一个算法封装起来,使得它们可以互换。

17,中介者模式(Mediator Pattern)就是定义一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显示的相互调用,从而使其耦合性松散,而且可以独立的改变他们 之间的交互。

18,观察者模式(Observer Pattern)定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

19,备忘录模式(Memento Pattern)是在不破坏封装的前提下,捕获一个对象的内部状态, 并在该对象之外保存这个状态。

20,访问者模式(Visitor Pattern)就是表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

21,状态模式(State Pattern)就是对象的行为,依赖于它所处的状态。

22,解释器模式(Interpreter Pattern)就是描述了如何为简单的语言定义一个语法,如何在该语言中表示一个句子,以及如何解释这些句子。

23,迭代器模式(Iterator Pattern)是提供了一种方法顺序来访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

行为型模式有十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
口诀:当车模他爹责备中介时,访问者命令观察者解释一下状态
解释:当车(策略模式)模(模板方法模式)他爹(迭代模式)责(责任链模式)备(备忘录模式)中介(中介者模式)时,访问者(访问者模式)命令(命令模式)观察者(观察着模式)解释(解释器模式)一下状态(状态模式)。

你可能感兴趣的:(SpringBoot项目笔记,设计模式,开闭原则)