关于设计原则以及设计模式的一些总结

原则归结:

1、单一职责原则

对类来说的,即一个类应该只负责一项职责

2、里式替换原则

所有引用基类的地方必须能透明地使用其子类的对象。

  • 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
  • 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。

3、接口隔离原则

一个类对另一个类的依赖 应该建立在最小的接口上

4、迪米特原则

  • 一个对象应该对其他对象保持最少的了解
  • 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的
    越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息

5、依赖倒置原则

  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象
  • 依赖倒转 ( 倒置 ) 的中心思想是面向接口编程

6、开闭原则

  • 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
  • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

7、合成复用原则

合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用功能的目的。简言之:复用时要尽量使用组合/聚合关系(关联关系),少用继承

通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,所以这种复用又称“白箱”复用,如果基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;而且继承只能在有限的环境中使用(如类没有声明为不能被继承)。

模式归结:

创建模式5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

结构模式7种:适配器模式、装饰器模式、代理模式、享元模式、外观模式、桥接模式、组合模式

关系模式11种:

  • 通过父类与子类关系实现:策略模式、模板方法模式
  • 通过两个类之间:观察者模式、迭代器模式、责任链模式、命令模式
  • 通过类的状态:备忘录模式、状态模式
  • 通过中间类:访问者模式、中介者模式、解释器模式

1、单例模式

饿汉方式:定义静态实例方式实现,事先初始化好实例,是线程安全的

懒汉方式:通过判断局部实例对象是否为空实现,用的时候进行初始化,是非线程安全的

双检锁方式:线程安全的,代码如下:

public class Singleton {
    
