2023 设计模式精选面试题30道

x33g5p2x  于2023-01-26 发布在 其他  
字(9.9k)|赞(0)|评价(0)|浏览(2162)

1、 讲一下模板模式?

模板模式属于行为型模式,使子类可以在不改变算法结构的情况下重新定义算法的某些步骤,适用于抽取子类重复代码到公共父类。
优点是可以封装固定不变的部分,扩展可变的部分。缺点是每一个不同实现都需要一个子类维护,会增加类的数量。
为防止恶意操作,一般模板方法都以final修饰。
HttpServlet定义了一套处理HTTP请求的模板, service方法为模板方法,定义了处理HTTP请求的基本流程, doxxx等方法为基本方法,根据请求方法的类型做相应的处理,子类可重写这些方法。

2、 说说工厂模式

简单工厂模式又叫静态工厂方法模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。比如,一台咖啡机就可以理解为一个工厂模式,你只需要按下想喝的咖啡品类的按钮(摩卡或拿铁),它就会给你生产一杯相应的咖啡,你不需要管它内部的具体实现,只要告诉它你的需求即可。
优点:
工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象;

客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量;

通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点:
不易拓展,一旦添加新的产品类型,就不得不修改工厂的创建逻辑;

产品类型较多时,工厂的创建逻辑可能过于复杂,一旦出错可能造成所有产品的创建失败,不利于系统的维护。

3、 适配器模式和和装饰器模式以及代理模式的区别?

适配器模式没有层级关系,适配器和被适配者没有必然连续,满足has-a的关系,解决不兼容的问题,是一种后置考虑。
装饰器模式具有层级关系,装饰器与被装饰者实现同一个接口,满足isa的关系,注重覆盖和扩展,是一种前置考虑。
适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。

4、 讲一讲装饰器模式?

装饰器模式属于结构型模式,在不改变原有对象的基础上将功能附加到对象,相比继承可以更加灵活地拓展原有对象的功能。

装饰器模式适合的场景:在不想增加很多子类的前提下拓展一个类的功能。

java.io包中,InputStream字节输入流通过装饰器BufferInputStream增强为缓冲字节输入流。

5、 说一下简单工厂模式?

简单工厂模式指由一个工厂对象来创建实例,客户端不需要关注创建逻辑,只需要提供传入工厂的参数。

适用于工厂类负责创建对象较少的情况,缺点是如果要增加新产品,就需要修改工厂类的判断逻辑,违背开闭原则,且产品多的话会使工厂类比较复杂。

Calandeer抽象类的日历对象。方法,调用方法根据不同的地区参数创建不同的对象。Spring中的BeanFactory使用简单工厂模式,根据传入一个唯一的标识来获得Bean对象。

6、 讲一讲代理模式?

** 代理模式属于结构型模式,为其他对象提供一种代理以控制对这个对象的访问**。优点是可以增强目标对象的功能,降低代码耦合度,扩展性好。缺点是在客户端和目标对象之间增加代理对象会导致请求处理速度变慢,增加系统复杂度。
        Spring利用动态代理实现AOP,如果Bean实现了接口就使用JDK代理,否则使用CGLib代理。
        **静态代理:代理对象持有被代理对象的引用,调用代理对象方法时也会调用被代理对象的方法,但是会在被代理对象方法的前后增加其他逻辑。需要手动完成,在程序运行前就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就已经确定了。缺点是一个代理类只能为一个目标服务,如果要服务多种类型会增加工作量。
        动态代理:动态代理在程序运行时通过反射创建具体的代理类代理类和被代理类的关系在运行前是不确定的。动态代理的适用性更强,主要分为JDK动态代理和CGLib动态代理。
        JDK动态代理:通过类的方法获取一个动态代理对象,需要传入三个参数,被代理对象的类加载器、被代理对象实现的接口,以及一个器来指明具体的逻辑,相比静态代理的优势是接口中声明的所有方法都被转移到调用处理的方法集中处理。
       ** CGLib动态代理
