设计模式简介
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
23种设计模式逐一说明
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
饿汉式 (static类加载实例化对象,不管用不用)
/**
* @author : LeeGaki
* @date : 2022/7/7 9:16
* 面向面试编程 --> 李佳琪
*/
public class Singleton1 {
private static final Singleton1 SINGLETON_1 = new Singleton1();
private Singleton1(){
}
public static Singleton1 getSingleton1(){
return SINGLETON_1;
}
public static void main(String[] args) {
Singleton1 singleton1 = Singleton1.getSingleton1();
Singleton1 singleton11 = Singleton1.getSingleton1();
System.out.println(singleton1==singleton11);
}
}
静态内部类方法
静态内部类不会在主类加载时加载,只有用到才会加载。
/**
* @author : LeeGaki
* @date : 2022/7/7 9:16
* 面向面试编程 --> 李佳琪
*/
public class Singleton3 {
private Singleton3(){
}
public static class Sing{
private static Singleton3 SINGLETON_1 = new Singleton3();
}
public static Singleton3 getSingleton1(){
return Sing.SINGLETON_1;
}
public static void main(String[] args) {
Singleton3 singleton1 = Singleton3.getSingleton1();
Singleton3 singleton11 = Singleton3.getSingleton1();
System.out.println(singleton1==singleton11);
}
}
懒汉式(线程不安全,用时加载)
利用双重检测保证线程安全 volatile防止指令重排又保证程序原子性操作
/**
* @author : LeeGaki
* @date : 2022/7/7 9:16
* 面向面试编程 --> 李佳琪
*/
public class Singleton2 {
private volatile static Singleton2 singleton2;
private Singleton2(){
}
public static Singleton2 getSingleton1() throws InterruptedException {
Thread.sleep(1);
if (singleton2 == null){
synchronized(Singleton2.class){
if (singleton2 == null){
singleton2 = new Singleton2();
}
}
}
return singleton2;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
try {
singleton2 = Singleton2.getSingleton1();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(singleton2);
}).start();
}
}
}
枚举默认单例
/**
* @author : LeeGaki
* @date : 2022/7/7 9:16
* 面向面试编程 --> 李佳琪
*/
public enum Singleton4 {
SINGLETON_4;
public static Singleton4 getSingleton4(){
return SINGLETON_4;
}
public static void main(String[] args) {
Singleton4 singleton4 = Singleton4.getSingleton4();
Singleton4 singleton5 = Singleton4.getSingleton4();
System.out.println(singleton4==singleton5);
}
}

暴力反射均可破坏单例。获取构造器创建对象。setAccessible(true)破坏私有
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Constructor<Singleton2> constructor = Singleton2.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
Singleton2 singleton2 = constructor.newInstance();
Singleton2 singleton3 = constructor.newInstance();
System.out.println(singleton2.hashCode());
System.out.println(singleton3.hashCode());
}

