常用设计模式(超级无敌认真好用,万字收藏篇!!!!)
文章目录
- 常用设计模式
- 前言
- 1 单例模式(必会)
- 1.1 饿汉式
- 1.2 懒汉式
- 1.3 枚举
- 2 工厂模式(必会)
- 2.1 简单工厂模式
- 2.2 抽象工厂模式
- 3 代理模式(必会)
- 3.1 静态代理模式
- 3.2 动态代理模式(基于JDK实现)
- 3.3 CGLIB代理
- 4 适配器模式
- 5 策略模式
- 6 责任链模式
- 7 装饰者模式
- 8 观察者模式(必会)
常用设计模式
前言
什么是设计模式?
解决软件开发中某些特定问题的方案(思路)
设计模式可以帮助我们增强代码的可重用性,可扩充性、可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的高内聚、低耦合。
设计模式的贯穿思想:面向接口编程,最大限度的适用变化、实现代码复用。
针对不同类型的问题,总结出23种解决方案,这23种解决方案就是我们所说的23种设计模式。
23种设计默认可以归纳为3类:创建型(Creational)、结构型(Structural)和行为型(Behavioral)。
- 创建型模式:对象实例化模式,创建型模式用于对象实例化的解耦。
- 结构型模式:把类或对象结合在一起形成一个更大的结构。
- 行为型模式:类和对象如何交互,及划分责任和算法
设计模式7大原则
设计原则名称 | 简介 |
---|---|
单一职责原则 | 类的职责要单一,不能将太多职责放到一个类中 |
开闭原则 | 软件实体对扩展是开放的,对修改是关闭的,即在不修改软件实体的基础上去扩展其功能 |
里氏代换原则 | 在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象 |
依赖倒转原则 | 要针对抽象层编程,而不要针对具体类编程 高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。 |
接口隔离原则 | 使用多个专门的接口来取代一个统一的接口 |
合成复用原则 | 在系统中应尽量多使用组合聚合关联关系,尽量少使用甚至不使用集成关系 |
迪米特法则 | 一个软件实体类对其他类的引用尽量越少越好,或者说,如果两个类,不必彼此直接通信,那么这两个类就不应当发生直接相互作用,而是引入一个第三者进行交互 |
1 单例模式(必会)
一个类的对象在整个应用中只能实例化一次
类进行实例化,调用类的"构造器",所以单例模式中,"构造器"只能被调用一次
- 核心做法:构造器私有化,在类的内部进行实例化
1.1 饿汉式
所谓饿汉式(立即加载模式)就是在类加载时直接创建对象;
public class Singleton01 {
// private static final Singleton01 INSTANCE = new Singleton01();
private static final Singleton01 INSTANCE = InitInstance();
private static Singleton01 InitInstance() {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Singleton01 object = new Singleton01();
return object;
}
/**
* 构造器私有化
*/
private Singleton01(){
}
public static Singleton01 getInstance() {
return INSTANCE;
}
}
1.2 懒汉式
所谓懒汉式(懒加载模式),在需要使用该对象时,创建该对象
- 懒汉式普通写法
/**
* 懒汉式
*/
public class Singleton02 {
private static Singleton02 instance = null;
private Singleton02() {}
public synchronized static Singleton02 getInstance(){
if (instance == null){
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
instance =new Singleton02();
}
return instance;
}
}
- 懒汉式写法2(推荐使用该写法)
/**
* 不用同步锁关键字的懒汉式
*/
public class Singleton03 {
private Singleton03() {}
private static class Inner{
static final Singleton03 instance = new Singleton03();
}
public static Singleton03 getInstance(){
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return Inner.instance;
}
}
- 使用枚举实现单例(反射机制不可获取枚举实例)
/**
* 使用枚举实现单例
*/
public enum Singleton04 {
INSTANCE,
}
1.3 枚举
枚举是一个类,枚举声明时使用 enum 声明
Java 中的枚举都继承 Enum 类
由于枚举都继承自 Enum 类,所有枚举类型的类无法继承其他,但可以实现接口
-
枚举中须有枚举项,枚举项是一个常量,每个枚举项相当于当前枚举的一个实例,枚举项数量必须确定
-
枚举中可以有成员变量,方法,及构造器,但是构造器必须是"私有的"。
- 在枚举中存在默认的无参构造,这个默认的无参的构造方法就是私有的。
- 枚举中虽然存在私有的构造方法,但在枚举内部也不允许使用 new 关键字创建枚举对象
public enum EnumDemo {
INSTANCE,INSTANCE1("实例2");
//成员变量
private String name ;
//带参构造无参数
private EnumDemo(){
}
//带参构造有参数
private EnumDemo(String name){
this.name=name;
}
//内部类
class EnumInner{
EnumDemo enumDemo =EnumDemo.INSTANCE;
String str = enumDemo.name;
}
}
2 工厂模式(必会)
工厂模式分为简单工厂模式和抽象工厂模式,它们都属于设计模式中的创建型模式。
其主要功能都是帮助我们把对象的实例化部分抽取了出来,目的是降低系统中代码耦合度,并且增强了系统的扩展性。
-
在举例下面两个工厂模式我们先创建几个要生产模型
- 抽象产品
/** * 抽象产品 */ public interface Phone { public void show(); }
- 华为手机
/** * 具体产品-华为手机 */ public class HUAWEIPhone implements Phone{ @Override public void show() { System.out.println("华为手机加工成功..........."); } }
- 苹果手机
/** * 具体产品-苹果手机 */ public class IPhone implements Phone{ @Override public void show() { System.out.println("苹果手机加工成功..........."); } }
- 小米手机
public class XIAOMIPhone implements Phone{ @Override public void show() { System.out.println("小米手机加工成功..........."); } }
2.1 简单工厂模式
简单工厂模式最大的优点是实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品时需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码逻辑将会非常复杂
- 创建工厂类
/**
* 简单手机工厂
*/
public class PhoneFactory {
/**
* 造手机
* @param phoneType
* @return
* @throws Exception
*/
public Phone makePhone(PhoneType phoneType) throws Exception{
switch (phoneType){
case HUAWEI:
return new HUAWEIPhone();
case IPHONE:
return new IPhone();
case XIAOMI:
return new XIAOMIPhone();
default:
throw new Exception("不支持手机异常");
}
}
}
- 手机类型枚举
/**
* 手机类型枚举
*/
public enum PhoneType {
IPHONE,
HUAWEI,
XIAOMI;
}
- 具体造手机
public class Test {
public static void main(String[] args) throws Exception {
//创建工厂
PhoneFactory phoneFactory =new PhoneFactory();
//制造手机
Phone phone = phoneFactory.makePhone(PhoneType.IPHONE);
phone.show();
}
}
PS:简单工厂虽然实现了对象的创建和使用的分离,但是要想新增产品,需要牵一发而动全身,不符合设计模式的开闭原则。
2.2 抽象工厂模式
抽象工厂好处在于,当你需要新增产品时,可直接新增一个对应工厂子类,无需修改现有代码
- 总工厂
//总工厂
public interface AbstractPhoneFactory {
Phone makePhone();
}
- 华为工厂
public class HUAWEIPhoneFactory implements AbstractPhoneFactory{
@Override
public Phone makePhone() {
return new HUAWEIPhone();
}
}
- 小米工厂
public class XIAOMIPhoneFactory implements AbstractPhoneFactory{
@Override
public Phone makePhone() {
return new XIAOMIPhone();
}
}
- 苹果工厂
public class IPhoneFactory implements AbstractPhoneFactory{
@Override
public Phone makePhone() {
return new IPhone();
}
}
- 具体造手机,各个工厂造各个工厂的手机
public class Test {
public static void main(String[] args) {
//创建华为工厂
HUAWEIPhoneFactory huaweiPhoneFactory =new HUAWEIPhoneFactory();
//华为工厂造手机
huaweiPhoneFactory.makePhone().show();
//创建苹果工厂
IPhoneFactory iPhoneFactory =new IPhoneFactory();
//苹果工厂造手机
iPhoneFactory.makePhone().show();
}
}
3 代理模式(必会)
代理模式是结构型设计模式
- 代理模式是,指给某一个对象(目标对象)提供一个代理对象,并由代理对象控制对原对象的引用。
- 代理对象和目标对象必须有相同业务
3.1 静态代理模式
静态代理模式,在代理对象中调用目标对象的操作方法,并在目标方法调用前或调用 后执行相应的功能
- 具体业务类
/**
* 业务类
*/
public interface Treat {
public void treat();
}
- 目标对象类
/**
* 目标对象
*/
public class Boss implements Treat{
@Override
public void treat() {
System.out.println("我付钱,你们敞开吃");
}
}
- 代理对象
/**
* 代理对象
*/
public class Assistant implements Treat{
Boss boss;
public void setBoss(Boss boss) {
this.boss = boss;
}
@Override
public void treat() {
System.out.println("大家吃老板请客");
boss.treat();
System.out.println("吃完了吧,大家一路平安");
}
}
- 测试
public class Test {
public static void main(String[] args) {
//实例化代理对象
Assistant assistant =new Assistant();
//实例化目标对象
Boss boss =new Boss();
//目标对象注入
assistant.setBoss(boss);
//调用代理对象方法
assistant.treat();
}
}
3.2 动态代理模式(基于JDK实现)
动态代理,我们不需要创建代理对象,只需编写一个动态处理器即可,真正的代理由JVM在运行时为我们动态创建
-
代理对象,在程序运行时,通过反射动态创建
-
JDK实现代理,只支持接口代理,不支持类代理
-
动态代理处理器
/** * 自定义动态处理器 * invoke()调用代理对象的代理方法时,代理方法会委托给invoke方法执行 */ public class DynamicProxyHandler implements InvocationHandler { private Object targetObj; public DynamicProxyHandler(Object targetObj) { this.targetObj = targetObj; } /** * 代理对象调用目标对象方法 * @param 代理对象 * @param 代理对象调用的方法 * @param 代理对象调用目标方法参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("大家吃老板请客"); Object invoke = method.invoke(targetObj, args); System.out.println("吃完了吧,大家一路平安"); return invoke; } }
-
测试
public class Test { public static void main(String[] args) { Boss boss =new Boss(); Treat treat=(Treat) Proxy.newProxyInstance( Test.class.getClassLoader(), new Class[]{Treat.class}, new DynamicProxyHandler(boss) ); treat.treat(); } }
3.3 CGLIB代理
CGLIB 代理也是一种动态代理,该代理支持使用类的子类做为代理对象。
-
CGLIB 采用了非常底层的字节码技术
其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用, 顺势织入横切逻辑。
但因为采用的是继承,所以不能对 final 修饰的类进行代理。JDK 动态代理与 CGLIB 动态代理均是实现 Spring AOP 的基础。 Enhancer 是一个非常重要的类,它允许为非接口类型创建一个 JAVA 代 理,Enhancer 动态的创建给定类的子类并且拦截代理类的所有的方法,和 JDK 动态代理不一样的是不管是接口还是类它都能正常工作。
-
导入cglib依赖包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
- cglib动态代理类
package com.jiazhong.设计模式.代理模式.动态代理_基于CGLIB;
import com.jiazhong.设计模式.代理模式.Boss;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cglib动态代理类,该类继承MethodInterceptor接口
* MethodInterceptor是方法拦截器接口
*/
public class CGlibProxy implements MethodInterceptor {
private Object target;
/**
* 获得代理对象的实例
* @return
*/
public Object getProxyInstance(Object target){
this.target = target;
/**
* 通过Enhancer对象来创建目标对象
*/
Enhancer enhancer =new Enhancer();
//设置代理对象的父类
enhancer.setSuperclass(target.getClass());
//设置拦截目标对象目标方法的拦截器
enhancer.setCallback(this);
//创建并返回代理对象
return enhancer.create();
}
/**
* 当拦截到目标方法被调用自动执行该方法
* @param proxy //代理对象
* @param method //目标方法
* @param args //方法参数
* @param methodProxy //代理对象方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("大家吃老板请客");
//调用目标对象中的目标方法
Object obj = method.invoke(target, args);
// Object obj = methodProxy.invokeSuper(proxy,args);//使用代理对象访问目标对象
System.out.println("吃完了吧,大家一路平安");
return obj;
}
}
- 测试类
public class Test {
public static void main(String[] args) {
Boss boss =new Boss();
Boss boss1=(Boss) new CGlibProxy().getProxyInstance(boss);
boss1.treat();
}
}
4 适配器模式
通过适配器我们可以解决接口不兼容问题,使原来没有任何关系的类来协同工作
-
下面案例中手机充电线就是我们的适配器,他把通过转换电压把家用电压和手机电压进行适配。
-
家用电压输出
/** * 电源 */ public class Power { public static final Integer VOLTAGE =220 ; public Integer outPut(){ System.out.println("输出电压为:220V"); return VOLTAGE; } }
-
手机充电头
/** * 手机接口 */ public interface PhoneCharge { //当前手机所需电压 Integer Phone_VOLTAGE = 5 ; public Integer output(); }
-
手机需输入电压
public class Phone { public void charge(PhoneCharge phoneCharge){ if (phoneCharge.output()==5){ System.out.println("手机开始充电"); }else { new Exception("手机充电器不匹配,请更换充电器"); } } }
-
充电线,也就是适配器
public class PowerAdapter implements PhoneCharge { Power power = new Power(); public PowerAdapter(Power power) { this.power = power; } @Override public Integer output() { int voltage = power.outPut(); int outputVoltage = voltage/(voltage/Phone_VOLTAGE); System.out.println("输出电压为:"+outputVoltage+"V"); return outputVoltage; } }
-
测试
public class Test { public static void main(String[] args) { //家用电源接口 Power power =new Power(); //手机电源接口 Phone phone =new Phone(); //连接充电线 PowerAdapter powerAdapter =new PowerAdapter(power); //手机进行充电 phone.charge(powerAdapter); } }
5 策略模式
策略:解决问题的方法
策略模式,就是提出一个问题,解决问题的方法,解决问题的方法有多种
例如:坐交通工具去海南
去海南就是一个问题
交通工具就是一个策略
具体策略就是(坐飞机,坐火车…)
-
问题
public class Travel { TravelTools travelTools; Travel(TravelTools travelTools) { this.travelTools = travelTools; } public void travel(){ System.out.println("一起去海南吧...."); //怎么去 travelTools.travelTools(); System.out.println("到海南要好好玩玩"); } }
-
抽象策略类
抽象策略,一般为借口,可为抽象类,根据具体业务而定
/** * 抽象策略,可为抽象类,根据具体业务而定 */ public interface TravelTools { void travelTools(); }
-
具体策略-火车
/** * 具体策略 */ public class TrainTools implements TravelTools{ @Override public void travelTools() { System.out.println("哦,上周花了好多钱,我们做火车去吧"); } }
-
具体策略-飞机
/** * 具体策略类 */ public class PlaneTools implements TravelTools{ @Override public void travelTools() { System.out.println("我们有钱,咋坐飞机去吧..."); } }
-
测试类
public class Test { public static void main(String[] args) { //坐飞机去 PlaneTools planeTools =new PlaneTools(); //坐火车去 TrainTools trainTools =new TrainTools(); Travel travel =new Travel(planeTools); travel.travel(); } }
6 责任链模式
**责任链模式:**顾名思义,就是用来处理相关事务责任的一条执行链,执行链上 有多个节点,每个节点都有机会处理请求事务,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。
员工请假审批流程:
请假时间 1 天以内,项目组长审批即可
请假时间大于 1 天小于等于 3 天,则需要项目经理审批
请假时间大于 3 天,则需要总经理审批
-
抽象处理类
/** * 抽象处理类 */ public abstract class AbstractHandler { //定义下个节点 private AbstractHandler nextHandler; public AbstractHandler getNextHandler() { return nextHandler; } public AbstractHandler() { } public AbstractHandler(AbstractHandler nextHandler) { this.nextHandler = nextHandler; } //事件 public abstract void handle(int days); }
-
小组组长
public class TeamHandler extends AbstractHandler{ public TeamHandler(AbstractHandler nextHandler) { super(nextHandler); } @Override public void handle(int days) { System.out.println("项目组长通过审批..."); if (days >1){ super.getNextHandler().handle(days); } } }
-
项目经理
public class PMHandler extends AbstractHandler { public PMHandler(AbstractHandler nextHandler) { super(nextHandler); } @Override public void handle(int days) { System.out.println("项目经理通过审批...."); if (days > 3) { super.getNextHandler().handle(days); } } }
-
总经理
public class GMHandler extends AbstractHandler { public GMHandler() { } @Override public void handle(int days) { System.out.println("项目经理通过审批..."); } }
-
测试
public class Test { public static void main(String[] args) { AbstractHandler handler =new TeamHandler(new PMHandler(new GMHandler())); handler.handle(1); } }
7 装饰者模式
装饰者模式:动态将责任(功能)附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性替性的方案。
- 装饰者和被装饰者有相同的超类
- 你可以用一个或多个装饰者包装一个对象(被装饰者对象)。
- 既然装饰者和被装饰对象有相同的超类,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。
- 装饰者可以在所委托被装饰者的行为之前与 / 或之后,加上自己的行为,以达到特定的目的。
- 在装饰者的行为(方法中)发起对被装饰者对象的行为(方法)的调用。
- 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象
如下案例
定义一个人类
学生继承人类,学生在学习
程序员继承人类,程序员在敲代码
学生和程序员不单单会学习和敲代码他们其中有些人可能还具有其他能力,
如唱歌、跳舞等,我们通过装饰模式的来为不同的人群添加新的能力
-
定义被装饰者-抽象类-人
public abstract class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract void action(); }
-
定义具体的人
- 学生
/** * 学生 */ public class Student extends Person{ @Override public void action() { System.out.println("学生在认真学习..."); } }
- 程序员
/** * 程序员-具体实现类 */ public class Programmer extends Person { @Override public void action() { System.out.println("程序员在疯狂敲代码..."); } }
-
定义装饰者抽象类
/** * 抽象-装饰类 */ public abstract class Decorator extends Person{ public Decorator() { } protected Person person; /** * 装饰器 * @param person:装饰对象 */ public Decorator(Person person){ this.person =person; } }
-
定义具体装饰类
- 唱歌
/** * 具体装饰器-唱歌 */ public class Sing extends Decorator{ public Sing(Person person){ super(person); } public Sing() { } @Override public void action() { System.out.println("喜欢唱歌"); person.action(); } }
- 跳舞
/** * 具体装饰类-跳舞 */ public class Dance extends Decorator{ public Dance(Person person) { super(person); } public Dance() { } @Override public void action() { System.out.println("喜欢跳舞"); } }
-
测试
public class Test { public static void main(String[] args) { Student student =new Student(); Person person =new Dance(new Sing(student)); student.setName("小王"); person.action(); } }
8 观察者模式(必会)
观察者模式:指多个对象之间存在一对多的依赖关系,**当一个对象(服务器|主题)的状态发生改变时, 所有依赖它的对象(**客户端|观察者)都能自动收到通知并根据通知来决定自己行为。这种模式有时又被称 为”发布-订阅模式”、“模型-视图模式”。
- 观察者模式属于行为型模式。观察者模式符合依赖倒置原则
观察者模式中的角色:
-
抽象主题(抖音平台):
他是观察者关注的事情,主题中会定义”注册观察者”、”解除观察者”、”通知观察者”的相关操作,一般使用接口表示
-
具体主题(关注列表):
具体主题实现抽象主题,并指定具体的关注事件,并设置一个用于存储观察者集合
-
观察者抽象(抖音全体用户):
定义观察者收到通知后要做的事情
-
具体观察者(具体用户):
具体观察者实现抽象观察者接口,对接收到通知后要做的事情进行具体实现。
-
抽象主题:
里面有关注,取关,通知等相关操作
/** * 抽象主题:类似抖音平台-有关注,取关,通知等功能 */ public interface SubObject { //注册 public void registerObServer(ObServer obServer); //取关 public void removeObServer(ObServer obServer); //通知 public void notifyObServer(); }
-
观察者抽象
不同观察者有不同行为
/** * 观察者抽象:类似抖音所有用户 */ public interface ObServer{ //行为 public void action(String msg); }
-
具体主题:
具体主题:也就是关注的主播,需要有一个关注列表
/** * 具体主题:类似是关注的主播,需要有一个关注列表 */ public class Singer implements SubObject { String msg;//通知信息 private List<ObServer> obServers = new ArrayList<>(); //关注该主播 @Override public void registerObServer(ObServer obServer) { if (obServer != null) { obServers.add(obServer); } } //取关该主播 @Override public void removeObServer(ObServer obServer) { if (obServers.contains(obServer)) { obServers.remove(obServer); } } /** * 发布信息 * @param msg */ public void setMsg(String msg) { this.msg = msg; //向观察者(抖音用户)发布信息 if (!this.msg.equals("")){ notifyObServer(); } } //有主播有新信息时进行通知 @Override public void notifyObServer() { for (ObServer obServer : obServers){ obServer.action(msg); } } }
-
具体观察者
当主播发出新通知,用户会进行某些行为
/** * 具体观察者-具体用户 * 主播发出新通知,用户会进行某些行为 */ public class AUser implements ObServer { private String username; //当前用户关注的主播 private SubObject subObject; public AUser(String username, SubObject subObject) { this.username = username; this.subObject = subObject; } @Override public void action(String msg) { System.out.println(username+":主播发送新动态了!!!快去看看"); if (msg!=null&&!msg.equals("")){ System.out.println(msg); } } }
-
测试
public class Test { public static void main(String[] args) { Singer singer =new Singer(); AUser xiaoming =new AUser("小明",singer); xiaoming.action("关注他"); AUser xiaohua =new AUser("小花",singer); xiaoming.action("点赞"); //用户关注主题 singer.registerObServer(xiaoming); singer.registerObServer(xiaohua); } }
- 学习来自于西安加中实训