设计模式面试题(设计模式速成版)

文章目录

  • 说明
  • 名词解释
  • UML基础
  • 面向对象编程中,都有哪些设计原则
    • 开闭原则
    • 里氏替换原则(Liskov Substitution Principle)
    • 依赖转置(依赖倒置)原则
    • 单一职责原则
    • 接口隔离原则
    • 迪米特法则
    • 合成复用原则
  • 设计模式的分类
  • 创建型模式
    • 单例模式
    • 原型模型
    • 工厂模式
      • 简单工厂模式
      • 工厂方法模式
      • 抽象工厂模式
      • 简单工厂、工厂方法和抽象工厂三者有什么区别
    • 建造者模式(Builder)
  • 结构型模式
    • 代理模式
      • Spring AOP如何进行动态代理
    • 装饰器模式
      • 装饰器模式和代理模式的区别
    • 适配器模式
    • 桥接模式
    • 外观模式(Facade)
    • 享元模式
      • 享元模式和原型模式比较
    • 组合模式
  • 行为型模型
    • 观察者模式
    • 中介者模式(mediator)
      • 中介者模式和观察者模式对比
    • 模板方法模式
    • 策略模式
    • 命令模式
    • 责任链模式
    • 状态模式
    • 迭代器模式
    • 访问者模式
    • 备忘录模式(快照模式)
    • 解释器模式

说明

该文章适用于之前学习过设计模式,但是基本忘了。使用该文章进行快速回忆。因为是应付面试(不是笔试),所以该文结合了自己的理解,使用大白话来解释各个模型,如有错误或不严谨的地方,欢迎在评论区指正。

若之前没有学习过设计模式,可以将该文章死记硬背,然后应付面试。

设计模式详细学习,可以参见 http://c.biancheng.net/view/1317.html

名词解释

  • 设计模式:软件设计模式是对各种面向对象方法的一种总结。前辈们遇到了好多设计问题,然后利用面向对象解决了。然后他们把解决方案汇总起来,形成了20多种设计模式。它可以有效的帮助我们利用面向对象,来提高代码的复用性、扩展性等。 设计模式包含4个关键元素:

    • 模式名称:就是设计模式的名字。
    • 问题:就是当面对什么样的设计问题时,应该采用该设计模式。即该设计模式的应用场景
    • 解决方案:该设计模式是使用什么样的方案解决的。
    • 效果:最后解决后,效果怎么样。比如扩展性怎么样,复用性怎么样。优缺点等等
  • 组合:将多个种类的对象合成一个新的对象。如 显示器 + 键盘 + 鼠标 +主机 = 电脑。

  • 聚合:将多个同种对象起来,形成一个新的对象,如 50个学生 = 一个班级

  • 用户:在设计模式中,用户并不是指具体使用系统的用户,而是使用你代码,或者是扩展你代码的其他程序员。

UML基础

UML主要用于表示类与类之间的关系。例如:

单例模式提供了两种方式:

  • 懒汉模式:重点是。系统运行初期并不把该单例对象实例化,而是等第一次使用的时候再进行实例化。就像你平时懒得学习,非要等到考前才学一样。
  • 饿汉模式:重点是饿,也就是饥渴。还没到用的时候,就先实例化了,等到用的时候直接就可以用了。就像平时对知识很饥渴,学好了,然后直接面试就行了。

原型模型

  • 模式名称:原型(prototype)就是人们常说的,把XXX作为原型,然后弄一个跟它差不多的。
  • 问题:经常会遇到需要克隆一个对象,然后对其稍作修改的业务场景。此时就可以使用原型模型,将对象A作为原型,然后克隆出一个对象B。
  • 解决方案:重写对象的clone方法。也可以根据需求自己写。根据实际需要决定要深拷贝还是浅拷贝。
  • 效果:简化了创建对象的过程

工厂方法模式

在简单工厂中,有一个缺点:如果我们想让工厂再能多生产一种东西,那就要修改这个工厂类的方法。这显然不符合开闭原则。

针对这个问题,所以引出了工厂方法模式。

在简单工厂模式中,不管生产什么品牌的鞋子,都用同一工厂。

而在工厂方法模式中,想要生产某一个品牌的鞋子,就使用其对应的工厂。也就是说一个工厂只生产一种产品,如果你想要的三种不同的产品,你就要有三个工厂。比如,耐克工厂专门生产耐克鞋,阿迪工厂专门生产阿迪鞋。当你后期想要扩展的时候,你就可以再来一个安踏工厂专门生产安踏鞋。