: JDK动态代理要求实现被代理对象的接口,而CGLib要求继承被代理对象,如果一个类是inal类则不能使用CGLib代理。两种代理都在运行期生成字节码, JDK动态代理直接写字节码,而CGLib动态代理使用ASM框架写字节码, ASM的目的是生成、转换和分析以字节数组表示的已编译Java类。
        JDK动态代理调用代理方法通过反射机制实现,而GCLib动态代理通过FastClass机制直接调用方法,它为代理类和被代理类各生成一个类,该类为代理类和被代理类的方法分配一个int参数,调用方法时可以直接定位,因此调用效率更高。

7、 说几种你熟悉的设计模式

设计模式一般被分为三大类二十三种:

创建型模式:
  1. 工厂方法模式(Factory Method)
  2. 抽象工厂模式(Abstract Factory)
  3. 单例模式(Singleton)
  4. 建造者模式(Builder)
  5. 原型模式(Prototype)
结构型模式:
  1. 适配器模式(Adapter)
  2. 装饰器模式(Decorator)
  3. 代理模式(Proxy)
  4. 外观模式(Facade)
  5. 桥接模式(Bridge)
  6. 组合模式(Composite)
  7. 享元模式(Flyweight)
行为型模式:
  1. 策略模式(Strategy)
  2. 模板方法模式(Template Method)
  3. 观察者模式(Observer)
  4. 迭代器模式(Iterator)
  5. 责任链模式(Chain of Responsibility)
  6. 命令模式(Command)
  7. 备忘录模式(Memento)
  8. 状态模式(State)
  9. 访问者模式(Visitor)
  10. 中介者模式(Mediator)
  11. 解释器模式(Interpreter)

8、 设计模式有那些原则?

开闭原则:OOP中最基础的原则,指一个软件实体(类、模块、方法等)应该对扩展开放,对修改关闭。强调用抽象构建框架,用实现扩展细节,提高代码的可复用性和可维护性。

单一职责原则:一个类、接口或方法只负责一个职责,降低代码复杂度以及变更引起的风险。

依赖倒置原则:程序应该依赖于抽象类或接口,而不是具体的实现类。

接口隔离原则:将不同功能定义在不同接口中实现接口隔离,避免了类依赖它不需要的接口,减少了接口之间依赖的冗余性和复杂性。

里氏替换原则:开闭原则的补充,规定了任何父类可以出现的地方子类都一定可以出现,可以约束继承泛滥,加强程序健壮性。

迪米特原则:也叫最少知道原则,每个模块对其他模块都要尽可能少地了解和依赖,降低代码耦合度。

合成/聚合原则:尽量使用组合(has-a)/聚合(contains-a)而不是继承(is-a)达到软件复用的目的,避免滥用继承带来的方法污染和方法爆炸,方法污染指父类的行为通过继承传递给子类,但子类并不具备执此行为的能力;方法爆炸指继承树不断扩大,底层类拥有的方法过于繁杂,导致很容易选择错误。

9、 简单说一下设计模式?你都知道哪些?

  • 设计模式分为23种
  • 总体来说可以分为三大类:1.创建型模式( Creational Patterns )、2.结构型模式( Structural Patterns )、3.行为型模式( Behavioral Patterns )
  • 关注点:
  1. 创建型模式:关注对象的创建
  2. 结构型模式:关注类与对象之间的组合
  3. 行为型模式:关注对象之间的通信
  • 分类:
    1.创建型模式:
    (1)单例模式
    (2)工厂模式
    (3)抽象工厂模式
    (4)建造者模式
    (5)原型模式
    2.结构型模式
    (1)代理模式
    (2)适配器模式
    (3)桥接模式
    (4)装饰模式
    (5)外观模式
    (6)享元模式
    (7)组合模式
    3.行为型模式
    (1)模板方法模式
    (2)策略模式
    (3)命令模式
    (4)责任链模式
    (5)状态模式
    (6)观察者模式
    (7)中介者模式
    (8)迭代器模式
    (9)访问者模式
    (10)备忘录模式
    (11)解释器模式

10、 讲一讲适配器模式?

