天天看点

java设计模式·面试准备。

  写在前边:这篇内容是自己学习《java设计模式与面试精解》学习笔记,里边涵盖了自己的思考内容。不是摘录。我把这个设计模式的内容都变成自己容易理解的内容来记录,绝对不官方。这适合做初步的入门,想深入就去看有代码的那种讲解。

  先说一下为什么要学习设计模式,我们在平常的代码中,如果心中没有一个系统的设计模式了解,很可能这样,自己写的代码,自己再删掉,因为不具有扩展性,实现的太僵硬。即使现在不删,以后还是得删。设计模式没有那么高大上,总共23种设计模式,平常我们用的也就几种,设计模式可以让我们有更好的实现,根据不同的业务场景,选择合适的设计模式,会让我们更加高效的码代码。

如果我写的内容不够满足你的学习,我个人推荐一个不错的学习地方:

​​https://design-patterns.readthedocs.io/zh_CN/latest/​​

目录

​​1. 个人学习套路​​

​​2.设计模式分类​​

​​3.创造型模式​​

​​4.结构型模式​​

​​5.行为型模式(11种)​​

1. 个人学习套路

   学习设计模式就是三个要素:干了什么;有什么好处,怎么做。

   宏观的思想:使用设计模式是为了更好的实现解耦,最终目的是让代码更好的维护。

2.设计模式分类

  • 创造型模式:就是为了创建对象。
  • 结构型模式:就是如何设计代码结构更加合理。
  • 行为型模式:关注的是对象间的通信。出发点是如何让对象可以更好的交换数据,保持松耦合。

3.创造型模式

 创建型模式又分:

  • 工厂方法模式
  • 抽象工厂模式
  • 生成器模式
  • 单例模式
  • 原型模式
  1. 工厂模式
  • 做了什么:灵活的创建对象。
  • 有什么好处:工厂模式和构造方法相关,目的在与运行时会根据不同的情况创建不同的对象,
  • 怎么做:定义一个接口,对接口有多个不同的实现类,定义一个工厂类,在工厂类中根据传入不同的参数,来创建不同的对象,并返回。
  • 联想:这个模式最典型的就是spring  IOC(控制反转)的实现,spring就是通过配置文件,使用工厂模式,通过从配置文件中的不同的类全称,传入到工厂类中,再反射创建对象的。

    注: 对于了解学习的了解掉这里就行了,想要更深入的学习我贴一个工厂模式的UML图:

java设计模式·面试准备。
  • 抽象工厂(Creator)角色:该角色是工厂方法模式的核心,与应用系统无关,任何在创建对象的工厂类必须实现这个接口。
  • 具体工厂(Concrete Creator)角色:该角色实现了抽象工厂接口,含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
  • 抽象产品(Product)角色:该角色负责定义产品的共性,实现对产品最抽象的定义。
  • 具体产品(Concrete Product)角色:该角色实现抽象产品角色所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。

到这里为止,想更深度的学习,就自己再查资料把。

  2.抽象工厂模式

  • 做了什么:更加丰富的创建对象。(为什么说是更广泛的创建对象,下边做解释)
  • 有什么好处:相比工厂模式可以提供更加丰富的对象出来,这里指的是对象的种类。工厂模式可以创建出带来的是一个类的对象,抽象工厂模式目的是创建更多类的对象,屏蔽了这些具体类的创建方法,实际应用的类名不再让客户端知道,将客户端与具体类解耦。
  • 下边看UML图
java设计模式·面试准备。

3.生成器模式

  • 做了什么:操作对象。将简单的对象一步一步的生成复杂的对象,最后将复杂的对象返回。
  • 优点:隐藏了产品类构建过程中的内部细节,各个生成器之间是独立的。每个生成器都能逐步的创建对象。
  • 我是这样理解生成器模式的,就是一个复杂问题拆解的过程,比方说我们想要一个房子对象,那么房子对象是最终态,生成器模式,就是对象过程化,打地基,盖房子,装修,最后返回房子,中间的每一个过程又是独立的。
  • 联想:在API中,StringBuffer 和StringBuilder 就是生成器的实例。
  • 联想2:这和工厂模式有什么区别,生成器模式是一点一点的创建对象,有目标形态,最终返回这个目标形态的对象就结束了。工厂模式是选一个类型的对象出来。
  • 再上一个UML图
java设计模式·面试准备。

抽象建造者(Builder)角色:该角色用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑。

具体建造者(Concrete Builder)角色:该角色实现抽象建造者中定义的所有方法,并且返回一个组建好的产品实例。