    private static Singleton singleton;
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        if (singleton == null) {  // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
            synchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化
                if (singleton == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
    
}

2、代理模式

代理模式是给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
优点:
代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;
可以灵活地隐藏被代理对象的部分功能和服务,也增加额外的功能和服务。

缺点:
由于使用了代理模式,因此程序的性能没有直接调用性能高;
使用代理模式提高了代码的复杂度。

举例:jdk动态代理

3、装饰器模式

装饰器模式是动态地给一个对象增加一些额外的功能,同时又不改变其结构。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模
式可以动态扩展一个实现类的功能。

代理模式和装饰器模式有什么区别?

都是结构型模式,代理模式重在访问权限的控制,而装饰器模式重在功能的加强。

装饰类内部并不会创建被装饰的对象示例,而是在外部创建好之后传给装饰类。
被代理的对象在内部进行创建,因此不能像装饰那样递归调用。

装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。在这里应用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序了。

举例:jdk动态代理

4、模板方法模式

模板方法模式是指定义一个算法骨架,将具体内容延迟到子类去实现。

优点:
提高代码复用性:将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中;
实现了反向控制:通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,
实现了反向控制并且符合开闭原则。

举例:java抽象类   abstract class

public  abstract class Cookie{  
//模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
//声明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序 
        final void cookProcess(){  
        //第一步:倒油
        this.pourOil();
        //第二步:热油
         this.HeatOil();
        //第三步:倒蔬菜
         this.pourVegetable();
        //第四步:倒调味料
         this.pourSauce();
        //第五步:翻炒
         this.fry();
    }  
//定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的
 
//第一步:倒油是一样的,所以直接实现
void pourOil(){  
        System.out.println("倒油");  
    }  
 
//第二步:热油是一样的,所以直接实现
    void  HeatOil(){  
        System.out.println("热油");  
    }  
 
//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
//所以声明为抽象方法,具体由子类实现 
    abstract void  pourVegetable();
 
//第四步:倒调味料是不一样的(一个下辣椒,一个是下蒜蓉)
//所以声明为抽象方法,具体由子类实现 
    abstract void  pourSauce();
 
 
//第五步:翻炒是一样的,所以直接实现
    void fry();{  
        System.out.println("炒啊炒啊炒到熟啊");  
    }  
}
//炒手撕包菜的类
  public class ConcreteClass_BaoCai extend  Abstract Class{
    @Override
    public void  pourVegetable(){  
        System.out.println(”下锅的蔬菜是包菜“);  
    }  
    @Override
    public void  pourSauce(){  
        System.out.println(”下锅的酱料是辣椒“);  
    }  
}
//炒蒜蓉菜心的类
  public class ConcreteClass_CaiXin extend  Abstract Class{
    @Override
    public void  pourVegetable(){  
        System.out.println(”下锅的蔬菜是菜心“);  
    }  
    @Override
    public void  pourSauce(){  
        System.out.println(”下锅的酱料是蒜蓉“);  
    }  
}
public class Template Method{
  public static void main(String[] args){
 
//炒 - 手撕包菜
    ConcreteClass_BaoCai BaoCai = new ConcreteClass_BaoCai();
    BaoCai.cookProcess();
 
//炒 - 蒜蓉菜心
  ConcreteClass_ CaiXin = new ConcreteClass_CaiXin();
    CaiXin.cookProcess();
    }
        
}
   

5、策略模式

策略模式是指定义一系列算法,将每个算法都封装起来,并且使他们之间可以相互替换。

优点:遵循了开闭原则,扩展性良好。
缺点:随着策略的增加,对外暴露越来越多

举例:interface的各种实现

策略模式和模板方法模式的区别:

策略模式是整体灵活化实现,而模板方法模式是部分灵活化实现

6、享元模式

享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。

享元模式和单例模式的区别:

单例模式是创建型模式,重在只能有一个对象。而享元模式是结构型模式,重在节约内存使
用,提升程序性能。

享元模式:把一个或者多可对象霍村起来,用的时候,直接从缓存里获取。也就是说享元模式不一
定只有一个对象。

举例:java中的final关键字定义的对象

7、责任链模式

是行为型设计模式之一,其将链中每一个节点看作是一个对象,每个节点处理的请求均不同,
且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给
每一个节点对象,直至有对象处理这个请求为止。

个人理解:类似工作流之类的模式

举例:SpringMVC中的拦截器和Mybatis中的插件机制,都是拦截器经典实现。

8、适配器模式

适配器模式是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而无
法一起工作的两个类能够在一起工作。

优点:

  • 将目标类和适配者类解耦
  • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性

缺点:

  • 过多地使用适配器,会让系统非常零乱,不易整体进行把握

9、观察者模式

观察者模式是定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关
依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模
型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模
式。

优点:

  • 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了
  • 更新接口,使得可以有各种各样不同的表示层作为具体观察者角色;
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合;
  • 观察者模式支持广播通信;
  • 观察者模式符合开闭原则(对拓展开放,对修改关闭)的要求。

缺点:

  • 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

举例:在Spring中大量的使用的观察者模式,只要看到是以Event结尾或者Publish开头的基本上都是观察者模式。

10、原型模式

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

举例:1、object的Cloneable实现 2、spring中原型bean的创建

关于浅拷贝和深拷贝的可看《一些基础知识的整理》

11、建造者模式(也称为生成器模式)

生成器模式理解为,假设我们有一个对象需要建立,这个对象是由多个组件(Component)组合而成,每个组件的建立都比较复杂,但运用组件来建立所需的对象非常简单,所以我们就可以将构建复杂组件的步骤与运用组件构建对象分离,使用builder模式可以建立。

生成器模式结构中包括四种角色:

(1)产品(Product):具体生产器要构造的复杂对象;

(2)抽象生成器(Bulider):抽象生成器是一个接口,该接口除了为创建一个Product对象的各个组件定义了若干个方法之外,还要定义返回Product对象的方法(定义构造步骤);

(3)具体生产器(ConcreteBuilder):实现Builder接口的类,具体生成器将实现Builder接口所定义的方法(生产各个组件);

(4)指挥者(Director):指挥者是一个类,该类需要含有Builder接口声明的变量。指挥者的职责是负责向用户提供具体生成器,即指挥者将请求具体生成器类来构造用户所需要的Product对象,如果所请求的具体生成器成功地构造出Product对象,指挥者就可以让该具体生产器返回所构造的Product对象。(按照步骤组装部件,并返回Product)

优点

  • 将一个对象分解为各个组件

  • 将对象组件的构造封装起来

  • 可以控制整个对象的生成过程

缺点

  • 对不同类型的对象需要实现不同的具体构造器的类,这可能回答大大增加类的数量

12、外观模式

外观模式是隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。

关于设计原则以及设计模式的一些总结_第1张图片

涉及的3个角色:

1).门面角色(Facede):客户调用,同时自身调用子系统功能

2).子系统角色:实现具体功能,对客户角色和门面角色时未知的

3).客户角色:通过调用门面角色来完成要实现的功能

优点
  - 松散耦合

  使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;

  - 简单易用

  客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。

  - 更好的划分访问层次

13、桥接模式

将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。例如手机(品牌)和手机软件的关系

关于设计原则以及设计模式的一些总结_第2张图片

桥接(Bridge)模式包含以下主要角色。

抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。提供高层控制逻辑, 依赖于完成底层实际工作的实现对象。
扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
客户端(Client):仅关心如何与抽象部分合作。但是,客户端需要将抽象对象与一个实现对象连接起来。

