概述
设计模式就是经过我们开发人员通过长时间的开发实践得出的一种开发模式,目的就是在开发过程中降低代码耦合度,提高代码可复用性/扩展/维护 。目前设计模式可以分为创建型模式、行为型模式、结构型模式,一共包括23种设计模式。本文列举了实际项目中使用到的设计模式,包括单例模式、策略模式、代理模式
创建型模式
单例模式
- 目的就是解决一个类Class在应用程序中只有一个实例存在,并提供对外的全局访问
- 构造函数私有化、对外提供获取实例静态方法
- 项目实践
- 在多线程环境中缓存文件操作记录到队列中,后续通过定时任务添加到数据库
java
public class LazyQueueSingleton {
private static int QUEUE_MAX_SIZE = 80000;
// LinkedBlockingQueue线程安全的共享队列,并设置队列初始大小避免内存溢出
private volatile Queue<HistoryRecords> ShareQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);
private static final Object lockedObj = new Object();
//懒汉式队列单例,保证实例全局唯一
private static volatile LazyQueueSingleton INSTANCE;
private LazyQueueSingleton() {
if(ShareQueue == null) {
ShareQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);
}
}
public static LazyQueueSingleton getInstance() {
if(INSTANCE == null) {
synchronized (lockedObj) {
if(INSTANCE == null) {
INSTANCE = new LazyQueueSingleton();
}
}
}
return INSTANCE;
}
public Queue<HistoryRecords> getPendingTodoQueue() {
return ShareQueue;
}
}
行为型模式
策略(Strategy)模式
-
定义了一组算法,将每一个算法封装到具有共同接口的独立的类中,使得它们可以相互替换。常常适用于业务存在很多复杂分支场景
-
上下文/Context(持有策略对象的引用,通过该引用来调用算法或行为)、策略/Strategy(定义所有支持的算法的公共接口)、具体策略/Concrete Strategy(实现策略接口的具体算法或行为)
-
项目实践
- 系统需要根据不同部门来计算报表生成的内容
java// 1.定义策略接口 public interface GernerateReportStrategy { boolean calculateReport(int departmentId); } // 2.实现具体策略 public class BDGernerateReportStrategy implements GernerateReportStrategy { @Override public boolean calculateReport(int departmentId) { ...... } } public class SwGernerateReportStrategy implements GernerateReportStrategy { @Override public boolean calculateReport(int departmentId) { ...... } } // 3.创建上下文 public class DownloadReportContext { private GernerateReportStrategy gernerateReportStrategy; public void setGernerateReportStrategy(GernerateReportStrategy gernerateReportStrategy) { this.gernerateReportStrategy = gernerateReportStrategy; } ...... } // 4.接下来就可以使用了,若后续有新增部门则直接实现具体策略类即可
结构型模式
代理模式
- 核心思想:为其他对象提供一种代理以控制对这个对象的访问
- 角色:代理角色、真实/原始/目标角色
- 作用:一是保护真实/原始/目标对象,二是增强真实/原始/目标对象
静态代理
-
静态代理是一种在编译时就已经确定代理类和目标对象关系的代理模式。它通常通过创建一个接口和两个实现这个接口的类(一个为目标对象类,另一个为代理类)来实现
-
代码实践
*java// 1.定义一个接口,该接口包含一个或多个方法 interface Service { void execute(); } // 2.创建一个目标对象类,它实现这个接口 class RealService implements Service { public void execute() { System.out.println("Executing real service."); } } // 3.创建一个代理类,也实现这个接口,并包含对目标对象的引用。也可以在调用目标对象的方法之前或之后添加额外的操作 class ProxyService implements Service { private RealService realService; public ProxyService(RealService realService) { this.realService = realService; } public void execute() { System.out.println("Before execution."); realService.execute(); System.out.println("After execution."); } } // 4.客户端使用 Service service = new ProxyService(new RealService()); service.execute();
动态代理
- 动态代理是一种在运行时动态创建代理对象的代理模式 。不需要事先编写代理类代码,而是在运行时根据目标对象动态生成代理类
- JDK动态代理 VS Cglib动态代理
- 代理的目标对象要求
- JDK要求目标类必须实现接口,而Cglib提供更大的灵活性,目标类实现接口或者只是普通类都可以(基于此SpringBoot2.x以上默认使用CGLIB代理)
- 代理对象的创建方式
- JDK通过Proxy类和实现InvocationHandler接口来创建代理对象; Cglib通过Enhancer类直接创建代理对象
- 执行性能
- JDK动态通过反射实现,Cglib则使用字节码生成技术直接操作字节码,因此在一些场景下,Cglib更加高效
- 通常情况下,我们开发人员不需要在配置文件中明确指定AOP使用的代理方式,因为Spring会自动根据目标对象的类型选择代理方式
- 代理的目标对象要求
- JDK动态代理 VS Cglib动态代理
- 项目实践
- JDK动态代理代码实践
java
// 1. 定义接口
public interface Report {
void generateReportByDepartmentId(int departmentId);
void generateReportByDepartmentName(String departmentName);
}
// 2. 定义目标/原始/真实对象
public class generateReport implements Report {
@Override
public void generateReportByDepartmentId(int departmentId) {
System.out.println("按部门ID已生成报表:" + departmentId);
}
@Override
public void generateReportByDepartmentName(String departmentName) {
System.out.println("按部门名称已生成报表:" + departmentName);
}
}
// 3. 定义代理对象
public class ReportJDKProxy implements InvocationHandler {
//需要被代理的对象
private Object object;
public ReportJDKProxy(Object object) {
this.object = object;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(){
// 使用反射API动态创建代理类
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),//当前线程的上下文ClassLoader
object.getClass().getInterfaces(), //代理需要实现的接口
this); // 处理器自身
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
//进行方法匹配,调用对应方法名的方法
if ("generateReportByDepartmentId".equals(method.getName())) {
System.out.println("按部门ID已生成报表------前置增强------");
result=method.invoke(object, args);
System.out.println("按部门ID已生成报表------后置增强------");
}
if ("generateReportByDepartmentName".equals(method.getName())) {
System.out.println("按部门名称已生成报表------前置增强------");
result=method.invoke(object, args);
System.out.println("按部门名称已生成报表------后置增强------");
}
return result;
}
}
// 4. 客户端使用
// 调用方式一
Report player=new generateReport();
Report proxy=new ReportJDKProxy(player).getProxy();
proxy.generateReportByDepartmentId(666);
proxy.generateReportByDepartmentName("SW");
/*
* // 调用方式二
* Report p=new generateReport();
* Report o = (Report)Proxy.newProxyInstance( p.getClass().getClassLoader(),p.getClass().getInterfaces(), new ReportJDKProxy(p) );
* o.generateReportByDepartmentId(666);
* o.generateReportByDepartmentName("SW");
*/
-
Cglib动态代理代码实践
*java// 1. 创建目标对象/接口实现类 class ToReport{ public void generateReport() { System.out.println("生成报表---CglibProxy"); } } // 2. 创建代理对象 class CglibProxy implements MethodInterceptor { /** * @param o: 代理对象 * @param method: 被代理方法 * @param params: 方法入参 * @param methodProxy: CGLIB方法 **/ @Override public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable { System.out.println("【Cglib前置方法】代理对象正在执行的方法:" + method.getName()); Object result = methodProxy.invokeSuper(o, params); System.out.println("【Cglib后置方法】代理对象正在执行的方法:" + method.getName()); return result; } } // 3. 创建Enhancer(设置要被代理的类和调用方法时触发的拦截器) class CglibProxyFactory{ public static Object creatCglibProxyObj(Class<?> clazz) { Enhancer enhancer = new Enhancer(); // 为加强器指定要代理的业务类(即为下面生成的代理类指定父类) enhancer.setSuperclass(clazz); // 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法 enhancer.setCallback(new CglibProxy()); return enhancer.create(); } } // 4. 客户端使用 public static void main(String[] args) { ToReport report = (ToReport)CglibProxyFactory.creatCglibProxyObj(ToReport.class); report.generateReport(); }