上一篇传送门: 点我
有哪些设计模式?
按照模式的应用目标分类,可以分为创建型模式、结构型模式、行为型模式三类。
创建型模式:
对象实例化的模式,创建型模式用于解耦对象的实例化过程。
单例模式:某个类只能有一个实例,提供一个全局的访问点。
工厂方法模式:一个工厂类根据传入的参量决定创建出哪一种产品类的实例。
抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。
创建者模式:封装一个复杂对象的创建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。
结构型模式:
把类或对象结合在一起形成一个更大的结构。
装饰器模式:动态的给对象添加新的功能。
代理模式:为其它对象提供一个代理以便控制这个对象的访问。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
适配器模式:将一个类的方法接口转换成客户希望的另一个接口。
组合模式:将对象组合成树形结构以表示"部分-整体"的层次结构。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
享元模式:通过共享技术来有效的支持大量细粒度的对象。
行为型模式:
类和对象如何交互,及划分责任和算法。
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
模板方法模式:定义一个算法结构,而将一些步骤延迟到子类实现。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
观察者模式:对象间的一对多的依赖关系。
仲裁者模式:用一个中介对象来封装一系列的对象交互。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
建造者模式:允许一个对象在其对象内部状态改变时改变它的行为。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
访问者模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能。
说说设计模式的基本原则
1.单一职责原则 :对于一个类,只有一个引起该类变化的原因;该类的职责是唯一的,且这个职责是唯一引起其他类变化的原因;
2.里氏替换原则 :子类可以扩展父类的功能,但不能改变原有父类的功能;
3.依赖倒置原则 :程序要依赖于抽象接口,不要依赖于具体实现 。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合;
4.接口隔离 :客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上;
5.迪米特原则 :迪米特法则又叫做最少知识原则,就是说一个对象应当对其它对象又尽可能少的了解 ,不和陌生人说话;
6.开闭原则 :对扩展开放,对修改关闭;
7.合成复用原则 :在软件复用时,要尽量优先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
单例模式
什么是单例模式?
单例模式是一种创建型设计模式 ,它必须保证单例对象的类仅有一个实例,并提供一个访问它的全局访问点 。这个模式主要应用于那些只需要一个对象来协调整个系统运作的场景。通过确保类只有一个实例,我们可以节省系统资源,因为不需要为多次创建和销毁对象分配内存。同时,单例模式也有助于确保数据的一致性,因为所有的请求都通过同一个实例来处理。
单例模式如何实现?
单例模式常见的实现方法有以下几种:
1.饿汉式 :在类加载时就完成了初始化 ,所以类加载较慢,但获取对象的速度快 。它是线程安全的实现方式。
java
public class Singleton {
// 在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
2.懒汉式 :在第一次调用时初始化 ,这是懒加载 的思想。但是这种方式在多线程环境下是不安全的 。由于懒汉式在获取单例对象时,会先判断当前是否已经存在对象实例,而如果同一时刻有两个甚至更多线程去进行判断,则可能会创建出多个单例对象。因此不做任何处理的懒汉式创建单例对象是非线程安全的。
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
//调用时若还没有对单例对象初始化,则new一个对象(线程不安全)
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.优化后的懒汉式:由于一般的懒汉式会有线程不安全的问题,所以可以考虑给代码加synchronized锁,从而避免线程安全问题的出现。
可以在getInstance()方法上添加synchronized关键字,从而保证线程安全。
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
//方法加锁保证线程安全
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
以上方法虽然保证了线程安全,但是每次调用getInstance()都需要进行同步,由于只有第一次调用时才会创建单例对象,因此这会造成大量不必要的同步开销。另一方面,由于在JVM内部会存在指令重排序 (JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能)的情况出现,这在并发场景下,有可能会出现NullPointerException空指针异常。
PS:指令重排可参考:我给面试官讲解了单例模式后,他对我竖起了大拇指!
因此可以设置双重校验锁 ,并结合volatile关键字,从而在保证线程安全的同时减少不必要的开销,也避免了指令重排问题的出现。
java
public class Singleton {
private volatile static Singleton instance; // 使用volatile防止JVM指令重排
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
//对代码块中的类对象加锁
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
4.静态内部类:静态内部类同样实现了懒加载,而且保证了线程安全。这种方式是Singleton模式的最佳实现之一。
java
public class Singleton {
private Singleton() {}
//定义静态内部类
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
5.枚举实现单例(最优!!!) :枚举法实现单例与上述其他方法相比,代码更加地简洁,并且不需要做任何额外的操作去保证对象单一性与线程安全性 ,最最重要的是,它能防止反射、序列化与反序列化 强制生成多个实例对象从而使得单例模式被破坏。
反射破坏单例的原理: 通过java反射中的setAccessible(true)能够强制访问类的私有构造器(将private权限强制转为public),从而可以调用构造器创建新的对象。
序列化与反序列化破坏单例的原理: 当使用readObject() 方法读入对象时,它必定会返回一个新的对象实例,必然指向新的内存地址。
java
public enum Singleton {
INSTANCE;
// 可以在这里定义其他方法
public void doSomething() {
// ...
}
}
工厂模式
什么是工厂模式?
工厂模式是一种用于创建对象 的设计模式,它通过将对象的创建逻辑与使用逻辑分离,从而提高了代码的可维护性和可扩展性。在工厂模式中,我们定义一个抽象的工厂接口,用于声明创建对象的方法,然后创建具体的工厂类来实现这个接口。这些具体的工厂类负责根据特定的需求创建相应的对象。通过这种方式,我们可以灵活地管理和扩展对象的创建过程,而无需修改客户端代码。
工厂模式有哪几种,它们的区别是什么?
工厂模式主要有三种:简单工厂模式、工厂方法模式和抽象工厂模式。
1.简单工厂模式(静态工厂模式): 这是最简单的工厂模式,但它不属于GOF23种设计模式中的其中一种,它通过一个工厂类来负责创建其他类的实例 。客户端只需要传入相应的参数,工厂类就会根据这些参数来返回对应的产品实例。这种模式的优点是实现了客户端与具体产品类的解耦,客户端无需关心产品的具体创建过程。然而,缺点是当需要增加新的产品时,需要修改工厂类的代码 ,这违背了开闭原则。
工厂方法模式: 工厂方法模式进一步抽象了对象的创建过程。它定义了一个创建对象的接口,让子类来决定具体实例化哪一个类 。这样,客户端只需要关心所需产品对应的工厂,而无需了解具体的创建细节。这种模式的优点是在增加新产品时,只需要增加相应的工厂子类,而无需修改原有的代码,这符合开闭原则。但缺点是增加了系统的复杂度,因为需要编写更多的工厂子类。
抽象工厂模式: 抽象工厂模式提供了一种方式来创建一系列相关或相互依赖的对象,而不需要指定它们具体的类。它定义了一个接口,用于创建相关产品族的多个产品对象 。客户端只需要知道产品的抽象类型或接口类型,而无需关心具体实现。这种模式的优点是能够隔离产品代码,提高系统的可扩展性和可维护性。然而,当产品族中需要增加一个新的产品时,需要修改抽象工厂的接口以及所有实现了该接口的工厂类,这同样会增加系统的复杂度和理解难度。
JDK中的常用设计模式有哪些?
1.单例模式 :确保一个类只有一个实例,并提供一个全局访问点。
JDK体现:Runtime类
2.工厂模式 :工厂模式用于创建对象,它提供了一种封装机制来减少程序之间的耦合性。
JDK体现:Integer.valueOf(),Class.forName()
3.原型模式 :拷贝原型来创建新的对象,拷贝是比new更快的创建对象的方法,
当需要大批量创建新对象而且都是同一类的对象的时候考虑使用原型模式。
JDK体现:clone()
4.代理模式 :为其他对象提供一种代理以控制对这个对象的访问。
JDK实现:动态代理
5.迭代器模式 :将集合的迭代和集合本身分离。
JDK实现:Iterator迭代器
6.适配器模式 :将一个类的接口转换成客户端所期望的另一个接口形式。
JDK实现:InputStream、OutputStream
7.策略模式 :定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。
JDK实现:JUC中线程池的拒绝策略
Spring中的常用设计模式有哪些?
1.单例模式 : 在Spring中,Bean的默认作用域就是单例 。这意味着在整个应用程序中只有一个Bean实例,由Spring容器负责管理。这种机制可以节省资源并提高性能。
2.工厂模式 :Spring中的BeanFactory 和ApplicationContext 就是工厂模式的应用。它们负责创建和管理Bean,隐藏了具体实例化的细节,使得应用程序更易于扩展和维护。
3.代理模式 :Spring AOP中广泛使用了代理模式。通过代理对象来实现对目标对象的增强,如日志记录、事务管理等。Spring支持JDK动态代理和CGLIB代理。
4.观察者模式 :Spring中的事件机制 就是观察者模式的应用。当某个事件发生时,相关的监听器会接收到通知并执行相应的操作。
5.模板方法模式 :在Spring的RedisTemplate 等模板类中使用了模板方法模式。它定义了一组通用的操作方法,同时允许子类根据需要自定义某些步骤的具体实现。
6.适配器模式 :Spring中的适配器模式应用比较广泛,如HandlerAdapter、ViewResolver等。它们都是通过适配器来实现不同组件之间的兼容。
下一篇传送门: 点我