当前位置:   article > 正文

面试-设计模式(全)_设计模式面试

设计模式面试

设计模式

一、设计模式六大原则

  • 单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。
  • 开放封闭原则:类、模块、函数等应该是可以拓展的,但是不可修改。
  • 里氏替换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。
    • 尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法。
    • 运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码
    • 增加新的功能可以通过增加一个新的子类来实现
    • 里氏替换原则是开放封闭原则的具体实现手段之一
  • 依赖倒置原则:
    • 定义:高层模块不应该依赖低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
    • 抽象指接口或者抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或者继 承抽象类而产生的就是细节,也就是可以加上一个关键字new产生的对象。
    • 高层模块就是调用端,低层模块就是具体实现类
    • 依赖倒置原则在Java中的表现就是,模块间的依赖通过抽象发生,实现类之间不发生直接依赖关系,其依赖关系是通过接口或者抽象类产生的。如果类与类直接依赖细节,那么就会直接耦合。
  • 迪米特原则:一个软件实体应当尽可能少地与其他实体发生相互作用。
    • 最少知识原则
    • 在类的划分上,应当尽量创建松耦合的类
    • 在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限。
    • 在对其他类的引用上,一个对象对其他对象的引用应当降到最低
  • 接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上
    • 接口尽量小,但是要有限度。
    • 依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来
    • 提高内聚,减少对外交互。接口方法尽量少用public修饰。

二、设计模式分类

  • 创建型设计模式,共5种:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。

  • 结构型设计模式,共7种:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  • 行为型设计模式,共11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

三、创建型设计模式(5)

创建型设计模式,顾名思义就是与对象创建有关,它包括单例模式、工厂方法模式、抽象工厂模式、

建造者模式、原型模式。

1. 单例模式

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点