适配器模式属于结构型模式,它作为两个不兼容接口之间的桥梁,结合了两个独立接口的功能,将一个类的接口转换成另外一个接口使得原本由于接口不兼容而不能一起工作的类可以一起工作。
缺点是过多使用适配器会让系统非常混乱,不易整体把握。
        java.io包中, InputStream字节输入流通过适配器InputStreamReader转换为Reader字符输入流。
        Spring MVC中的HandlerAdapter,由于handler有很多种形式,包括Controller. HtpRequesthander, Servlet等,但调用方式又是确定的,因此需要适配器来进行处理,根据适配规则调用handle方法。
        Arras.asList方法,将数组转换为对应的集合(注意不能使用修改集合的方法,因为返回的ArrayList是ATTays的一个内部类) 。

11、 说一下设计模式?你都知道哪些?

设计模式总共有 23 种,总体来说可以分为三大类:创建型模式( Creational Patterns )、结构型模式( Structural Patterns )和行为型模式( Behavioral Patterns )。

分类包含关注点
创建型模式工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式关注于对象的创建,同时隐藏创建逻辑
结构型模式适配器模式、过滤器模式、装饰模式、享元模式、代理模式、外观模式、组合模式、桥接模式关注类和对象之间的组合
行为型模式责任链模式、命令模式、中介者模式、观察者模式、状态模式、策略模式、模板模式、空对象模式、备忘录模式、迭代器模式、解释器模式、访问者模式关注对象之间的通信

下面会对常用的设计模式分别做详细的说明。

12、 观察者模式和发布订阅模式的区别?

观察者模式提供了一种降低一对多依赖关系的方法,使对象间的紧耦合关系变为松耦合关系。

发布订阅模式可以说是观察者模式的高级实现,它比观察者模式中间多了一个经纪人的角色,使对象间的耦合度进一步降低甚至可以说是无耦合状态。

具体的区别可以用一张图来说明:

13、 讲一下观察者模式?

观察者模式属于行为型模式,也叫发布订阅模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。主要解决一个对象状态改变给其他对象通知的问题,缺点是如果被观察者对象有很多的直接和间接观察者的话通知很耗时,如果存在循环依赖的话可能导致系统崩溃,另外观察者无法知道目标对象具体是怎么发生变化的。
        ServletContextListener能够监听ServletContext对象的生命周期实际上就是监听Web应用。当Servlet容器启动Web应用时调用contextlnitialized方法,终止时调用contextDestroyed方法。

14、 说说什么是单例模式

单例模式是一种常用的软件设计模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在,整个系统只能使用一个对象实例。
优点:不会频繁地创建和销毁对象,浪费系统资源。
可能这会需要你手写一个单例模式,这就得自己去学了,因为单例模式有很多种写法,懒汉模式,饿汉模式,双重检查模式等。懒汉模式就是用的时候再去创建对象,饿汉模式就是提前就已经加载好的静态static对象,双重检查模式就是两次检查避免多线程造成创建了多个对象。
单例模式有很多种的写法,我总结一下:
(1)饿汉式单例模式的写法:线程安全
(2)懒汉式单例模式的写法:非线程安全
(3)双检锁单例模式的写法:线程安全

15、 能写两个单例模式吗?

饿汉式:
public class Singleton { 
// 直接创建对象
 public static Singleton instance = new Singleton();
// 私有化构造函数 
private Singleton() { }
// 返回对象实例 
public static Singleton getInstance() {
 return instance; 
 }
}
懒汉式:
public class Singleton { 
// 声明变量
   private static  Singleton singleton = null;
// 私有构造函数 
   private Singleton() { }
// 提供对外方法
   public static Singleton getInstance() { 
// 两次判空是因为,等待锁时可能已经被人初始化过了,如果不再判断可能会重复初始化。
    if (singleton == null) {
      synchronized(this) {
        if (singleton == null) {
          singleton = new Singleton(); 
        } 
      }
    }

     return singleton;
    }
}

16、 说说你对代理模式的理解

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

黄牛卖火车票:没有流行网络购票的年代是很喜欢找黄牛买火车票的,因为工作忙的原因,没时间去买票,然后就托黄牛给你买张回家过年的火车票。这个过程中黄牛就是代理人,火车票就是被代理的对象。