简单工厂、工厂方法和抽象工厂三者有什么区别

  • 简单工厂:所有的产品都由一个工厂生产。如果你想加产品,就改源代码。
  • 工厂方法:有多个工厂,但一个工厂只生产一种产品。如耐克厂只生产耐克鞋,阿迪厂只生产阿迪鞋。如果你想加产品,那就加工厂类。
  • 抽象工厂:有多个工厂,每个工厂可以生产多类产品。如耐克厂可以生产耐克鞋和耐克衣服,阿迪厂可以生产阿迪鞋和阿迪衣服。但如果你想生产帽子,那么就需要在抽象工厂里加抽象方法,然后修改所有的子类。

建造者模式(Builder)

  • 模式名称:用户就是建造者,自己动手造自己想要的东西。
  • 问题:有些产品需要由多个部件组成,比如电脑需要键盘、鼠标、显示器、音响、主机等等。这些东西有些需要,有些也可以不需要,有些可以随便用(取默认值)。将其抽象到代码世界,若这些配置的组合方式全部弄成构造函数,那需要好多构造函数。用户光看这些构造函数就看晕了。
  • 解决方案:可以将每个部件的构造过程都封装成方法,提供给用户,用户选完自己的部件后,调用build()方法构建出他想要的对象。
  • 效果:用户可以定制化自己的产品。避免臃肿的构造方法。

结构型模式

代理模式

  • 模型名称:和生活中的代理一个意思。本来该我做的事情,我找个代理人替我做。
  • 问题:我想把房子租出去,但是又不想直接带人看房,原因有很多:找客户、风险等。所以我找个中介(代理),让他替我把房子租出去。在代码世界中,前端调用一个后台方法,因为风险(要验证其是否登录),我不想让它直接调用核心方法,而是让它调用代理对象,如果没有风险,再由这个代理对象替它调用核心方法。
  • 解决方案:新建代理类,在代理类的方法中调用实际方法,可以在实际方法前后增加逻辑。当访问该方法时,不new该方法对应的对象,而是new其代理对象。通常有动态代理和静态代理
  • 效果:可以在方法前后增加一些业务逻辑,如日志、安全校验等。

装饰器模式为FileReader增加了Buffer功能。

装饰器模式和代理模式的区别

共同点:

  • 效果类似:装饰器模式和代理模式都可以在方法的前后增加逻辑
  • 可以嵌套:代理可以层层代理,装饰也可以层层装饰

区别:

  • 实现方式不同:代理类和原有类之间并没有什么直接关联。就像我找中介帮我卖房子,我和中介之间并没有什么直接关系,我出钱他办事,我是我,他是他。但是装饰类和被装饰类之间就有关系,他们有共同的父类。比如BufferReader装饰了FileReader,他们有共同的父类Reader。
  • 应用场景不同:代理模式主要用来做AOP,适用于同时对好多方法做切面。就像中介也不可能只帮我一个人卖房子。 而装饰器只专注于装饰一个类,BufferReader只专注于装饰Reader。

适配器模式

  • 模式名称:在现实世界中,充电器全名为电源适配器。它的作用是将220V的交流电转化为5V的直流电。适配器就是一个转换器,将两种不兼容的东西,通过转换器使其互相兼容。
  • 问题:在现实世界有很多适配器。在代码世界中,同样有很多相互之间不兼容的接口或类,它们之间无法直接相互调用,所以就需要适配器来进行转换。如”手机”调用”电源适配器”的充电接口,然后由电源适配器调用电源来获取电。
  • 解决方案:适配器(充电器)通过组合(也可以使用继承,但不推荐)的形式保存适配者(手机)的引用。同时适配器实现目标对象(电源)的方法(充电)。当用户要给访问目标方法时,就new一个适配器,然后将适配者传给适配器,通过适配器访问目标方法。 比如,用户要给手机充电时,先new一个充电器,然后把手机连上充电器(把手机对象传给充电器),然后充电器访问电源的充电方法。
  • 效果:解决了两个对象之间不兼容导致无法调用的问题。

外观模式(Facade)

  • 模式名称: 外观模式也叫门面模式。门面模式可能更形象一点。将所有细节都封装起来,只给你提供一个门面,你通过这个门面来做自己要做的事。
  • 问题:当你去银行办理业务时,流程可能很复杂,比如要先到窗口A登记信息,到窗口B验证信息,再到窗口C办理业务,最后到窗口D拿结果。
  • 解决方案:新建一个门面类(Facade),使用组合的方式组合窗口ABCD,然后只为客户提供一个窗口,客户在这个窗口就可以办理完毕。
  • 效果:降低了子系统和客户之间的耦合度。