工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
简单工厂
简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离,降低系统的耦合度,使得两个修改起来相对容易些,当以后实现改变时,只需要修改工厂类即可。
创建一个接口汽车,然后创建汽车工厂,通过传入的参数不同返回不同的实现了汽车接口的实例化对象,比如宝马,奔驰。
工厂方法
创建工厂接口,让汽车的工厂都实现工厂接口,需要什么牌子的汽车就实例化什么牌子的工厂,最后一步是一样常见汽车的具体代码。
便于扩展。增加汽车牌子,只需要汽车去实现car接口,汽车工厂去实现工厂方法。
Factory factory = (需要什么工厂)(不用管实例化的是哪个汽车牌子)
Car car = factory.creatCar(得到实例化的对象)
抽象工厂模式
抽象工厂模式拥有多个抽象产品类(产品族)和一个抽象工厂类,每个抽象产品类可以派生出多个具体产品类;抽象工厂类也可以派生出多个具体工厂类,同时每个具体工厂类可以创建多个具体产品类的实例。
建造者模式
它提供了一种创建对象的最佳方式。
介绍:
1.建造者模式,又叫生成器模式,是一种对象构建模式。它可以将复杂的对象建造过程抽象处理来,使这个抽象过程的不同实现方法可以构造出不同表现或属性的对象
2.建造者模式是一步一步创建一个复杂的对象,它允许用户通过制定复杂对象的类型和内容就可以构建他们,用户不需要知道具体的构建细节
【4个角色】
1.product(产品角色):一个具体的产品对象
2.builder(抽象建造者):创建一个product对象的各个部件指定的接口
3.concreteBuilder(实际建造者):实现接口,构建和装配各个部件
4.director(指挥者):构建一个使用builder对象的接口。他主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程
房子建造者中含有房子类,房子类中定义好一个房子实现的公共流程
当每一个实例房子继承房子建造者都要重写单独的建造方法细节,去装饰这个房子。
然后指挥者去控制装饰房子的是高级还是普通
最后客户端要什么拿什么
抽象工厂模式 VS 建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
原型模式
【注意事项和细节】
1.创建的对象较为复杂的时候,可以利用原型模式简化对象的创建过程,同时也能够提高效率
2.不用重新初始化对象,而是动态获得对象运行时状态
3.如果原始对象发生变化时(增加或减少属性),其克隆对象也会发生相应的变化,无需修改代码
4.在实现深拷贝的时候可能需要比较复杂的代码
5.【缺点】需要为每一个原型类配一个克隆方法,这对新增的类来说,比较容易,但对已有的类做修改时,就违背了ocp原则
【原型模式的实现】
场景:经典克隆羊例子。spring的bean创建方式
主要有两种实现方式:
浅拷贝:原型类重写object类的clone方法。不会拷贝引用类型的属性,如list,obj对象,只是会拷贝这些引用对象的引用等
深拷贝:原型类需重写object类的clone方法。使用在clone方法对引用对象在做处理或序列化。
浅拷贝生成的对象是一个新对象,但是假如被拷贝的对象中有其他对象,就不会重新建一个新的子对象,而是主对象和拷贝对象中的子对象属性都指向主对象中的地址。
深拷贝 将主对象中的子对象类中也继承Cloneable和序列化操作,重写拷贝方法,将子对象拷贝后设置到主对象的属性中。
装饰者模式
【定义】装饰者模式:动态的将新功能附加在对象上。在对象的扩展方面它比继承更有弹性,装饰者模式也体现了面向对象的核心思想ocp(开放封闭)原则
【原理】
1.装饰者模式就像是打包一个快递
2.component组件:
主体(被装饰者):陶瓷,衣服等
包装(装饰者 Decorator):报纸填充、塑料泡沫、纸板、木板等
3.concreteComponent:具体的主体
Decorator:具体包装
4.component与concreteComponent中间还可以设计一个缓冲层,将共有的部分提取出来,作为一个单独的缓冲层
代理模式
静态代理 动态代理 (JDK代理、Cglib代理)
静态代理和动态代理的区别
1、静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
2、静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
3、动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。
4、还有一种动态代理CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。
静态代理的特点
1、目标角色固定
2、在应用程序执行前就得到目标角色
3、代理对象会增强目标对象的行为
4、有可能存在多个代理 引起"类爆炸"(缺点)
动态代理
相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。
动态代理的特点
1、目标对象不固定
2、在应用程序执行时动态创建目标对象
3、代理对象会增强目标对象的行为
模板方法模式
【介绍】
1.模板方法模式又叫模板模式,在一个抽象类中公开定义了执行它的模板方法,它的子类可以按需要重写方法实现,但调用将以抽象类中的定义来进行
2.简单来说,模板方法定义了一个操作中的算法骨架,而将一些步骤延迟到子类中去执行,使得子类可以不改变一个算法结构,就可以重新定义某些特定的步骤
【模板模式钩子方法对上面的改进】
理解:在模板方法模式的父类中,定义一个方法,默认不做任何事情,子类可以视情况去覆盖它,该方法称为“钩子”方法
【模板模式:注意事项和细节】
1.基本思想:算法只存在一个地方,就是父类中,容易修改,一改全改。
2.实现了最大化代码复用,父类的模板方法和某些已经实现的方法可以被子类字节使用
3.即统一了算法,也提供了很大的灵活性。
4.不足:每一个不同实现都需要一个类,导致类的数量变多
5.一般父类的模板方法都要加final,防止子类覆盖
6.使用场景:完成某个过程,这些过程调用顺序基本相同,个别步骤可能不同情况下,可以使用模板方法模式
好像与建造者模式很像?他们有什么区别?
建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。旨在创建对象。
模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。是行为旨在这个事情要怎么做
观察者模式
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
观察者模式是一种对象行为型模式,其主要优点如下。
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
- 目标与观察者之间建立了一套触发机制。
它的主要缺点如下。
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
【使用观察者模式好处】
1.观察者模式设计好后,会以集合的形式来管理用户(Observe),包括注册、移除、 通知
2.这时新增一个观察者,就不需要去修改核心的被观察者。遵守OCP原则