婚姻介绍所:婚姻介绍所的工作人员,搜集单身人士信息,婚介所的工作人员为这个单身人士找对象,这个过程也是代理模式的生活案例。对象就是被代理的对象。

注意了,问代理模式的时候,很有可能会问:动态代理。在Spring篇中已经讲述过,如果你把动态代理讲了后,很有可能还会问什么是静态代理?一个洞一个是静,大致也能才出来,就是中间代理层使我们手动写的,通常说的代理模式就是静态代理。

17、 说说策略模式在我们生活的场景

策略模式是指定义一系列算法,将每个算法都封装起来,并且使他们之间可以相互替换。
优点:遵循了开闭原则,扩展性良好。
缺点:随着策略的增加,对外暴露越来越多。
条条大路通罗马,条条大路通北京。
我们去北京的交通方式(策略)很多,比如说坐飞机、坐高铁、自己开车等方式。每一种方式就可以理解为每一种策略。
这就是生活中的策略模式。

18、 装饰器模式和动态代理的区别?

装饰器模式的关注点在于给对象动态添加方法,而动态代理更注重对象的访问控制。动态代理通常会在代理类中创建被代理对象的实例,而装饰器模式会将装饰者作为构造方法的参数。

19、 说一下工厂方法模式?

工厂方法模式指定一个创建对象的接口,让接口的实现类决定创建哪种对象,让类的实例化推迟到子类中进行。

客户端只需要关心对应工厂而无需关心创建细节,主要解决了产品的拓展问题,在简单工厂模式中如果产品种类变多,工厂的职责会越来越多,不便于维护。

Collection接口这个抽象工厂中定义了一个抽象的工厂方法,返回一个Iterator类的抽象产品。该方法通过ArrayList、HashMap等具体工厂实现,返回itr,keyiteratoe等具体产品。

spring的FactoryBean的接口方法也是工厂方法。

20、 装饰器模式是什么

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

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

装饰器模式的关键:装饰器中使用了被装饰的对象。

比如,创建一个对象“laowang”,给对象添加不同的装饰,穿上夹克、戴上帽子…,这个执行过程就是装饰者模式。

一句名言:人靠衣裳马靠鞍。都是装饰器模式的生活案列。

21、 设计模式的优点?

设计模式可在多个项目中重用。 设计模式提供了一个帮助定义系统架构的解决方案。设计模式吸收了软件工程的经验。 设计模式为应用程序的设计提供了透明性。设计模式是被实践证明切实有效的,由于它们是建立在专家软件开发人员的知识和经验之上的。

22、 设计模式的分类?

  1. 根据目的来分 根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型模式和行为型模式 3 种。 创建型模式:用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。行为型模式:用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。

  2. 根据作用范围来分 根据模式是主要用于类上还是主要用于对象上来分,这种方式可分为类模式和对象模式两种。 类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。GoF 中的工厂方法、(类)适配器、模板方法、解释器属于该模式。对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。GoF 中除了以上 4 种,其他的都是对象模式。

23、 设计模式的六大基本原则?

(1)单一原则(Single Responsibility Principle):一个类只负责一项职责,尽量做到类的只有一个行为原因引起变化;

(2)里氏替换原则(LSP liskov substitution principle):子类可以扩展父类的功能,但不能改变原有父类的功能;

(3)依赖倒置原则(dependence inversion principle):面向接口编程;

(4)接口隔离(interface segregation principle):建立单一接口;

(5)迪米特原则(law of demeter LOD):最少知道原则,尽量降低类与类之间的耦合;(6)开闭原则(open closed principle):用抽象构建架构,用实现扩展原则;

24、 说一下你熟悉的设计模式?

**设计模式:**是一套被反复使用的代码设计经验的总结(情境中一个问题经过证实的一个解决方案)。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。

**创建型:**Abstract Factory(抽象工厂模式),Builder(建造者模式),Factory Method(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式);

**结构型:**Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式),Flyweight(享元模式),Proxy(代理模式);

