# docsify **Repository Path**: gui-qiang-zhang/docsify ## Basic Information - **Project Name**: docsify - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-12-14 - **Last Updated**: 2023-12-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Java设计模式 00:模拟场景 01:优化前的代码 02:使用设计模式的代码 ## 行为型设计模式 ### 策略模式 ​ 定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。 ​ 需要3种类:**策略接口**、**策略接口实现类**、**执行策略类(Context类)** ```java // 定义策略接口 public interface Strategy { public int doOperation(int num1, int num2); } ``` ```java // 实现具体策略类 public class OperationAdd implements Strategy { @Override public int doOperation(int num1, int num2) { return num1 + num2; } } public class OperationSubtract implements Strategy { @Override public int doOperation(int num1, int num2) { return num1 - num2; } } public class OperationMultiply implements Strategy { @Override public int doOperation(int num1, int num2) { return num1 * num2; } } ``` ```java // Context类,用于执行策略 public class Context { //****将策略接口作为Context类的属性,并添加构造方法。最后用一个方法来封装接口的抽象方法(实现复用)***** private Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } public int executeStrategy(int num1, int num2) { return strategy.doOperation(num1, num2); } } ``` 调用方只需要创建Context类,并传入一个接口的实现类作为构造参数。 ### 责任链模式(没搞懂) ### 访问者模式 在一个稳定的数据结构下,例如用户信息、雇员信息等,增加易变的业务访问逻辑。 在一个抽象方法中,调用另一个接口的抽象方法(把接口作为参数) ```java @Test public void test(){ DataView dataView = new DataView(); logger.info("\r\n家长视角访问:"); dataView.show(new Parent()); logger.info("\r\n校长视角访问:"); dataView.show(new Principal()); } ``` ```java public class DataView { List userList = new ArrayList(); public DataView() { userList.add(new Student("谢飞机", "重点班", "一年一班")); userList.add(new Student("windy", "重点班", "一年一班")); userList.add(new Teacher("BK", "特级教师", "一年一班")); userList.add(new Teacher("娜娜Goddess", "特级教师", "一年一班")); userList.add(new Teacher("dangdang", "普通教师", "二年三班")); userList.add(new Teacher("泽东", "实习教师", "三年四班")); } /** * 展示 * @param visitor 访问者 */ public void show(Visitor visitor) { for (User user : userList) { user.accept(visitor); } } } ``` ```java //抽象类 public abstract class User { /**姓名*/ public String name; /** 身份;重点班、普通班 | 特级教师、普通教师、实习教师*/ public String identity; /**班级*/ public String clazz; public User(String name, String identity, String clazz) { this.name = name; this.identity = identity; this.clazz = clazz; } /** * 核心访问方法:形参是访问者的接口 * @param visitor 访问者 */ public abstract void accept(Visitor visitor); } ``` ```java //继承User抽象类,重写accept方法,并且可以添加自己的方法。 public class Student extends User { public Student(String name, String identity, String clazz) { super(name, identity, clazz); } @Override public void accept(Visitor visitor) { visitor.visit(this); } public int ranking() { return (int) (Math.random() * 100); } } ``` ```java //继承User抽象类,重写accept方法,并且可以添加自己的方法。 public class Teacher extends User { public Teacher(String name, String identity, String clazz) { super(name, identity, clazz); } @Override public void accept(Visitor visitor) { visitor.visit(this); } /** * 获取升本率 * @return 升本率 */ public double entranceRatio() { return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); } } ``` ```java //接口:抽象了访问者 public interface Visitor { /** * 访问学生信息 * @param student 被访问的学生 */ void visit(Student student); /** * 访问老师信息 * @param teacher 被访问的老师 */ void visit(Teacher teacher); } ``` ```java //Visitor接口的实现类(访问者):家长 public class Parent implements Visitor { private Logger logger = LoggerFactory.getLogger(Parent.class); @Override public void visit(Student student) { logger.info("学生信息 姓名:{} 班级:{} 排名:{}", student.name, student.clazz, student.ranking()); } @Override public void visit(Teacher teacher) { logger.info("老师信息 姓名:{} 班级:{} 级别:{}", teacher.name, teacher.clazz, teacher.identity); } } ``` ```java //Visitor接口的实现类(访问者):校长 public class Principal implements Visitor { private Logger logger = LoggerFactory.getLogger(Principal.class); @Override public void visit(Student student) { //不同的实现类有不同的业务逻辑 logger.info("学生信息 姓名:{} 班级:{}", student.name, student.clazz); } @Override public void visit(Teacher teacher) { //不同的实现类有不同的业务逻辑 logger.info("学生信息 姓名:{} 班级:{} 升学率:{}", teacher.name, teacher.clazz, teacher.entranceRatio()); } } ``` 在嵌入访问者模式后,可以让整个工程结构变得容易添加和修改。也就做到了系统服务之间的解耦,不至于为了不同类型信息的访问而增加很多多余的`if`判断或者类的强制转换。 定义抽象类的时候还需要等待访问者接口的定义,这样的设计首先从实现上会让代码的组织变得有些难度。另外从设计模式原则的角度来看,违背了迪米特原则,也就是最少知道原则。 ## 结构型设计模式 ### 代理模式 对于Mybatis的使用中只需要定义接口不需要写实现类就可以完成增删改查操作。 代理模式在开发中间件十分常见。 1. BeanDefinitionRegistryPostProcessor: ​ spring的接口类用于处理对bean的定义注册。 2. GenericBeanDefinition: ​ 定义bean的信息,在mybatis-spring中使用到的是;ScannedGenericBeanDefinition 略有不同。 3. FactoryBean: ​ 用于处理bean工厂的类,这个类非常见 代理模式是一种结构型设计模式,其主要目标是**为其他对象提供一种代理或占位符**,以控制对该对象的访问。代理模式的设计方式确实可以让代码更加整洁、干净和易于维护。以下是几个体现代码更加简洁和易于维护的方面: 1. **解耦**:代理模式可以帮助实现客户端和目标对象之间的解耦。客户端**只需要与代理对象交互**,而不需要了解目标对象的实现细节。这样,如果目标对象的实现发生更改,**只要代理的接口保持不变,客户端代码就不需要修改**。 2. **额外功能添加**:通过代理,我们可以在不修改原始类的情况下添加额外的功能,如**日志记录**、**权限检查**、**缓存**等。这些功能可以在代理中实现,从而保持原始类的简洁和职责单一。 3. **隐藏复杂性**:如果目标对象具有复杂的接口或实现,代理可以**提供一个更简洁或更高级的接口**给客户端使用,从而隐藏底层的复杂性。 4. **控制访问**:代理可以控制对目标对象的访问,例如,可以按照特定的条件决定是否允许访问目标对象,或者在访问前后添加额外的操作。 ![image-20231207150640038](README.assets/image-20231207150640038.png) ![image-20231207150843090](README.assets/image-20231207150843090.png) ![image-20231207151448588](README.assets/image-20231207151448588.png) ### 适配器模式 主要作用就是把原本不兼容的接口,通过适配修改做到统一。 在业务开发中我们会经常的需要做不同接口的兼容,尤其是中台服务,中台需要把各个业务线的各种类型服务做统一包装,再对外提供接口进行使用。而这在我们平常的开发中也是非常常见的。 在配置不同的消费类(业务处理类)时,如果不希望一个个开发类,那么可以使用代理类的方式进行处理。 我目前已知的适配器:接口适配器、类型适配器。 1. **接口适配器** 需要3种类:接口、接口实现类、业务处理类。 其中**接口**负责统一适配器;**接口实现类**负责调用实际的业务处理类,只充当适配器。 ```java //测试类 public void test_itfAdapter() { OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl(); System.out.println("判断首单,接口适配(POP):" + popOrderAdapterService.isFirst("100001")); OrderAdapterService insideOrderService = new InsideOrderServiceImpl(); System.out.println("判断首单,接口适配(自营):" + insideOrderService.isFirst("100001")); } ``` ```java //接口 public interface OrderAdapterService { /** * 判断是否为首单用户 * @param uId 用户id * @return */ boolean isFirst(String uId); } ``` ```java //接口实现类(调用实际的业务处理类) public class InsideOrderServiceImpl implements OrderAdapterService { private OrderService orderService = new OrderService(); @Override public boolean isFirst(String uId) { return orderService.queryUserOrderCount(uId) <= 1; } } ``` ```java //实际的业务处理类 public class OrderService { /** * 查询用户内部下单数量接口 * @param userId 用户id * @return 用户内部下单数量 */ public long queryUserOrderCount(String userId){ return 10L; } } ``` 2. **类型适配器** ```java //测试类 public void test_MQAdapter(){ SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date parse = s.parse("2020-06-01 23:20:16"); //适配前的数据 OrderMq orderMq = new OrderMq(); orderMq.setUid("100001"); orderMq.setSku("10928092093111123"); orderMq.setOrderId("100000890193847111"); orderMq.setCreateOrderTime(parse); //映射规则 HashMap link02 = new HashMap(); link02.put("userId", "uid"); link02.put("bizId", "orderId"); link02.put("bizTime", "createOrderTime"); //适配后的数据 RebateInfo rebateInfo02 = MQAdapter.filter(orderMq.toString(), link02); System.out.println("mq.orderMq(适配前)" + orderMq.toString()); System.out.println("mq.orderMq(适配后)" + JSON.toJSONString(rebateInfo02)); } ``` ```java //类型适配器 public class MQAdapter { /** * 通用的MQ消息体类型转换 * @param strJson MQ消息体 * @param link 适配映射规则Map * @return 通用的数据类型 * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ public static RebateInfo filter(String strJson, Map link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { return filter(JSON.parseObject(strJson, Map.class), link); } public static RebateInfo filter(Map obj, Map link) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { RebateInfo rebateInfo = new RebateInfo(); for (String key : link.keySet()) { Object val = obj.get(link.get(key)); RebateInfo.class.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1), String.class) .invoke(rebateInfo, val.toString()); } return rebateInfo; } } ``` 总的来说,适配器就相当于一个“转接头”,把一头暴露出来,另一头**去调用另一个接口的方法**或者**处理其他规则**。这样就实现两端的兼容。 ## 创建型设计模式 ### 工厂模式 适用场景:需要根据多个if判断调用不同的service时。 需要3种类:**接口**、**接口的实现类**、**工厂类**。 其中工厂类的方法的返回类型是接口。 使用说明:创建工厂类,由工厂类的传参,再if判断调用不同的service 这样调用时只需要关注传参。扩展时只需要创建新的实现类,以及工厂类新增判断。 ```java //工厂类 public class StoreFactory { public ICommodity getCommodityService(Integer commodityType) { if (null == commodityType) return null; if (1 == commodityType) return new CouponCommodityService(); if (2 == commodityType) return new GoodsCommodityService(); if (3 == commodityType) return new CardCommodityService(); throw new RuntimeException("不存在的商品服务类型"); } } ``` ```java //接口 public interface ICommodity { void sendCommodity(String uId, String commodityId, String bizId, Map extMap) throws Exception; } ``` ```java //接口实现类 public class CardCommodityService implements ICommodity { private Logger logger = LoggerFactory.getLogger(CardCommodityService.class); // 模拟注入 private IQiYiCardService iQiYiCardService = new IQiYiCardService(); public void sendCommodity(String uId, String commodityId, String bizId, Map extMap) throws Exception { String mobile = queryUserMobile(uId); iQiYiCardService.grantToken(mobile, bizId); logger.info("请求参数[爱奇艺兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap)); } } ``` ```java //测试 public void test_commodity() throws Exception { StoreFactory storeFactory = new StoreFactory(); // 1. 优惠券 ICommodity commodityService_1 = storeFactory.getCommodityService(1); commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null); // 2. 实物商品 ICommodity commodityService_2 = storeFactory.getCommodityService(2); Map extMap = new HashMap(); extMap.put("consigneeUserName", "谢飞机"); extMap.put("consigneeUserPhone", "15200292123"); extMap.put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109"); commodityService_2.sendCommodity("10001","9820198721311","1023000020112221113",new HashMap() {{ put("consigneeUserName", "谢飞机"); put("consigneeUserPhone", "15200292123"); put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109"); }}); // 3. 第三方兑换卡(爱奇艺) ICommodity commodityService_3 = storeFactory.getCommodityService(3); commodityService_3.sendCommodity("10001","AQY1xjkUodl8LO975GdfrYUio",null,null); } ``` ### 抽象工厂 工厂模式一般是为了兼容不同的接口。 抽象工厂会和适配器模式相结合。这里还通过代理模式实现。 ```java /** 调用顺序: 1.ApiTest的JDKProxy.getProxy 2.JDKProxy的getProxy 3.ApiTest的proxy_EGM.set 4.JDKInvocationHandler的invoke 5.EGMCacheAdapter的set 6.ApiTest的proxy_EGM.get 7.JDKInvocationHandler的invoke 8.EGMCacheAdapter的get */ @Test public void test_CacheService() throws Exception { //根据传入不同的适配器 CacheService proxy_EGM = JDKProxy.getProxy(CacheServiceImpl.class, new EGMCacheAdapter()); proxy_EGM.set("user_name_01", "小傅哥"); String val01 = proxy_EGM.get("user_name_01"); System.out.println("测试结果:" + val01); CacheService proxy_IIR = JDKProxy.getProxy(CacheServiceImpl.class, new IIRCacheAdapter()); proxy_IIR.set("user_name_01", "小傅哥"); String val02 = proxy_IIR.get("user_name_01"); System.out.println("测试结果:" + val02); } ``` ```java //jdk动态代理 public class JDKProxy { public static T getProxy(Class interfaceClass, ICacheAdapter cacheAdapter) throws Exception { InvocationHandler handler = new JDKInvocationHandler(cacheAdapter); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Class[] classes = interfaceClass.getInterfaces(); //创建给定接口的动态代理对象,这个代理对象的方法调用都会转发到handler处理器 return (T) Proxy.newProxyInstance(classLoader, new Class[]{classes[0]}, handler); } } ``` ```java // public class JDKInvocationHandler implements InvocationHandler { private ICacheAdapter cacheAdapter; public JDKInvocationHandler(ICacheAdapter cacheAdapter) { this.cacheAdapter = cacheAdapter; } /** * 在代理对象的方法被调用时被JDK自动调用,将代理对象上的方法调用转发到cacheAdapter对象上 * @param proxy 代理对象 * @param method 被调用方法的信息 * @param args 被调用方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args); } } ``` ```java //统一适配器的接口 public interface ICacheAdapter { String get(String key); void set(String key, String value); void set(String key, String value, long timeout, TimeUnit timeUnit); void del(String key); } ``` ```java //EGM的适配器:通过实现ICacheAdapter接口,统一方法名 public class EGMCacheAdapter implements ICacheAdapter { //集群1:EGM private EGM egm = new EGM(); public String get(String key) { return egm.gain(key); } public void set(String key, String value) { egm.set(key, value); } public void set(String key, String value, long timeout, TimeUnit timeUnit) { egm.setEx(key, value, timeout, timeUnit); } public void del(String key) { egm.delete(key); } } ``` 抽象工厂的核心是:**在一个产品族,存在多个不同类型的产品(Redis集群、操作系统)情况下,接口选择的问题**。 **所以只需要把接口抽象出来,并作为调用方法的形参;在真正调用时传入接口的实现类作为实参**。这样就可以得到很好的完成功能实现并提升扩展性和优雅度。