产品(Product)角色:该角色是建造中的复杂对象,一个系统中会有多于一个的产品类,这些产品类并不一定有共同的接口,完全可以是不相关联的。

导演者(Director)角色:该角色负责安排已有模块的顺序,然后告诉Builder开始建造。

java设计模式·面试准备。

4.单例模式(常用,常见的设计模式)

  • 干什么:操作对象。
  • 有什么好处:保证使用的是统一的对象,避免程序中重复创建对象的问题。因为对象是要占用内存的,大量重复的对象会造成资源浪费。
  • 怎么做:私有化构造方法,静态私有化对象。单例模式有线程安全的问题,这是我们不得不考虑的问题。这也是配合锁一起问的设计模式。

手写一个单例模式

//双重同步锁保证线程安全 , 在第一次调用这个对象的时候才创建对象
public class Singleton {
  private static Singleton instance;
  private Singleton(){

  }
  public synchronized static  Singleton getInstance(){
    if(instance == null){
      synchronized (Singleton.class){
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }

//主动实例化单例模式,直接在类加载就创建对象
public class singleton{
     private static Singleton instance = new Singleton();
     private Singleton(){
    };
}      
  • UML
java设计模式·面试准备。

5.原型模式

  • 做了什么:操作了对象
  • 有什么好处:原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好,特别是在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点 。
  • 怎么做:就就是以克隆的形式复制对象。这里需要注意的一点,想要被克隆的那个对象的类应该实现了Cloneable接口,因为是浅克隆,所以我们还应该重写clone()方法。
java设计模式·面试准备。
  • UML图
java设计模式·面试准备。

4.结构型模式

 结构型模式用于指导我们完成代码的结构部分,这样的机构设计能够让代码更加清晰和易于理解,提高整体的可维护性。

  • 适配器设计模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 门面模式
  • 享元模式
  • 代理模式

1.适配器设计模式

  • 做了什么:操作类
  • 有什么好处:帮助那些因接口不兼容而无法一起工作的类,让他们能一起工作。提高类的复用度。增加了类的透明性。适配器模式可以让两个没有任何关系的类在一起运行。
  • 适配器模式的应用场景:修改一个已经投产中的系统时,需要对系统进行扩展,此时使用一个已有的类,但这个类不符合系统中的接口,这时使用适配器模式是最合适的,它可以将不符合系统接口的类进行转换,转换成符合系统接口的、可以使用的类
  • UML
java设计模式·面试准备。
  • 目标(Target)角色:该角色定义要转换成的目标接口。
  • 源(Adaptee)角色:需要被转换成目标角色的源角色。
  • 适配器(Adapter)角色:该角色是适配器模式的核心,其职责是通过继承或是类关联的方式,将源角色转换为目标角色。

2.桥接模式

  • 做了什么:将类的接口与接口实现互相解耦
  • 比如一个电视接口,两种不同品牌实现类,接口中有开操作,关操作,换台操作,但又做了一个要遥控器类,在遥控器类中添加 了对电视接口的引用,所以遥控器可以对电视进行换台操作,将遥控器类抽象,可以再扩充功能。
  • 联想:问适配器模式和桥接模式有什么区别,适配器模式是为了让两个不兼容的类一起工作,桥接模式通过两个不同的类的继承层次将抽象对象和实现类对象解耦
  • UML
java设计模式·面试准备。

抽象化(Abstraction)角色:该角色抽象化给出的定义,并保存一个对实现化对象的引用。

实现化(Implementor)角色:该角色给出实现化角色的接口,但不给出具体的实现。

修正抽象化(RefinedAbstraction)角色:该角色扩展抽象化角色,它引用实现化角色并对抽象化角色进行修正。

具体实现化(ConcreteImplementor)角色:该角色对实现化角色接口中的方法进行具体实现。

3.组合模式

  • 简单来说一组对象的集合,将对象组合成树形结构以表示“部分—整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性
  • 举出组合模式的例子
  • 比如文件系统的结构
  • UML图
java设计模式·面试准备。

抽象构件(Component)角色:该角色定义参加组合对象的共有方法和属性,规范一些默认的行为接口。

叶子构件(Leaf)角色:该角色是叶子对象,其下没有其他的分支,定义出参加组合的原始对象的行为。

树枝构件(Composite)角色:该角色代表参加组合的、其下有分支的树枝对象,它的作用是将树枝和叶子组合成一个树形结构,并定义出管理子对象的方法,如add()、remove()等。

4.装饰模式

  • 干了什么:操作了对象。我们一般有继续扩展类的功能的需求,而装饰器模式可以修改和扩展实例的行为,增加某个对象的功能,而不必扩展类。
  • 怎么做:定义一个接口,为接口创建一个实现类,在创建一个包含类型属性的抽象类,此类的构造函数将接口类型实例赋值给该属性,此类是装饰器的基类,我们能够根据需要扩展这个类并创建我们想要的具体的修饰类。
  • 我个人是这样理解的,实际上装饰器类就是一个实现目标类的接口的抽象类,然后子类再继续继承装饰器抽象类,扩展功能就是在子类中增加的。
  • 联想:装饰器模式自爱javaAPI中有体现,比如BufferReader方法
  • 问装饰器模式和继承有什么区别:使用继承我们可以实现装饰器模式最终的结果,但是装饰器模式是在运行时更改原有对象达到目的。好处在于灵活。
  • UML图
java设计模式·面试准备。

抽象构件(Component)角色:该角色用于规范需要装饰的对象(原始对象)。

具体构件(Concrete Component)角色:该角色实现抽象构件接口,定义一个需要装饰的原始类。

装饰(Decorator)角色:该角色持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口。

具体装饰(Concrete Decorator)角色:该角色负责对构件对象进行装饰。

5.门面模式

  • 干了什么:简化了复杂系统的外部接口,将用户与系统内部复杂的细节进行屏蔽,门面模式注重的是代码的解耦,强调代码抽象。
  • 实例:JDBC接口
  • 生活中的例子帮我们理解门面模式:比方说我们都知道汽车内部的构造很复杂,具体的工作细节很多,但是想要开车并不难。汽车的设计就在于给我们提供了一个钥匙的接口,插上钥匙,我们就能启动车,而不需要了解汽车内部怎么启动的。
  • 中介模式和门面模式的区别:中介模式的目的在于对交互对象间任意通信进行抽象,并要实现不属于交互对象实现的功能,在中介模式中,对象要清楚中介者的存在,并借助中介者完成通信。而在门边模式中,不需要子系统知道门面层的存在。
  • 适配器模式和门面模式的区别:适配器模式是让两个现有接口一起工作,并不需要定义新的接口,门面模式需要定义一个新的接口。
  • UML
java设计模式·面试准备。

外观(Facade)角色:客户端可以调用该角色的方法,该角色知晓相关子系统的功能和责任。正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,即该角色没有实际的业务逻辑,只是一个委托类。

子系统(subsystem)角色:可以同时有一个或多个子系统,每一个子系统都不是一个单独的类,而是一个类的集合。子系统不知道外观角色的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。

6.代理模式

  • 做了什么:将复杂对象用简单对象代理。
  • 用于当我们需要一个简单的对象代替一个复杂的对象的时候。如果创建大对象时间空间开销都很大,我们可以选择推迟大对象的创建,用简单对象代替大对象去工作,直到必须创建的时候才去创建。
  • 举个例子:使用URL地址代替大的图片对象,之前的操作都发生在URL上,直到图片必须显现出来的时候,才去创建这个大对象。
  • 问代理模式和装饰器模式有什么不同:首先目的不同,装饰器模式是为了增强对象的功能,而代理模式是一个为了避免系统大开销,用一个小的对象代替一个大对象去工作。
  • UML图
java设计模式·面试准备。

抽象主题(Subject)角色:该角色是真实主题和代理主题的共同接口,以便在任何可以使用真实主题的地方都可以使用代理主题。

代理主题(Proxy Subject)角色:也叫做委托类、代理类,该角色负责控制对真实主题的引用,负责在需要的时候创建或删除真实主题对象,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

真实主题(Real Subject)角色:该角色也叫做被委托角色、被代理角色,是业务逻辑的具体执行者。

7.享元模式

  • 干了什么:减少相似对象的创建。
  • 优点:大幅减少内存中对象的数量,降低程序内存的占用,提高性能
  • UML
java设计模式·面试准备。
  • 内部状态是存储在享元对象内部的、可以共享的信息,并且不会随环境改变而改变。
  • 外部状态是随环境改变而改变且不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。
  • 享元模式的角色: 抽象享元(Flyweight)角色:该角色对享元类进行抽象。
  • 具体享元(ConcreteFlyweight)角色:该角色实现抽象享元定义的业务。
  • 享元工厂(FlyweightFactory)角色:该角色就是构造一个池容器,负责创建和管理享元角色,并提供从池容器中获得对象的方法,保证享元对象可以被系统适当的共享。
  • 客户端(Client)角色:该角色需要自行存储所有享元对象的外部状态。

5.行为型模式(11种)

  行为型模式关注的是对象相互通信,出发点是如何让对象之间交互数据,保持松耦合。

   行为型模式又可以分为对象行为型模式和类行为模式。

  • 职责链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板模式
  • 访问者模式

1.职责链模式

  • 干了什么:发送端发送一个请求到对象链中,对象链处理这个请求,如果链中的对象不处理请求,就将请求发送给链中的下一个对象。
  • 职责链的目的是通过特定的设计,对请求的发送者和接受者解耦。
  • 联想:最典型的就是java中的异常处理机制。 servlet过滤器。
  • UML
java设计模式·面试准备。

抽象处理者(Handler)角色:该角色对请求进行抽象,并定义一个方法以设定和返回对下一个处理者的引用。

具体处理者(Concrete Handler)角色:该角色接到请求后,可以选择将请求处理掉,或者将请求传给下一个处理者。由于具体处理者持有对下一个处理者的引用,因此,如果需要,具体处理者可以访问下一个处理者。

2.命令模式

  • 干了什么:实现的是发送者和接受者之间完全解耦,发送者是调用者的对象,接收者是请求执行特定操作的对象。将一个请求封装成一个对象,从而使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
  • 优点:类间解耦。调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只须调用Command中的execute()方法即可,不需要了解是哪个接收者执行。命令模式结合其他模式会更优秀。命令模式可以结合责任链模式,实现命令族解析任务,结合模板方法模式,则可以减少Command子类的膨胀问题。
  • 问命令模式和这职责链模式之间的区别是什么:职责链模式中命令找对象,经过对象传递,命令模式会找到特定的对象。
  • UML
java设计模式·面试准备。

命令(Command)角色:该角色声明一个给所有具体命令类的抽象接口,定义需要执行的命令。

具体命令(Concrete Command)角色:该角色定义一个接收者和行为之间的弱耦合,实现命令方法,并调用接收者的相应作。

调用者(Invoker)角色:该角色负责调用命令对象执行请求。

接收者(Receive)角色:该角色负责具体实施和

3.解释器模式

  使用范围有限。不做介绍了。

4.迭代器模式

最常用的一种设计模式

  • 做了什么:允许对一组对象元素的遍历,之所以非常常用,是因为其能在不考虑存储方式情况下而直接对各类型的数据进行迭代。提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。
  • 通常访问集合对象中的元素可以使用迭代器,迭代器使我们遍历,获取和删除元素。
  • 使用迭代器访问集合中的数据内容的步骤如下:调用Iterator()获取一个位于结合起点的迭代器。调用hasNext()实现数据的循环访问,只要hasNext方法返回真,就循环下去。循环中,通过next方法获取每一个节点。
  • UML
java设计模式·面试准备。

抽象迭代器(Iterator)角色:该角色负责定义访问和遍历元素的接口。

具体迭代器(Concrete Iterator)角色:该角色实现Iterator接口,完成容器元素的遍历。

抽象聚集(Aggregate)角色:该角色提供创建迭代器角色的接口。

具体聚集(Concrete Aggregate)角色:该角色实现抽象聚集接口,创建出容纳迭代器的对象。

5.中介模式

  • 干了什么:用一个中介对象封装一系列对象(同事)的交互,中介者使各对象不需要显式的相互作用,从而使其耦合松散,而且可以独立的改变它们之间的交互。
  • 优点:减少类间的依赖,将原有的一对多的依赖变成一对一的依赖,使得对象之间的关系更易维护和理解。 避免同事对象之间的过度耦合,同事类只依赖于中介者,使同事类更易被复用,中介类和同事类可以相对独立地演化。 中介者模式将对象的行为和协作抽象化,将对象在小尺度的行为上与其他对象的相互作用分开处理。
  • UML
java设计模式·面试准备。

抽象中介者(Mediator)角色:该角色定义出同事对象到中介者对象的统一接口,用于各同事角色之间的通信。

具体中介者(Concrete Mediator)角色:该角色实现抽象中介者,它依赖于各个同事角色,并通过协调各同事角色实现协作行为。

抽象同事(Colleague)角色:该角色定义出中介者到同事对象的接口,同事对象只知道中介者而不知道其余的同事对象。

具体同事(Concrete Colleague)角色:该角色实现抽象同事类,每一个具体同事类都清楚自己在小范围内的行为,而不知道大范围内的目的。

6.备忘录模式

  • 做了什么:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
  • 备忘录模式有很多使用场景:提供一个可回滚的操作。数据库连接的事务管理使用的就是备忘录模式。需要保存和恢复数据的相关状态场景。
  • 备忘录模式注意事项:备忘录的生命周期,备忘录创建出来就要在最近的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。 备忘录的性能。不要在频繁建立备份的场景中使用备忘录模式,例如for循环中,一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的。系统的性能需要考虑。因此,如果出现这样的代码,设计师应该修改架构。
  • UML
java设计模式·面试准备。

发起人(Originator)角色:该角色记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘数据。

备忘录(Memento)角色:该角色负责存储发起人角色的内部状态,在需要时提供发起人需要的内部状态数据。

负责人(Caretaker)角色:该角色对备忘录角色进行管理、保存和提供备忘录。

7.观察者模式

  • 做了什么:观察者模式主要解决的是对象之间一对多的关系,当一个对象改变时,同时告诉多个关联对象。定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
  • 优点:观察者和被观察者之间是抽象耦合。被观察者角色所知道的只是一个具体观察者集合,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体的观察者,它只知道它们都有一个共同的接口。由于被观察者和观察者没有紧密的耦合在一起,因此它们可以属于不同的抽象化层次,且都非常容易扩展。
  • UML
java设计模式·面试准备。

抽象主题(Subject)角色:该角色又称为“被观察者”,可以增加和删除观察者对象。

抽象观察者(Observer)角色:该角色为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。

具体主题(Concrete Subject)角色:该角色又称为“具体被观察者”,它将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。

具体观察者(Concrete Observer)角色:该角色实现抽象观察者所要求的更新接口,以便使自身的状态与主题的状态相协调。

8.状态模式

  • 做了什么:根据状态的不同,而有不同的行为。
  • UML
java设计模式·面试准备。

抽象状态(State)角色:该角色用以封装环境对象的一个特定的状态所对应的行为。

具体状态(Concrete State)角色:该角色实现环境的一个状态所对应的行为。

环境(Context)角色:该角色定义客户端需要的接口,并负责具体状态的切换。它会保留一个具体状态类的实例,该实例给出环境对象的现有状态。

9.策略模式:

  • 干了啥:根据不同的情况选择不同的计算策略。把行为抽象成接口,再通过组合的形式,展现出来的就是不一样的。
  • 怎么用:比方说一个商场柜台的结账系统,根据不同的情况,商场可能会做活动,而打不同的折。这就需要不同的算法。这个时候使用策略模式就比较好。

UML

java设计模式·面试准备。

环境(Context)角色:该角色也叫上下文角色,起到承上启下的作用,屏蔽高层模块对策略、算法的直接访问,它持有一个Strategy类的引用。

抽象策略(Strategy)角色:该角色对策略、算法进行抽象,通常定义每个策略或算法必须具有的方法和属性。

具体策略(Concrete Strategy)角色:该角色实现抽象策略中的具体操作,含有具体的算法。

10.模板方法模式

  • 做了啥:一个模板方法定义算法的各个步骤,算法的一个步骤或若干个步骤由子类通过重写实现,同时保证了算法的完整性,又能实现不同的功能。
  • 怎么做:定义好算法流程,将过程中的某几个方法抽象,丢给子类去实现。
  • UML
java设计模式·面试准备。

抽象模板(Abstract Template)角色:该角色定义一个或多个抽象操作,以便让子类实现;这些抽象操作是基本操作,是一个顶级逻辑的组成步骤。还需要定义并实现一个或几个模板方法,这些模板方法一般是具体方法,即一个框架,实现对基本方法的调度,完成固定的逻辑。

具体模板(Concrete Template)角色:该角色实现抽象模板中定义的一个或多个抽象方法,每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法的不同实现,从而使得顶级逻辑的实现各不相同。

11.访问者模式

  • 做了啥:用简化对象相关操作的分组,这些操作由访问者来执行,而不是放在访问者的类中。封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
  • 优点:访问者模式使得增加新的操作变得很容易,增加新的操作只需增加新的访问者类。
  • 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个元素类中
  • 累积状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态积累在自己内部,而不是分散到很多的元素对象中,益于系统的维护。
  • UML
java设计模式·面试准备。

抽象访问者(Visitor)角色:该角色声明一个或多个访问操作,定义访问者可以访问哪些元素。

具体访问者(Concrete Visitor)角色:该角色实现抽象访问者角色中的各个访问操作。 抽象元素(Element)角色:该角色声明一个接受操作,接受一个访问者对象。

具体元素(Concrete Element)角色:该角色实现抽象元素中的接受操作。

结构对象(Object Structure)角色:该角色有以下责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素,也可以设计一个复合对象或者一个集合,如List或Set。

继续阅读