**行为型:**Command(命令模式),Interpreter(解释器模式),Visitor(访问者模式),Iterator(迭代模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(责任链模式)。 

25、 谈谈Spring中用到了哪些设计模式?

工厂设计模式 : Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
代理设计模式 : Spring AOP 功能的实现。
单例设计模式 : Spring 中的 Bean 默认都是单例的。
模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
装饰者(包装器)设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

26、 设计模式的分类,你知道哪些设计模式?

创建型:在创建对象的同时隐藏创建逻辑,不使用new直接实例化对象,程序在判断需要创建哪些对象时更加灵活。包括工厂、抽象工厂、单例、建造者、原型模式。

结构型:通过类和接口间的继承和引用实现创建复杂结构的对象。包括适配器、桥接模式、过滤器、组合、装饰器、外观、享元、代理模式。

行为型:通过类之间不同通信方式实现不同行为。包括责任链、命名、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板、访问者模式。

27、 请列举出在 JDK 中几个常用的设计模式?

单例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些类中。工厂模式
(Factory pattern)被用于各种不可变的类如 Boolean,像 Boolean.valueOf,观察者模式
(Observer pattern)被用于 Swing 和很多的事件监听中。装饰器设计模式(Decorator
design pattern)被用于多个 Java IO 类中。

28、 说一说设计模式的六大原则

开闭原则(总纲)、里氏替换原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特法则。

开闭原则:

一个软件实体应该对扩展开放,对修改关闭。 即:软件实体应该在尽量不修改原有代码的情况下扩展。

里氏替换原则:

所有引用基类(父类、接口)的地方,都必须能够透明的使用其子类的对象。

例如:我喜欢动物(父类)—(替换为)—》我喜欢狗(子类)【√】

反例:我喜欢狗(子类)—(替换为)—》我喜欢动物(父类)【×】

依赖倒置原则:

依赖倒置的核心原则就是面向接口编程。

上层模块不应该依赖下层模块,两者应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

例如:学生写字需要用到笔,学生类里面就要依赖笔这个接口,而不应该依赖某一种笔(比如铅笔)。

单一职责原则:

一个类只负责一个功能领域中相应的职责,换而言之,一个类应该只有一个引起它变化的原因。

如果一个类承担了过多的职责,就会使多个职责耦合在一起,只要有一个职责变化,那么就需要改动这个类。

接口隔离原则:

使用多个专门的接口,而不使用单一的总接口。例如:狗会叫还会跑,叫和跑应该分成两个接口,狗分别实现这两个接口,而不应该将叫和跑放在同一个接口里。

迪米特法则:

迪米特法则又称“最少知识原则”,即一个类应该只拥有有限的知识,一个类应该尽可能少的与其它类发生作用。

在迪米特法则中,一个类应该只与它的“朋友”通信,如果要用到其它类的方法,可以通过朋友转发调用。

迪米特法则中,“朋友”包括以下几种:

  1. 当前对象 this
  2. 以参数形式传递到当前对象方法中的对象
  3. 当前对象的成员对象
  4. 如果当前对象是一个集合,那么集合中的对象也是“朋友”。
  5. 当前对象所创建的对象

迪米特法则的目的就是让类之间解耦,降低类之间的耦合度,使类的复用性增强。

29、 除了单例模式,你在生产环境中还用过什么设计模式?

这需要根据你的经验来回答。一般情况下,你可以说依赖注入,工厂模式,装饰模式或者观察者模式,随意选择你使用过的一种即可。不过你要准备回答接下的基于你选择的模式的问题。

30、 讲一讲策略模式?

策略模式属于行为型模式,定义了一系列算法并封装起来,之间可以互相替换。策略模式主要解决在有多种算法相似的情况下,使用ifelse所带来的难以维护。
优点是算法可以自由切换,可以避免使用多重条件判断并且扩展性良好,缺点是策略类会增多并且所有策略类都需要对外暴露。
在集合框架中,经常需要通过构造方法传入一个比较器Comparator进行比较排序。Comparator就是
一个抽象策略,一个类通过实现该接口并重写compare方法成为具体策略类。
创建线程池时,需要传入拒绝策略,当创建新线程使当前运行的线程数超过maximumPoolsize时会使用相应的拒绝策略处理。

相关文章