(1)饿汉模式
public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){
        
    }
    
    public static Singleton getInstance(){
        return instance;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 在类加载时就完成初始化,所以类加载较慢,但获取对象的速度快
  • 在类加载时就完成初始化,避免了多线程的问题
  • 在类加载时就完成实例化,没有达到懒加载效果
  • 如果从始至终都没有使用过这个实例,就会造成内存的浪费
(2)懒汉模式(线程不安全)
public class Singleton{
    private static Singleton instance;
    private Singleton(){
        
    }
    
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
(3)懒汉模式(线程安全)
public class Singleton{
    private static Singleton instance;
    private Singleton(){
        
    }
    
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 线程安全
  • 但每次调用getInstance方法时都需要进行同步(造成不必要的同步开销,而且很多时候我们是用不到同步的)
  • 不建议使用
(4)双重检查模式(DCL)
public class Singleton{
    private static volatile Singleton instance;
    private Singleton(){
        
    }
    
    public static Singleton getInstance() {
        if(instance == null) {
            synchronized(Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • static的用法
    • 三种用法
    • static变量存在哪?
    • 加载的时机
  • volatile的用法
    • 可见性
    • 禁止指令重排序:在该代码中是禁止哪条指令重排序
  • 在getSingleton方法中对Singleton进行了两次判空
    • 第一次是为了不必要的同步
    • 第二次是在 Singleton等于null的情况下才创建实例
  • 优点:资源利用率高
(4)静态内部类单例模式
public class Singleton{
    private Singleton(){
        
    }
    public static Singleton getInstance getInstance() {
        return SingletonHolder.sInstance;
    }
    
    private static class SingletonHolder {
        private staic final Singleton sInstance = new Singleton();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载 SingletonHolder 并初始化 sInstance。这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

所以,推荐使用静态内部类单例模式。

(6)枚举单例
public enum Singleton {
    INSTANCE;
    public void doSomeThing() {
        
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

默认枚举实例的创建是线程安全的,并且在任何情况下都是单例。


在上面讲的几种单例模式实现中,有一种情况下其会重新创建对象,那就是反序列化将一个单例实例对象写到磁盘再读回来,从而获得了一个实例。反序列化操作提供了readResolve方法,这个方法可以让开发人员控制对象的反序列化。在上述几个方法示例中,如果要杜绝单例对象被反序列化时重新生成对象,就必须加入如下方法:

private Object readResolve() throws ObjectStreamException {
    return singleton;
}
  • 1
  • 2
  • 3
(7)使用场景

在一个系统中,要求一个类有且仅有一个对象,它的具体使用场景如下:

• 整个项目需要一个共享访问点或共享数据。

• 创建一个对象需要耗费的资源过多,比如访问I/O或者数据库等资源。

• 工具类对象。

2. 简单工厂模式

简单工厂模式(又叫作静态工厂方法模式),其属于创建型设计模式,但是并不属于 23种GoF设计模式之一。

定义:简单工厂模式属于创建型模式,其又被称为静态工厂方法模式,这是由一个工厂对象决定创建出哪一种产品类的实例。

image-20210501122717537

  • Factory:工厂类,这是简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。

  • IProduct:抽象产品类,这是简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。

  • Product:具体产品类,这是简单工厂模式的创建目标。

3. 工厂方法模式

image-20210501125623744

4. 建造者模式

  • 适用于复杂对象

image-20210501133512789

四、结构型设计模式

结构型设计模式是从程序的结构上解决模块之间的耦合问题,它包括适配器模式、代理模式、装饰模式、外观模式、桥接模式、组合模式和享元模式。

1. 代理模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H3WYyJcq-1628430944333)(C:/Users/wei/AppData/Roaming/Typora/typora-user-images/image-20210501135307568.png)]

2. 装饰模式

image-20210501135556043

3. 外观模式

image-20210501140900391

4. 享元模式

image-20210501142720906

五、行为型设计模式

行为型模式主要处理类或对象如何交互及如何分配职责。它共有11种模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式和解释器模式。

1. 策略模式

image-20210501143025582

2. 模板方法模式

在软件开发中,有时会遇到类似的情况:某个方法的实现需要多个步骤,其中有些步骤是固定的;而有些步骤并不固定,存在可变性。为了提高代码的复用性和系统的灵活性,可以使用模板方法模式来应对这类情况。

定义:定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类不改变一个算法的结构即可重定义算法的某些特定步骤。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LCyHNxZF-1628430944343)(C:/Users/wei/AppData/Roaming/Typora/typora-user-images/image-20210501143659054.png)]

使用场景:

  • 多个子类有共有的方法,并且逻辑基本相同时。

  • 面对重要、复杂的算法,可以把核心算法设计为模板方法,周边相关细节功能则由各个子类实现。

  • 需要通过子类来决定父类算法中的某个步骤是否执行,实现子类对父类的反向控制。

• 优点:

  1. 模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
  2. 子类实现算法的某些细节,有助于算法的扩展。

• 缺点:

每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象

3. 观察者模式

观察者模式又被称为发布-订阅模式,属于行为型设计模式的一种,是一个在项目中经常使用的模式。 它的定义如下。

**定义:**定义对象间一种一对多的依赖关系,每当一个对象改变状态时,则所有依赖于它的对象都会 得到通知并被自动更新。

image-20210501144314317

  1. Subject:抽象主题(抽象被观察者)。抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
  2. ConcreteSubject:具体主题(具体被观察者)。该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  3. Observer:抽象观察者,是观察者的抽象类。它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  4. ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

• 使用场景:

  1. 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
  2. 事件多级触发场景。
  3. 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

• 优点:

  1. 观察者和被观察者之间是抽象耦合,容易扩展。
  2. 方便建立一套触发机制。

• 缺点:

在应用观察者模式时需要考虑一下开发效率和运行效率的问题。程序中包括一个被观察者、多 个观察者,开发、调试等内容会比较复杂,而且在 Java 中消息的通知一般是顺序执行的,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步方式


class Solution {
    public String addStrings(String num1, String num2) {
        StringBuilder res = new StringBuilder("");
        int i = nums1.length() -1, j = nums2.length() - 1;
        int carry = 0;
        while(i >= 0 || j >= 0) {
            int n1 = i >= 0 ? num1.charAt(i) - '0' : 0;
            int n2 = j >= 0 ? num2.charAt(j) - '0' : 0;
            
            int temp = n1 + n2 + carry;
            carry = temp / 10;
            res.append(temp % 10);
            i--;
            j--;
        }
        if(carry == 1) res.append(1);
        return res.reverse().toString();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
       int n2 = j >= 0 ? num2.charAt(j) - '0' : 0;
        
        int temp = n1 + n2 + carry;
        carry = temp / 10;
        res.append(temp % 10);
        i--;
        j--;
    }
    if(carry == 1) res.append(1);
    return res.reverse().toString();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

}


  • 1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/157550
推荐阅读
相关标签
  

闽ICP备14008679号