享元模式

  • 模式名称:享元就是分享元数据。其中元为本原,也可以理解为本质。比如围棋子对象,可以有坐标、颜色、大小等。但围棋子的本质是它的颜色和大小,坐标是它的次要属性(每个棋子的坐标基本都不一样)
  • 问题:在程序中,需要创建大量相似的对象。比如围棋,需要很多围棋对象,它们除了坐标不一样,其他属性基本一致。LOL中的小兵,除了位置血量等不一样,其他属性也基本一样。假如每生成一个小兵,都使用深拷贝,那么内存中就会存在大量重复的数据。这样我的小破电脑肯定吃不消。
  • 基本概念: 这里插一条概念解释。
    • 外部状态:会改变的属性称为外部属性,如五子棋的坐标。它对应的对象被称为非享元对象
    • 内部状态:不会改变的属性。如五子棋的大小等。它对应的对象被称为享元对象
  • 解决方案:将原本的整个对象抽象出享元对象和非享元对象,然后通过工厂进行管理。用户通过工厂获取对象,工厂组合享元对象和非享元对象返回给用户。
  • 效果:优化了内存占用

行为型模型

观察者模式

  • 模式名称:作为偷窥者,看到脱衣服,要流鼻血,看到自己被发现了,要跑。作为观察者同样,当被观察的事物状态发生变化时,观察者要做出相应的反映。
  • 问题:在代码世界,用户也想让自己的对象监听(观察)另一个对象,当另一个对象发生变化后,能够回调自己的对象的方法,但是他们不知道怎么做。
  • 解决方案:目标(被观察者)对象要包含观察者列表,当目标对象状态发生变化时,目标对象要依次调用观察者的通知方法。
  • 效果:目标对象发生变化时,会通知观察者。

  • register:负责注册同事。如果你不来婚姻介绍所注册登记,人家怎么给你介绍对象
  • relay:转发消息。将消息转发给其他同事
  • receive:接收消息。中介的转发消息会调用每个同事的该方法。
  • send:发送消息。用户可以调用同事的该方法来发送消息,该方法会调用中介的relay方法来让中介去通知其他同事。这也就是为什么同事类要包含中介的引用。

中介者模式和观察者模式对比

相同点:中介者模式和观察者模式都可以用于当一方状态发生变化时通知其他人。

区别:

  • 观察者模式:主要用于一对多。多个对象观察一个对象。观察者和目标对象通常不一致。比如,多个人观察天气变化。
  • 中介者模式:主要用于多对多,大家都是观察者,同时也都是目标对象。每个对象也都一样,至少是同一种抽象。比如群聊,每个人都可以发消息,然后通过中介转发给其他所有人。

模板方法模式

  • 模式名称:与现实中的模板一致。拿一个简历模板,把里面内容改改。拷贝别人的代码模板,把里面内容改改。
  • 问题:在代码世界,经常会遇到很多逻辑相似的地方,但还不太一样。比如用纯jdbc操作数据库时,要连接数据库、构建Statement、写sql、获取结果、把结果转为对象。虽然都是这个流程,但是每次还都不太一样。如果每次都是复制之前的代码,然后改改,那会出现大量重复的代码。如果后期出现需求变换,比如要改下Statement的实现类,那所有涉及到的地方都要改。
  • 解决方案:将相同的过程抽象成一个抽象类,一模一样的地方用具体方法,不一样的地方使用抽象方法。下次再使用时,只需要继承该抽象类,然后实现其抽象方法即可。
  • 效果:减少了大量的重复逻辑。

设计模式面试题(设计模式速成版)

策略模式

  • 模式名称:一个人想干一件事,有很多策略,然后他选其中一种策略来做。
  • 问题:用户想对一组数据进行排序,有很多策略可以选,如冒泡排序、快速排序、堆排序等。他不太确定后期会不会变,要是为每种排序都写一个类,而后期想切换排序就会很麻烦。
  • 解决方案:定义一个接口,让其他排序类继承这个接口。用户在使用时不使用具体的类,而是使用接口。这样后期变更策略时,不会影响用户的代码。
  • 效果:将行为和具体实现分离开,便于后期扩展和维护。

策略模式其实到处都是,一个接口有好几个实现类,你选择其中一个进行使用。

但有一点不同的是,策略模式需要有策略的选择角色,比如环境类。通过设置环境对象,让环境对象帮助用户选择使用哪一种策略,而不是由用户自己选择。

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年5月25日
下一篇 2022年5月25日

相关推荐