备战面试之设计模式
1、列举出几个你已知的设计模式
策略模式、观察者模式、装饰者模式、工厂模式、单例模式、命令模式、模板方法、迭代器模式、代理模式、状态模式、责任链模式。
2、设计模式的原则有哪些
开放封闭原则、里氏替换原则、依赖倒置原则、单一职责原则、迪米特法则(最小知道原则)、接口隔离原则。
3、有哪些设计模式是你比较熟悉的(设计模式的应用场景)
观察者模式
如果需要在一对多的关系下进行消息的实时转发,就可以使用观察者模式,当一个对象的状态发生改变时,它的所有依赖者都会收到通知并自动更新。使用观察者模式可以让消息更新更加实时。 观察者模式的应用十分广泛,在一对多关系中可以使用观察者模式,除此之外凡是需要监听状态变化的情况都可以使用观察者模式,比如spring框架的事件监听机制。消息队列中消费者实时接受消息等。
工厂模式
使用工厂模式的目的主要是为了封装对象的创建这一经常发生变化的过程,要实现这一点就需要利用面向对象编程的封装以及多态特性,将实例化具体类的代码从程序中抽离出来封装进工厂中,同时利用多态从工厂中获取对象,这样以来高层组件不在负责实例化底层组件,因此也就不在依赖于底层组件,二者都依赖于接口,满足了依赖倒置原则(要依赖抽象,不要依赖具体类)。工厂模式包含简单工厂模式、工厂方法以及抽象工厂模式三种。
- 简单工厂:将对象实例化过程封装进一个对象中,由这个对象负责实例化。
- 工厂方法:工厂方法定义了一个创建对象的抽象方法,由子类决定要实例化的类是哪一个。工厂方法把实例化推迟到子类。
- 抽象工厂:抽象工厂模式提供一个接口,使用户使用抽象的接口来获取一组相关的产品,而不需要关心实际产出的具体产品是什么。
在Spring框架的的IOC容器中,使用bean工厂创建bean,使用者通过调用getBean直接获取组件。
在Spring框架的aop中,代理的创建是通过代理工厂完成的(ProxyFactory),使用者通过调用getProxy来获取代理。
单例模式
如果希望某个类只存在一个实例,并且避免其他的类再自行产生实例,就可以使用单例模式,单例模式能够确保一个类只有一个实例并且提供一个全局访问点。一般来说要实现单例模式,可以将单例类的构造方法设为私有的,同时提供一个静态的访问方法来获取单例对象,有以下几种实现方式。
- 懒加载式:这种方式只在程序第一次调用访问方法的时候创建对象,之后再次调用返回的都是同一对象,但是这种方式在多线程环境中会出现问题,可能造成两个线程获取到的不是同一对象。以下几种实现方式可以避免这个问题。
- 提前加载:在声明的时候就实例化对象,但是这样可能造成程序启动缓慢。
- 访问方法加同步锁:给访问方法加上同步锁也可以避免多线程环境中出现的这个问题,但是会对性能产生较大影响。
- 双重检查加锁:首先检查实例是否存在,不存在则进入同步区块,进入之后再次检验,如果实例仍未null才创建实例。同时要在声明对象的时候添加volatile关键字。
Spring框架中所有的单例实例被存储在缓存中,这个缓存设计为单例的。
JavaEE规范中的servletContext被设计成单例的。
Spring框架中的ApplicationContext被设计成单例的。
Druid数据库连接池默认为单例模式。
命令模式
我的理解是命令模式将程序对其他对象的操作这一过程封装成对象,这样以来程序通过发出命令来操作其他对象而不是直接委托给命令接收者,这样一来就实现了发出请求的对象和执行请求的对象之间的解耦。一般来说命令模式支持撤销操作。
命令模式的一个常见的用法是将操作其他对象这一过程封装成任务并添加到任务队列中,接收者依次执行,我认为Runnable接口就是使用了命令模式,我们把一系列操作封装进run方法里面并且把这个任务添加到线程池的任务队列中,线程池调度线程依次执行。
除此之外在spring框架的RabbitMQ客户端中也用到了命令模式,RabbitMQ分为客户端和服务器,二者之间通过TCP连接互传数据,其中通过TCP连接发送数据这个过程被封装成了命令,客户端只需要下发命令,不需要关心数据是如何被发送出去的。
迭代器模式
针对数据不同的特性Jdk定义出了不同的数据集合来供我们使用,这些数据结构的底层实现方式不同,例如ArrayList内部使用数组实现的,LinkedList内部使用链表实现的,HashMap底层内部使用链表数组的方式实现。而在使用这些集合的过程中遍历集合是一个十分常用的需求,为了让使用者在遍历集合的时候不必知晓其底层实现,定义了Iterator接口,这一接口实际上是对遍历的封装,所有实现了Iterator接口的集合都可以使用统一的遍历方式,这使迭代器模式在Jdk中的应用。
代理模式
Spring框架中面向切面编程的实现就是使用了代理模式,其中一种实现方式使利用Jdk的动态代理机制来操作对象,这样就可以在调用相应的方法之前也就是所谓的切点之前进行一些增强,以此完成横向切面的功能。