优点

  • 抽象与实现分离,扩展能力强
  • 符合开闭原则
  • 符合合成复用原则
  • 其实现细节对客户透明

14、组合模式

有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。

例如学校、学院、系需要增加、删除、查询操作,不用管对象是学校、学院还是系。

优点:

  1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;

  2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

15、迭代器模式

提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

举例:java中的iterator

16、命令模式

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

涉及的角色有:

  • 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
  • 具体命令角色(Concrete    Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  • 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

17、状态模式

状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。

关于设计原则以及设计模式的一些总结_第3张图片

状态模式包含以下主要角色。

  1. 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
  2. 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
  3. 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

状态模式与责任链模式的区别

状态模式和责任链模式都能消除 if-else 分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用。

从定义来看,状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象间的改变。

从代码实现上来看,两者最大的区别就是状态模式的各个状态对象知道自己要进入的下一个状态对象,而责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责。

状态模式与策略模式的区别

状态模式和策略模式的 UML 类图架构几乎完全一样,但两者的应用场景是不一样的。策略模式的多种算法行为择其一都能满足,彼此之间是独立的,用户可自行更换策略算法,而状态模式的各个状态间存在相互关系,彼此之间在一定条件下存在自动切换状态的效果,并且用户无法指定状态,只能设置初始状态。

18、备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。

优点:

  • 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
  • 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
  • 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。

缺点:

资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

19、访问者模式

将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离。

访问者模式是迭代器模式的扩充,业务规则要求遍历多个不同的对象,针对访问对象的不同,执行不同的操作。访问者还有一个用途,就是充当拦截器角色。

20、中介者模式

定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

21、解释器模式

解释器模式就是为不同的语法设置不同的解释器,让后利用解释器来计算表达式的值或解释表达式的含义,关键在于如何实现解释器。

Spring框架中都用到了哪些设计模式?

  • 简单工厂模式:Spring 中的 BeanFactory 就是简单工厂模式的体现。根据传入一个唯一的标识来获得 Bean 对象,但是在传入参数后创建还是传入参数前创建,要根据具体情况来定。
  • 工厂模式:Spring 中的 FactoryBean 就是典型的工厂方法模式,实现了 FactoryBean 接口的 bean是一类叫做 factory 的 bean。其特点是,spring 在使用 getBean() 调用获得该 bean 时,会自动调用该 bean 的 getObject() 方法,所以返回的不是 factory 这个 bean,而是这个 bean.getOjbect()方法的返回值。
  • 单例模式:在 spring 中用到的单例模式有: scope="singleton" ,注册式单例模式,bean 存放于Map 中。bean name 当做 key,bean 当做 value。
  • 原型模式:在 spring 中用到的原型模式有: scope="prototype" ,每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。
  • 迭代器模式:在 Spring 中有个 CompositeIterator 实现了 Iterator,Iterable 接口和 Iterator 接口,这两个都是迭代相关的接口。可以这么认为,实现了 Iterable 接口,则表示某个对象是可被迭代的。Iterator 接口相当于是一个迭代器,实现了 Iterator 接口,等于具体定义了这个可被迭代的对象时如何进行迭代的。
  • 代理模式:Spring 中经典的 AOP,就是使用动态代理实现的,分 JDK 和 CGlib 动态代理。
  • 适配器模式:Spring 中的 AOP 中 AdvisorAdapter 类,它有三个实现:MethodBeforAdviceAdapter、AfterReturnningAdviceAdapter、ThrowsAdviceAdapter。Spring会根据不同的 AOP 配置来使用对应的 Advice,与策略模式不同的是,一个方法可以同时拥有多个Advice。Spring 存在很多以 Adapter 结尾的,大多数都是适配器模式。
  • 观察者模式:Spring 中的 Event 和 Listener。spring 事件:ApplicationEvent,该抽象类继承了EventObject 类,JDK 建议所有的事件都应该继承自 EventObject。spring 事件监听器:ApplicationListener,该接口继承了 EventListener 接口,JDK 建议所有的事件监听器都应该继承EventListener。
  • 模板模式:Spring 中的 org.springframework.jdbc.core.JdbcTemplate 就是非常经典的模板模式的应用,里面的 execute 方法,把整个算法步骤都定义好了。
  • 责任链模式:DispatcherServlet 中的 doDispatch() 方法中获取与请求匹配的处理器HandlerExecutionChain,this.getHandler() 方法的处理使用到了责任链模式。

你可能感兴趣的:(设计模式,java,开发语言)