目录
设计模式本质上是面向对象设计原则的实际运用,是对类的封装性、继承性、多态性以及类的关联关系和组合关系的充分理解。
最早诞生在建筑模式,后来引用到软件领域,经过前辈的代码设计的经验,总结的一套可以反复使用的设计模式。
java中设计模式共23种
分为3大类
一.创建型:
如何创建对象
1.单例模式
解决一个类在一个程序中保证只能创建一个对象
单例模式只创建一个对象,这个对象由单例类自己创建,并向外提供访问方法。
实现方式有两种:
1.饿汉式(急切式)
立即创建,类加载时就创建好,没有线程安全问题
2. 懒汉式
在类加载时,并不创建单例对象,在第一次获取单例对象时才创建
存在线程安全问题,如下:可能出现两个线程同时进入创建两个对象。
给方法添加锁后,虽然能解决线程安全问题,但是并发访问的效率低,
解决方法:
双重检索+volatile
例如:
static volatile WindowDemo windowDemo = null;
Runtime类就是一个单例类,可查看源码:
2.工厂模式
工厂类,负责生产某一类产品,同一类产品,将创建对象和使用对象分离。
在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦
简单工厂模式(不属于GOF的23种经典设计模式) :
简单工厂违背了开闭原则
例如Car工厂:加一个类型,工厂中的代码就需要修改。
使用场景:所有产品子类都有一个父类(或接口),属于同一个产品系列,产品子类比较少,创建操作比较简单。
工厂方法模式:
工厂方法,对工厂进行抽象,一个抽象的产品对应一个抽象的工厂
一个具体的产品,对应一个具体的工厂,一个具体的工厂负责生产一个具体的产品
需要扩展新产品时,只需添加新的具体产品和生产产品的工厂,不用修改原来的工厂,符合开闭原则。
结构:
抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂
方法来创建产品。
具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品(product):定义了产品的规范,描述了产品的主要特性和功能。
具体产品(concreteproduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间--对应。
3.抽象工厂模式
抽象工厂是生产一系列产品(某个公司的一系列产品:小米手机,小米汽车;)
在抽象的工厂中定义不同的产品。
具体的工厂负责生产一个公司的一系列产品。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。缺点:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
4.原型模式
使用对象克隆,以先创建出来的原型对象为模板。
使用对象克隆,以先创建出来的原型对象为模板进行对象复制,提高了创建效率
例如: 简历,奖状
示例:
二、结构型
1.代理模式
目标对象的代理者
例如:汽车厂买汽车,需要4s店代理去卖,可以帮助客户上牌,买保险,售后等其他服务;
帮助目标(实际的执行者),扩展目标对象的功能,而不修改目标对象代码。
优点:
将目标与添加的功能相分离,保护了目标对象,
降低了耦合度,增加新的功能,不需要修改目标对象。
代理模式的结构:
抽象主题:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题:实现了抽象主题中的具体业务,是代理对象 所代表的真实对象,是最终要引用的对象。
代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
代理可以分为静态代理和动态代理:
1.静态代理:
一个代理类可以对某一类的目标提供代理,满足开闭原则(添加一类目标时,可以扩展添加一个新的代理类)
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:一个代理类只能代理一个接口,工作量太大;代理类是运行前编码已经完成的;必须先有接口,再有代理;接口一旦发生变量,代理类也要修改。
2.动态代理:
在运行时可以动态的创建代理类,分为jdk、cglib
jdk代理:
创建一个代理对象的生成器,实现InvocationHandler,重写invoke方法,此方法会被代理动态调 用。
代理对象在运行时,被动态的创建,可以代理任意的目标对象,必须实现一个接口,在生成代理对象时,需要通过接口来获取目标对象的信息。
底层使用的是反射机制。
Java中提供了一个动态代理类 Proxy,Proxy并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法 (newProxyInstance方法)来获取代理对象。
Cglib代理:
实现时,不需要目标类去实现接口,采用动态字节码生成技术,为我们的目标类生成一个子类对象,当调用方法时,对方法进行拦截,调用目标类方法。
要求目标类不能是final修饰,方法也不能是final修饰的,和static修饰的。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
spring框架中两种代理生成机制都实现了;可以根据目标是否实现接口自动选择生成代理对象的方式,SpringAOP中用到两种动态代理方式,如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,如果目标对象没有实现接口,则会采用CGLIB动态代理。
JDK动态代理和CGLIB动态代理的区别?(面试高频)
- JDK动态代理是通过反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用nvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来通过修改其字节码生成子类来处理。
- JDK动态代理的核心是实现InvocationHandler接口,使用invoke()方法进行面向切面的处理,调用相应的通知。cglib动态代理核心是实现Methodlnterceptor接口,使用intercept()方法进行面向切面的处理,调用相应的通知。
- JDK动态代理只能代理实现实现接口的类,不能代理没有实现接口的类。CGLIB动态代理是针对类来实现代理的,对指定的目标类生成一个子类,并覆盖其中方法实现增强,不能对final修饰的类进行代理。
- 在JDK1.6之前,CGLIB动态代理的效率比IDK动态代理要高,JDK1.6开始对IDK动态代理优化后JDK动态代理的效率比CGLIB动态代理要高,但当大量调用的时候,JDK1.6和IDK1.7的效率比CGLIB动态代理效率低一些,到DK1.8,JDK动态代理的效率比CGLIB动态代理高。
2.适配器模式
结构:
三、行为型
1. 模板方法模式
在程序开发中,有些实现步骤流程是固定的,可以抽取父类,在父类中实现各个步骤,在父类中定义一个流程控制方法,在此方法中按照步骤调用执行。
可以将某个容易发生变化的步骤定义为抽象方法,针对不同的情况,可以扩展一个子类实现抽象的方法,最终是父类调用子类实现的方法。
2.策略模式
可以将同一种功能不同实现细节先抽取一个抽象接口/抽象类,
然后把不同的实现让子类继承实现,
最中选择时,只需要选择不同的子类实现,将不同实现封装不同的子类中。
使用场景:一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
例如:Arrays中的sort()方法
3.观察者模式
又称为“发布-订阅”模式,
例如“微信公众号”,微信用户就是观察者,微信公众号是被观察者,有多个微信用户关注了这个公众号。
使用场景:
对象间存在一对多关系,一个对象的状态发生改变会影响其他对象;当一个抽象模型有两个方面,其中一个方面依赖于另一方面时。