Java23种设计模式

本文主要是对Java中一些常用的设计模式进行讲解 后期会进行不断的更新,欢迎浏览

23种设计模式

  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式。
  • 结构型模式,共七种:适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式、代理模式。
  • 行为型模式,共十一种:责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式、解释器模式。

六大设计原则

单一职责原则

  • 强调一个类或模块应该只负责一项功能或职责(高内聚 低耦合)

开闭原则

  • 在设计和构建软件实体时,应该尽量通过扩展(继承)现有的代码来实现新的功能,而不是修改已有的代码

里氏替原则

  • 里氏替换原则要求子类能够替换父类并在不影响程序正确性的前提下扩展父类的功能。这意味着子类应该保持父类的行为和约束,并且可以在不违反父类定义的前提下进行功能扩展。
  • 子类必须实现父类的所有抽象方法。
  • 子类可以有自己的个性化方法,但不能修改父类已有的方法。
  • 子类的前置条件(输入参数)要比父类的更宽松。
  • 子类的后置条件(输出结果)要比父类的更严格。
  • 子类不能抛出比父类更多或更宽泛的异常。

迪米特法则

  • 一个对象应该对其他对象有尽可能少的了解。具体而言,一个对象应该尽量减少对其他对象的依赖,只与直接的朋友通信,不与陌生的对象通信。

接口隔离原则

  • 通过合理设计接口,使接口的使用者不依赖于它们不需要的方法(允许实现多个接口)

依赖倒置原则

  • 强调高层模块不应该依赖于低层模块的具体实现,而是应该依赖于抽象接口。同时,抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

创建型模式

工厂方法模式(重点)

  • 在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型
  • 1.新建一个ICommodity接口,他拥有三个实现类CardCommodityService、CouponCommodityService、GoodsCommodityService
  • 2.创建一个统一的工厂类StoreFactory,用工厂类去创建上面的三个业务对象
public class StoreFactory {private Map<Integer, ICommodity> factoryMap = new HashMap<>();/*** 工厂初始化*/{factoryMap.put(1, new CouponCommodityService());factoryMap.put(2, new GoodsCommodityService());factoryMap.put(3, new CardCommodityService());}/*** 奖品类型方式实例化* @param commodityType 奖品类型* @return              实例化对象*/public ICommodity getCommodityService(Integer commodityType) {if (null == commodityType) return null;ICommodity iCommodity = factoryMap.get(commodityType);if(iCommodity != null){return iCommodity;}throw new RuntimeException("不存在的奖品服务类型");}/*** 奖品类信息方式实例化* @param clazz 奖品类* @return      实例化对象*/public ICommodity getCommodityService(Class<? extends ICommodity> clazz) throws IllegalAccessException, InstantiationException {if (null == clazz) return null;return clazz.newInstance();}
}
  • 3.使用时通过工厂类来创建对象
    @Testpublic void test_awardToUser() throws Exception {// 1. 优惠券ICommodity commodityService_1 = storeFactory.getCommodityService(1);commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);// 2. 实物商品ICommodity commodityService_2 = storeFactory.getCommodityService(GoodsCommodityService.class);commodityService_2.sendCommodity("10001", "9820198721311", "1023000020112221113", new HashMap<String, String>() {{put("consigneeUserName", "谢飞机");put("consigneeUserPhone", "15200292123");put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");}});// 3. 第三方兑换卡(模拟爱奇艺)ICommodity commodityService_3 = storeFactory.getCommodityService(3);commodityService_3.sendCommodity("10001", "AQY1xjkUodl8LO975GdfrYUio", null, null);}

抽象工厂模式

  • 抽象工厂模式与工厂方法模式虽然主要意图都是为了解决,接口选择问题。但在实现上,抽象工厂是一个中心工厂,创建其他工厂的模式。
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  • 1.ICacheAdapter接口下有EGMCacheAdapter、IIRCacheAdapter两个服务实现类
  • 2.InvocationHandler是针对ICacheAdapter接口的动态代理类
public class JDKInvocationHandler implements InvocationHandler {private ICacheAdapter cacheAdapter;public JDKInvocationHandler(ICacheAdapter cacheAdapter) {this.cacheAdapter = cacheAdapter;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 获取处理的指定方法return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);}
}
  • 3.抽象工厂创建代理类
public class JDKProxyFactory {public static <T> T getProxy(Class<T> cacheClazz, Class<? extends ICacheAdapter> cacheAdapter) throws Exception {InvocationHandler handler = new JDKInvocationHandler(cacheAdapter.newInstance());ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 动态代理方法:Proxy.newProxyInstance(类加载器,需要代理的接口,执行handler.invoke方法)return (T) Proxy.newProxyInstance(classLoader, new Class[]{cacheClazz}, handler);}
}
  • 4.测试使用
    @Testpublic void test_cacheService() throws Exception {CacheService proxy_Gm = JDKProxyFactory.getProxy(CacheService.class, EGMCacheAdapter.class);proxy_Gm.set("user_name_01", "imwj");String val01 = proxy_Gm.get("user_name_01");logger.info("缓存服务 EGM 测试,proxy_EGM.get 测试结果:{}", val01);CacheService proxy_IIR = JDKProxyFactory.getProxy(CacheService.class, IIRCacheAdapter.class);proxy_IIR.set("user_name_01", "imwj");String val02 = proxy_IIR.get("user_name_01");logger.info("缓存服务 IIR 测试,proxy_IIR.get 测试结果:{}", val02);}

建造者模式

  • 通过将多个简单对象通过一步步的组装构建出一个复杂对象的过程。
  • 1.建造模式接口
public interface IMenu {/*** 吊顶*/IMenu appendCeiling(Matter matter);/*** 涂料*/IMenu appendCoat(Matter matter);/*** 地板*/IMenu appendFloor(Matter matter);/*** 地砖*/IMenu appendTile(Matter matter);/*** 明细*/String getDetail();
}
  • 2.接口实现类
public class DecorationPackageMenu implements IMenu{private List<Matter> list = new ArrayList<Matter>();  // 装修清单private BigDecimal price = BigDecimal.ZERO;      // 装修价格private BigDecimal area;  // 面积private String grade;     // 装修等级;豪华欧式、轻奢田园、现代简约private DecorationPackageMenu() {}public DecorationPackageMenu(Double area, String grade) {this.area = new BigDecimal(area);this.grade = grade;}@Overridepublic IMenu appendCeiling(Matter matter) {list.add(matter);price = price.add(area.multiply(new BigDecimal("0.2")).multiply(matter.price()));return this;}@Overridepublic IMenu appendCoat(Matter matter) {list.add(matter);price = price.add(area.multiply(new BigDecimal("1.4")).multiply(matter.price()));return this;}public IMenu appendFloor(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}public IMenu appendTile(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}public String getDetail() {StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +"装修清单" + "\r\n" +"套餐等级:" + grade + "\r\n" +"套餐价格:" + price.setScale(2, BigDecimal.ROUND_HALF_UP) + " 元\r\n" +"房屋面积:" + area.doubleValue() + " 平米\r\n" +"材料清单:\r\n");for (Matter matter: list) {detail.append(matter.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");}return detail.toString();}
}
  • 3.建造模式构造器
public class Builder {public IMenu levelOne(Double area) {return new DecorationPackageMenu(area, "豪华欧式").appendCeiling(new LevelTwoCeiling())    // 吊顶,二级顶.appendCoat(new DuluxCoat())             // 涂料,多乐士.appendFloor(new ShengXiangFloor());     // 地板,圣象}public IMenu levelTwo(Double area){return new DecorationPackageMenu(area, "轻奢田园").appendCeiling(new LevelTwoCeiling())   // 吊顶,二级顶.appendCoat(new LiBangCoat())           // 涂料,立邦.appendTile(new MarcoPoloTile());       // 地砖,马可波罗}public IMenu levelThree(Double area){return new DecorationPackageMenu(area, "现代简约").appendCeiling(new LevelOneCeiling())   // 吊顶,一级顶.appendCoat(new LiBangCoat())           // 涂料,立邦.appendTile(new DongPengTile());        // 地砖,东鹏}}
  • 4.测试使用
    @Testpublic void test_Builder(){IMenu one = builder.levelOne(100D);System.out.println(one.getDetail());IMenu two = builder.levelTwo(100D);System.out.println(two.getDetail());IMenu three = builder.levelThree(100D);System.out.println(three.getDetail());}

原型模式

  • 主要解决的问题就是创建重复对象,而这部分对象内容本身比较复杂,生成过程可能从库或者RPC接口中获取数据的耗时较长,因此采用克隆的方式节省时间
  • 使用频率不高 可以参考step04-00相关代码

单例模式

  • 单例对象的类只能允许一个实例存在
  • 0.静态类
public class Singleton_00 {private static Map<String, String> cache = new ConcurrentHashMap<String, String>();}
  • 1.懒汉模式(线程不安全)
public class Singleton_01 {private static Singleton_01 singleton_01;private Singleton_01() {}public static Singleton_01 getSingleton_01() {if (singleton_01 == null) {singleton_01 = new Singleton_01();}return singleton_01;}
}
  • 2.懒汉模式(线程安全)
public class Singleton_02 {private static Singleton_02 instance;private Singleton_02() {}public static synchronized Singleton_02 getInstance(){if (null != instance) return instance;instance = new Singleton_02();return instance;}
}
  • 3.饿汉模式(线程安全)
public class Singleton_03 {private static Singleton_03 instance = new Singleton_03();private Singleton_03() {}public static Singleton_03 getInstance() {return instance;}
}
  • 4.使用类的内部类(线程安全)
public class Singleton_04 {private static class SingletonHolder {private static Singleton_04 instance = new Singleton_04();}private Singleton_04() {}public static Singleton_04 getInstance() {return SingletonHolder.instance;}}
  • 5.双重锁校验(线程安全)
public class Singleton_05 {private static volatile Singleton_05 instance;private Singleton_05() {}public static Singleton_05 getInstance(){if(null != instance) return instance;synchronized (Singleton_05.class){if (null == instance){instance = new Singleton_05();}}return instance;}
}
  • 6.CAS「AtomicReference」(线程安全)
public class Singleton_06 {private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();private Singleton_06() {}public static final Singleton_06 getInstance() {for (; ; ) {Singleton_06 instance = INSTANCE.get();if (null != instance) return instance;INSTANCE.compareAndSet(null, new Singleton_06());return INSTANCE.get();}}
  • 7.Effective Java作者推荐的枚举单例(线程安全)
public enum Singleton_07 {INSTANCE;public void test(){System.out.println("hi~");}
}

结构型模式

适配器模式

  • 适配器模式的主要作用就是把原本不兼容的接口,通过适配修改做到统一
  • 1.实体对象适配,service服务适配(实现同一个接口即可)
  • 2.现有一个create_account对象需要转换为RebateInfo对象
  • 3.字段转换适配器
public class MQAdapter {public static RebateInfo filter(String strJson, Map<String, String> link) throws Exception {return filter(JSON.parseObject(strJson, Map.class), link);}/*** 消息过滤转换** @param obj  数据实体* @param link key对应关系(key:rebateInfo字段名  value:原先字段名)* @return*/public static RebateInfo filter(Map obj, Map<String, String> link) throws Exception {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);}return rebateInfo;}
}
  • 4.测试使用
    @Testpublic void test() throws Exception {create_account create_account = new create_account();create_account.setNumber("100001");create_account.setAddress("河北省.廊坊市.广阳区.大学里职业技术学院");create_account.setAccountDate(new Date());create_account.setDesc("在校开户");HashMap<String, String> link01 = new HashMap<String, String>();link01.put("userId", "number");link01.put("bizId", "number");link01.put("desc", "desc");RebateInfo rebateInfo01 = MQAdapter.filter(JSON.toJSONString(create_account), link01);System.out.println("mq.create_account(适配前)" + create_account.toString());System.out.println("mq.create_account(适配后)" + JSON.toJSONString(rebateInfo01));System.out.println("======service适配(实现同一个接口即可)===========");OrderAdapterService popOrderAdapterService = new POPOrderAdapterServiceImpl();System.out.println("判断首单,接口适配(POP):" + popOrderAdapterService.isFirst("100001"));OrderAdapterService insideOrderService = new InsideOrderServiceImpl();System.out.println("判断首单,接口适配(自营):" + insideOrderService.isFirst("100001"));}

桥接模式

  • 桥接模式的主要作用就是通过将抽象部分与实现部分分离,把多种可匹配的使用进行组合。说白了核心实现也就是在A类中含有B类接口,通过构造函数传递B类的实现,这个B类就是设计的桥
  • 1.定义一个支付方式接口IPayMode,下面有n个实现类PayCypherPayFaceModePayFingerprintMode
public interface IPayMode {/*** 支付是否安全* @param uId* @return*/boolean security(String uId);}
public class PayFaceMode implements IPayMode{protected Logger logger = LoggerFactory.getLogger(IPayMode.class);@Overridepublic boolean security(String uId) {logger.info("人脸支付,风控校验脸部识别");return true;}
}
  • 2.抽象一个支付类Pay,下面有两个支付实体WxPayZfbPay,抽象类的构造方法会将支付方式作为参数传递进来,ZfbPay实体就能根据传递进来的支付方式进行支付了
public abstract class Pay {protected Logger logger = LoggerFactory.getLogger(Pay.class);protected IPayMode payMode;Pay(IPayMode payMode){this.payMode = payMode;}public abstract String transfer(String uId, String tradeId, BigDecimal amount);}
public class ZfbPay extends Pay{public ZfbPay(IPayMode payMode) {super(payMode);}@Overridepublic String transfer(String uId, String tradeId, BigDecimal amount) {logger.info("模拟支付宝渠道支付划账开始。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);boolean security = payMode.security(uId);logger.info("模拟支付宝渠道支付风控校验。uId:{} tradeId:{} security:{}", uId, tradeId, security);if (!security) {logger.info("模拟支付宝渠道支付划账拦截。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);return "0001";}logger.info("模拟支付宝渠道支付划账成功。uId:{} tradeId:{} amount:{}", uId, tradeId, amount);return "0000";}
}
  • 3.测试使用
public class ApiTest {@Testpublic void test(){System.out.println("\r\n模拟测试场景;微信支付、人脸方式。");Pay wxPay = new WxPay(new PayFaceMode());wxPay.transfer("weixin_1092033111", "100000109893", new BigDecimal(100));System.out.println("\r\n模拟测试场景;支付宝支付、指纹方式。");Pay zfbPay = new ZfbPay(new PayFingerprintMode());zfbPay.transfer("jlu19dlxo111", "100000109894", new BigDecimal(100));}
}

组合模式

  • 通过把相似对象(也可以称作是方法)组合成一组可被调用的结构树对象的设计思路叫做组合模式,并且能够以统一的方式处理单个对象以及组合对象
  • 1.一个相对简单的例子
// 组件接口
interface Component {void operation();
}// 叶子类
class Leaf implements Component {public void operation() {System.out.println("Leaf operation");}
}// 容器类
class Composite implements Component {private List<Component> components = new ArrayList<>();public void addComponent(Component component) {components.add(component);}public void removeComponent(Component component) {components.remove(component);}public void operation() {System.out.println("Composite operation");// 遍历子组件并调用其操作方法for (Component component : components) {component.operation();}}
}// 客户端代码
public class Client {public static void main(String[] args) {Component leaf1 = new Leaf();Component leaf2 = new Leaf();Component composite = new Composite();composite.addComponent(leaf1);composite.addComponent(leaf2);composite.operation();}
}
  • 2.输出结果
Composite operation
Leaf operation
Leaf operation

装饰器模式

  • 通过创建包装器对象来包裹原始对象,从而在不修改原始对象的情况下动态地扩展其功能。
  • 1.原有一个拦截器接口HandlerInterceptor,以及一个实现类SsoInterceptor,在不改变原有类的情况下 我们对其做扩展
public interface HandlerInterceptor {boolean preHandle(String request, String response, Object handler);
}
public class SsoInterceptor implements HandlerInterceptor{@Overridepublic boolean preHandle(String request, String response, Object handler) {// 模拟获取cookieString ticket = request.substring(1, 8);// 模拟校验return ticket.equals("success");}
}
  • 2.抽象类装饰角色(将原有的逻辑接口接入)SsoDecorator,并针对抽象类进行继承增强LoginSsoDecorator
public abstract class SsoDecorator {private HandlerInterceptor handlerInterceptor;/*** 将原有的逻辑方法传入* @param handlerInterceptor*/public SsoDecorator(HandlerInterceptor handlerInterceptor){this.handlerInterceptor = handlerInterceptor;}/*** 继承原有的逻辑方法* @param request* @param response* @param handler* @return*/public boolean preHandle(String request, String response, Object handler) {return handlerInterceptor.preHandle(request, response, handler);}
}
public class LoginSsoDecorator extends SsoDecorator{private Logger logger = LoggerFactory.getLogger(LoginSsoDecorator.class);private static Map<String, String> authMap = new ConcurrentHashMap<String, String>();static {authMap.put("huahua", "queryUserInfo");authMap.put("doudou", "queryUserInfo");}/*** 将原有的逻辑方法传入** @param handlerInterceptor*/public LoginSsoDecorator(HandlerInterceptor handlerInterceptor) {super(handlerInterceptor);}@Overridepublic boolean preHandle(String request, String response, Object handler) {// 先调用原有的逻辑方法boolean success = super.preHandle(request, response, handler);// 自己新增的逻辑if (!success) return false;String userId = request.substring(8);String method = authMap.get(userId);logger.info("模拟单点登录方法访问拦截校验:{} {}", userId, method);// 模拟方法校验return "queryUserInfo".equals(method);}
}
  • 3.测试使用
public class ApiTest {@Testpublic void test_LoginSsoDecorator() {LoginSsoDecorator ssoDecorator = new LoginSsoDecorator(new SsoInterceptor());String request = "1successhuahua";boolean success = ssoDecorator.preHandle(request, "ewcdqwt40liuiu", "t");System.out.println("登录校验:" + request + (success ? " 放行" : " 拦截"));}
}

外观模式

  • 将一组复杂的子系统封装起来,对外提供一个简单的接口,以简化客户端与子系统的交互
  • 1.在实际开发中我们会有多个controller,每个controller都需要拦截处理 校验用户是否能够访问(白名单),这个时候我们可以用自定义注解DoDoor + aop来进行处理
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoDoor {/*** 拦截字段值* @return*/String key() default "";/*** 拦截时返回json* @return*/String returnJson() default "";}
  • 2.aop实现自定义注解逻辑
@Aspect
@Component
public class DoJoinPoint {private Logger logger = LoggerFactory.getLogger(DoJoinPoint.class);@Value("${userIdStr}")public String userIdStr;@Pointcut("@annotation(com.imwj.design.door.annotation.DoDoor)")public void aopPoint(){}@Around("aopPoint()")public Object doRouter(ProceedingJoinPoint jp) throws Throwable {// 获取方法内容Method method = getMethod(jp);// 获取注解中的字段值DoDoor door = method.getAnnotation(DoDoor.class);String keyValue = getFiledValue(door.key(), jp.getArgs());logger.info("door handler method:{} value:{}", method.getName(), keyValue);// 获取不到值直接放行if (null == keyValue || "".equals(keyValue)) return jp.proceed();// 白名单放行if(checkUserIdIntercept(keyValue))return jp.proceed();// 拦截并返回return returnObject(door, method);}/*** 获取方法* @param jp* @return* @throws NoSuchMethodException*/private Method getMethod(JoinPoint jp) throws NoSuchMethodException {Signature sig = jp.getSignature();MethodSignature methodSignature = (MethodSignature) sig;return getClass(jp).getMethod(methodSignature.getName(), methodSignature.getParameterTypes());}private Class<? extends Object> getClass(JoinPoint jp) throws NoSuchMethodException {return jp.getTarget().getClass();}/*** 校验用户id是否需要拦截* @param userId* @return*/private Boolean checkUserIdIntercept(String userId){return userIdStr.contains(userId);}/*** 返回对象* @param doGate* @param method* @return* @throws IllegalAccessException* @throws InstantiationException*/private Object returnObject(DoDoor doGate, Method method) throws IllegalAccessException, InstantiationException {Class<?> returnType = method.getReturnType();String returnJson = doGate.returnJson();if ("".equals(returnJson)) {return returnType.newInstance();}return JSON.parseObject(returnJson, returnType);}/*** 获取属性值* @param filed* @param args* @return*/private String getFiledValue(String filed, Object[] args) {String filedValue = null;for (Object arg : args) {try {if (null == filedValue || "".equals(filedValue)) {filedValue = BeanUtils.getProperty(arg, filed);} else {break;}} catch (Exception e) {if (args.length == 1) {return args[0].toString();}}}return filedValue;}
}
  • 3.controller层使用
@RestController
public class HelloController {@DoDoor(key = "userId", returnJson = "{\"code\":\"1111\",\"info\":\"非白名单用户拦截!\"}")@RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET)public UserInfo queryUserInfo(@RequestParam String userId) {return new UserInfo("团团:" + userId, 19, "天津市南开区旮旯胡同100号");}
}

享元模式

  • 享元模式,主要在于共享通用对象,减少内存的使用,提升系统的访问效率
  • 1.有一个活动商品类Activity,里面有商品基础信息以及库存信息Stock,我们希望通过数据库存储商品信息 redis里存储商品库存
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Activity {/** 活动ID */private Long id;/** 活动名称 */private String name;/** 活动描述 */private String desc;/** 开始时间 */private Date startTime;/** 结束时间 */private Date stopTime;/** 活动库存 */private Stock stock;
}
@Data
@AllArgsConstructor
public class Stock {/** 库存总量 */private int total;/** 库存已用 */private int used;
}
  • 2.RedisUtils存储库存信息,获取时通过redis工具类获取
public class RedisUtils {private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);private AtomicInteger stock = new AtomicInteger(0);public RedisUtils() {scheduledExecutorService.scheduleAtFixedRate(() -> {// 模拟库存消耗stock.addAndGet(1);}, 0, 100000, TimeUnit.MICROSECONDS);}public int getStockUsed() {return stock.get();}
}
  • 3.controller层获取库存
public class ActivityController {private RedisUtils redisUtils = new RedisUtils();public Activity queryActivityInfo(Long id) {Activity activity = ActivityFactory.queryInfo(id);// 模拟从Redis中获取库存变化信息Stock stock = new Stock(1000, redisUtils.getStockUsed());activity.setStock(stock);return activity;}
}

代理模式

  • 它允许通过代理对象控制对另一个对象的访问。代理模式创建了一个代理对象,代理对象与原始对象具有相同的接口,客户端通过代理对象间接地访问原始对象,从而可以在不改变原始对象的情况下增加额外的功能或控制访问
  • 常见的如:mybatis中只需要定义一个接口 然后在注解或xml中配置sql即能执行sql查询
  • 1.定义一个IUserDao接口,一个@Select注解
public interface IUserDao {@Select("select userName from user where id = #{uId}")String queryUserInfo(String uId);}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Select {/*** sql语句* @return*/String value() default "";
}
  • 2.代理类定义MapperFactoryBean
public class MapperFactoryBean<T> implements FactoryBean<T> {private Logger logger = LoggerFactory.getLogger(MapperFactoryBean.class);private Class<T> mapperInterface;public MapperFactoryBean(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}/*** 创建代理对象* @return* @throws Exception*/@Overridepublic T getObject() throws Exception {InvocationHandler handler = (proxy, method, args) ->{Select select = method.getAnnotation(Select.class);logger.info("SQL:{}", select.value().replace("#{uId}", args[0].toString()));return args[0] + ",乐于分享!";};return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperInterface}, handler);}@Overridepublic Class<?> getObjectType() {return mapperInterface;}@Overridepublic boolean isSingleton() {return true;}
}
  • 3.注册beanFactory
public class RegisterBeanFactory implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {GenericBeanDefinition beanDefinition = new GenericBeanDefinition();beanDefinition.setBeanClass(MapperFactoryBean.class);beanDefinition.setScope("singleton");beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(IUserDao.class);BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, "userDao");BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}
}
  • 4.spring-config.xml中配置扫描;路径
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"default-autowire="byName"><bean id="userDao" class="com.imwj.design.agent.RegisterBeanFactory"/></beans>
  • 5.注入使用
    @Testpublic void test_IUserDao() {BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");IUserDao userDao = beanFactory.getBean("userDao", IUserDao.class);String res = userDao.queryUserInfo("100001");logger.info("测试结果:{}", res);}

行为型模式

责任链模式

  • 用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会(条件匹配)处理请求事务,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。
  • Spring拦截器链、servlet过滤器链等都采用了责任链设计模式。
/*** 责任链抽象处理类* @author langao_q* @since 2021-12-29 15:37*/
public class AbstractLeaveHandler {/**三级领导处理*/protected int MIN = 10;/**二级领导处理*/protected int MIDDLE = 20;/**一级级领导处理*/protected int MAX = 30;/**领导名称*/protected String handlerName;/**下一个处理节点(即更高级别的领导)*/protected AbstractLeaveHandler nextHandler;/**设置下一节点*/protected void setNextHandler(AbstractLeaveHandler handler){this.nextHandler = handler;}/**处理请求,子类实现*/protected void handlerRequest(LeaveRequest request){}
}
/*** 请求实体* @author langao_q* @since 2021-12-29 15:37*/
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class LeaveRequest {/**天数*/private int leaveDays;/**姓名*/private String name;
}
/***  一级领导* @author langao_q* @since 2021-12-29 15:39*/
public class OneLeaveHandler extends AbstractLeaveHandler {public OneLeaveHandler(String name) {this.handlerName = name;}@Overrideprotected void handlerRequest(LeaveRequest request) {if(request.getLeaveDays() > this.MIDDLE && request.getLeaveDays() <= this.MAX){System.out.println(handlerName + ",已经处理;流程结束。");return;}if(null != this.nextHandler){this.nextHandler.handlerRequest(request);}else{System.out.println("审批拒绝!");}}
}
/*** 二级领导* @author langao_q* @since 2021-12-29 15:38*/
public class TwoLeaveHandler extends AbstractLeaveHandler {public TwoLeaveHandler(String name) {this.handlerName = name;}@Overrideprotected void handlerRequest(LeaveRequest request) {if(request.getLeaveDays() >this.MIN && request.getLeaveDays() <= this.MIDDLE){System.out.println(handlerName + ",已经处理;流程结束。");return;}if(null != this.nextHandler){this.nextHandler.handlerRequest(request);}else{System.out.println("审批拒绝!");}}
}
/*** 三级领导* @author langao_q* @since 2021-12-29 15:38*/
public class ThreeLeaveHandler extends AbstractLeaveHandler{public ThreeLeaveHandler(String name) {this.handlerName = name;}@Overrideprotected void handlerRequest(LeaveRequest request) {if(request.getLeaveDays() <= this.MIN){System.out.println(handlerName + ",已经处理;流程结束。");return;}if(null != this.nextHandler){this.nextHandler.handlerRequest(request);}else{System.out.println("审批拒绝!");}}
}
/*** 测试类* @author langao_q* @since 2021-12-29 15:39*/
public class MainTest {public static void main(String[] args) {//根据leaveDays的值来决定是哪一级别的领导处理LeaveRequest request = LeaveRequest.builder().leaveDays(50).name("测试").build();/*** 三级(10) < 二级(20) < 一级(30);三级领导能处理就不往上走了  三级处理不了再抛给二级领导*/AbstractLeaveHandler directLeaderLeaveHandler = new ThreeLeaveHandler("三级领导");TwoLeaveHandler deptManagerLeaveHandler = new TwoLeaveHandler("二级领导");OneLeaveHandler gManagerLeaveHandler = new OneLeaveHandler("一级领导");//将各个处理类串联起来directLeaderLeaveHandler.setNextHandler(deptManagerLeaveHandler);deptManagerLeaveHandler.setNextHandler(gManagerLeaveHandler);//处理方法directLeaderLeaveHandler.handlerRequest(request);}
}

命令模式

  • 将请求和操作进行解耦,以便能够将请求封装成独立的对象,并在需要时进行参数化
  • 1.新建一个ICook厨师接口、一个ICuisine菜系接口(菜系实现类中将对应的厨师实现类作为构造参数传入),两个接口各自有各自的n个实现类
public class GuangDongCook implements ICook{private Logger logger = LoggerFactory.getLogger(ICook.class);@Overridepublic void doCooking() {logger.info("广东厨师,烹饪粤菜,宫廷菜系,以孔府风味为龙头");}
}public class GuangDoneCuisine implements ICuisine {private ICook cook;private GuangDoneCuisine() {}public GuangDoneCuisine(ICook cook) {this.cook = cook;}@Overridepublic void cook() {cook.doCooking();}}
  • 2.新建一个XiaoEr小二类,将对应的菜系作为构造参数传入
public class XiaoEr {private Logger logger = LoggerFactory.getLogger(XiaoEr.class);private List<ICuisine> cuisineList = new ArrayList<ICuisine>();public void order(ICuisine cuisine) {cuisineList.add(cuisine);}public synchronized void placeOrder() {for (ICuisine cuisine : cuisineList) {cuisine.cook();}cuisineList.clear();}
}
  • 3.测试使用:构建厨师、菜系将菜系放入小二类的订单中
public class ApiTest {@Testpublic void test_xiaoEr(){// 菜系 + 厨师;广东(粤菜)、江苏(苏菜)、山东(鲁菜)、四川(川菜)ICuisine guangDoneCuisine = new GuangDoneCuisine(new GuangDongCook());JiangSuCuisine jiangSuCuisine = new JiangSuCuisine(new JiangSuCook());// 点单XiaoEr xiaoEr = new XiaoEr();xiaoEr.order(guangDoneCuisine);xiaoEr.order(jiangSuCuisine);xiaoEr.placeOrder();}
}

迭代器模式

  • 略.

中介者模式

  • 解决的就是复杂功能应用之间的重复调用,在这中间添加一层中介者包装服务,对外提供简单、通用、易扩展的服务能力。
  • 1.新建一个SqlSession接口,以及一个对应实现类DefaultSqlSession
public class DefaultSqlSession implements SqlSession{private Connection connection;/*** key:dao路径 + 方法名,value:xml中的sql信息*/private Map<String, XNode> mapperElement;public DefaultSqlSession(Connection connection, Map<String, XNode> mapperElement) {this.connection = connection;this.mapperElement = mapperElement;}@Overridepublic <T> T selectOne(String statement) {try {XNode xNode = mapperElement.get(statement);PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());ResultSet resultSet = preparedStatement.executeQuery();List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));return objects.get(0);} catch (Exception e) {e.printStackTrace();}return null;}@Overridepublic <T> T selectOne(String statement, Object parameter) {XNode xNode = mapperElement.get(statement);Map<Integer, String> parameterMap = xNode.getParameter();try {PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());buildParameter(preparedStatement, parameter, parameterMap);ResultSet resultSet = preparedStatement.executeQuery();List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));return objects.get(0);} catch (Exception e) {e.printStackTrace();}return null;}@Overridepublic <T> List<T> selectList(String statement) {try {XNode xNode = mapperElement.get(statement);PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());ResultSet resultSet = preparedStatement.executeQuery();List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));return objects;} catch (Exception e) {e.printStackTrace();}return null;}@Overridepublic <T> List<T> selectList(String statement, Object parameter) {XNode xNode = mapperElement.get(statement);Map<Integer, String> parameterMap = xNode.getParameter();try {PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());buildParameter(preparedStatement, parameter, parameterMap);ResultSet resultSet = preparedStatement.executeQuery();List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));return objects;} catch (Exception e) {e.printStackTrace();}return null;}@Overridepublic void close() {if (null == connection) return;try {connection.close();} catch (SQLException e) {e.printStackTrace();}}/*** jdbc查询结果转换为指定对象* @param resultSet* @param clazz* @return* @param <T>*/private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {List<T> list = new ArrayList<>();try {ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();// 每次遍历行值while (resultSet.next()) {T obj = (T) clazz.newInstance();for (int i = 1; i <= columnCount; i++) {Object value = resultSet.getObject(i);String columnName = metaData.getColumnName(i);String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);Method method;if (value instanceof Timestamp) {method = clazz.getMethod(setMethod, Date.class);} else {method = clazz.getMethod(setMethod, value.getClass());}method.invoke(obj, value);}list.add(obj);}} catch (Exception e) {e.printStackTrace();}return list;}private void buildParameter(PreparedStatement preparedStatement, Object parameter, Map<Integer, String> parameterMap) throws SQLException, IllegalAccessException {int size = parameterMap.size();// 单个参数if (parameter instanceof Long) {for (int i = 1; i <= size; i++) {preparedStatement.setLong(i, Long.parseLong(parameter.toString()));}return;}if (parameter instanceof Integer) {for (int i = 1; i <= size; i++) {preparedStatement.setInt(i, Integer.parseInt(parameter.toString()));}return;}if (parameter instanceof String) {for (int i = 1; i <= size; i++) {preparedStatement.setString(i, parameter.toString());}return;}Map<String, Object> fieldMap = new HashMap<>();// 对象参数Field[] declaredFields = parameter.getClass().getDeclaredFields();for (Field field : declaredFields) {String name = field.getName();field.setAccessible(true);Object obj = field.get(parameter);field.setAccessible(false);fieldMap.put(name, obj);}for (int i = 1; i <= size; i++) {String parameterDefine = parameterMap.get(i);Object obj = fieldMap.get(parameterDefine);if (obj instanceof Short) {preparedStatement.setShort(i, Short.parseShort(obj.toString()));continue;}if (obj instanceof Integer) {preparedStatement.setInt(i, Integer.parseInt(obj.toString()));continue;}if (obj instanceof Long) {preparedStatement.setLong(i, Long.parseLong(obj.toString()));continue;}if (obj instanceof String) {preparedStatement.setString(i, obj.toString());continue;}if (obj instanceof Date) {preparedStatement.setDate(i, (java.sql.Date) obj);}}}
}
  • 2.新建一个SqlSessionFactory接口,以及对应实现类DefaultSqlSessionFactory,其作用主要是配置和获取DefaultSqlSession
public class DefaultSqlSessionFactory implements SqlSessionFactory{private final Configuration configuration;public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}@Overridepublic SqlSession openSession() {return new DefaultSqlSession(configuration.connection, configuration.mapperElement);}
}
  • 3.新建一个SqlSessionFactoryBuilder,主要用于读取User_Mapper.xml构建DefaultSqlSessionFactory
public class SqlSessionFactoryBuilder {public DefaultSqlSessionFactory build(Reader reader) {SAXReader saxReader = new SAXReader();try {saxReader.setEntityResolver(new XMLMapperEntityResolver());Document document = saxReader.read(new InputSource(reader));Configuration configuration = parseConfiguration(document.getRootElement());return new DefaultSqlSessionFactory(configuration);} catch (DocumentException e) {e.printStackTrace();}return null;}private Configuration parseConfiguration(Element root) {Configuration configuration = new Configuration();configuration.setDataSource(dataSource(root.selectNodes("//dataSource")));configuration.setConnection(connection(configuration.dataSource));configuration.setMapperElement(mapperElement(root.selectNodes("mappers")));return configuration;}// 获取数据源配置信息private Map<String, String> dataSource(List<Element> list) {Map<String, String> dataSource = new HashMap<>(4);Element element = list.get(0);List content = element.content();for (Object o : content) {Element e = (Element) o;String name = e.attributeValue("name");String value = e.attributeValue("value");dataSource.put(name, value);}return dataSource;}private Connection connection(Map<String, String> dataSource) {try {Class.forName(dataSource.get("driver"));return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}return null;}// 获取SQL语句信息private Map<String, XNode> mapperElement(List<Element> list) {Map<String, XNode> map = new HashMap<>();Element element = list.get(0);List content = element.content();for (Object o : content) {Element e = (Element) o;String resource = e.attributeValue("resource");try {Reader reader = Resources.getResourceAsReader(resource);SAXReader saxReader = new SAXReader();Document document = saxReader.read(new InputSource(reader));Element root = document.getRootElement();//命名空间String namespace = root.attributeValue("namespace");// SELECTList<Element> selectNodes = root.selectNodes("select");for (Element node : selectNodes) {String id = node.attributeValue("id");String parameterType = node.attributeValue("parameterType");String resultType = node.attributeValue("resultType");String sql = node.getText();// ? 匹配Map<Integer, String> parameter = new HashMap<>();Pattern pattern = Pattern.compile("(#\\{(.*?)})");Matcher matcher = pattern.matcher(sql);for (int i = 1; matcher.find(); i++) {String g1 = matcher.group(1);String g2 = matcher.group(2);parameter.put(i, g2);sql = sql.replace(g1, "?");}XNode xNode = new XNode();xNode.setNamespace(namespace);xNode.setId(id);xNode.setParameterType(parameterType);xNode.setResultType(resultType);xNode.setSql(sql);xNode.setParameter(parameter);map.put(namespace + "." + id, xNode);}} catch (Exception ex) {ex.printStackTrace();}}return map;}}
  • 4.测试使用,读取mybatis-config-datasource.xml配置 然后构建SqlSessionFactory对象,最后获取到SqlSession对象用于执行sql
    @Testpublic void test_queryUserInfoById() {String resource = "mybatis-config-datasource.xml";Reader reader;try {reader = Resources.getResourceAsReader(resource);SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);SqlSession session = sqlMapper.openSession();try {User user = session.selectOne("com.imwj.design.dao.IUserDao.queryUserInfoById", "62baae12-41cb-11eb-8d21-00163e0cd193");logger.info("测试结果:{}", JSON.toJSONString(user));} finally {session.close();reader.close();}} catch (Exception e) {e.printStackTrace();}}

备忘录模式

  • 以回滚,配置、版本、悔棋为核心功能的设计模式,在功能实现上是以不破坏原对象为基础增加备忘录操作类,记录原对象的行为从而实现备忘录模式。
  • 1.新建一个配置文件类ConfigFile,主要用于记录版本的相关信息
@Data
@AllArgsConstructor
public class ConfigFile {private String versionNo; // 版本号private String content;   // 内容private Date dateTime;    // 时间private String operator;  // 操作人
}
  • 2.新建一个备忘录类ConfigMemento,对原有配置类的扩展,可以设置和获取配置信息
public class ConfigMemento {private ConfigFile configFile;public ConfigMemento(ConfigFile configFile) {this.configFile = configFile;}public ConfigFile getConfigFile() {return configFile;}public void setConfigFile(ConfigFile configFile) {this.configFile = configFile;}
}
  • 3.新建一个记录者类ConfigOriginator,保存操作的相关信息
public class ConfigOriginator {private ConfigFile configFile;public ConfigFile getConfigFile() {return configFile;}public void setConfigFile(ConfigFile configFile) {this.configFile = configFile;}public ConfigMemento saveMemento(){return new ConfigMemento(configFile);}public void getMemento(ConfigMemento memento){this.configFile = memento.getConfigFile();}
}
  • 4.创建一个管理员类Admin,主要操作回滚、前进、跳转到指定版本
public class Admin {private int cursorIdx = 0;private List<ConfigMemento> mementoList = new ArrayList<>();private Map<String, ConfigMemento> mementoMap = new ConcurrentHashMap<>();public void append(ConfigMemento memento){mementoList.add(memento);mementoMap.put(memento.getConfigFile().getVersionNo(), memento);cursorIdx ++;}public ConfigMemento undo(){if(--cursorIdx <= 0){return mementoList.get(0);}return mementoList.get(cursorIdx);}public ConfigMemento  redo(){if(++cursorIdx > mementoList.size()){return mementoList.get(mementoList.size() - 1);}return mementoList.get(cursorIdx);}public ConfigMemento get(String versionNo){return mementoMap.get(versionNo);}}
  • 5.测试使用
    @Testpublic void test() {Admin admin = new Admin();ConfigOriginator configOriginator = new ConfigOriginator();configOriginator.setConfigFile(new ConfigFile("1000001", "配置内容A=哈哈", new Date(), "imwj"));admin.append(configOriginator.saveMemento()); // 保存配置configOriginator.setConfigFile(new ConfigFile("1000002", "配置内容A=嘻嘻", new Date(), "imwj"));admin.append(configOriginator.saveMemento()); // 保存配置configOriginator.setConfigFile(new ConfigFile("1000003", "配置内容A=么么", new Date(), "imwj"));admin.append(configOriginator.saveMemento()); // 保存配置configOriginator.setConfigFile(new ConfigFile("1000004", "配置内容A=嘿嘿", new Date(), "imwj"));admin.append(configOriginator.saveMemento()); // 保存配置  // 历史配置(回滚)configOriginator.getMemento(admin.undo());logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(回滚)configOriginator.getMemento(admin.undo());logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(前进)configOriginator.getMemento(admin.redo());logger.info("历史配置(前进)redo:{}", JSON.toJSONString(configOriginator.getConfigFile()));// 历史配置(获取)configOriginator.getMemento(admin.get("1000002"));logger.info("历史配置(获取)get:{}", JSON.toJSONString(configOriginator.getConfigFile()));}

观察者模式

  • 当一个行为发生时传递信息给另外一个用户接收做出相应的处理,两者之间没有直接的耦合关联。
  • 1.新建一个监听接口EventListener,对应的有两个实现类MessageEventListenerMQEventListener
public class MessageEventListener implements EventListener{private Logger logger = LoggerFactory.getLogger(MessageEventListener.class);@Overridepublic void doEvent(LotteryResult result) {logger.info("给用户 {} 发送短信通知(短信):{}", result.getUId(), result.getMsg());}
}
  • 2.新建一个监听事件处理类EventManager
public class EventManager {Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();public enum EventType {MQ, Message}public EventManager(Enum<EventType>... operations) {for (Enum<EventType> operation : operations) {this.listeners.put(operation, new ArrayList<>());}}/*** 订阅* @param eventType* @param listener*/public void subscribe(Enum<EventType> eventType, EventListener listener){List<EventListener> users = listeners.get(eventType);users.add(listener);}/*** 取消订阅* @param eventType* @param listener*/public void unsubscribe(Enum<EventType> eventType, EventListener listener){List<EventListener> users = listeners.get(eventType);users.remove(listener);}/*** 消息通知* @param eventType* @param result*/public void notify(Enum<EventType> eventType, LotteryResult result){List<EventListener> users  = listeners.get(eventType);for(EventListener listener : users){listener.doEvent(result);}}
}
  • 3.抽取业务类接口LotteryService(抽象类)
public abstract class LotteryService {private EventManager eventManager;public LotteryService(){eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message);eventManager.subscribe(EventManager.EventType.MQ, new MQEventListener());eventManager.subscribe(EventManager.EventType.Message, new MessageEventListener());}public LotteryResult draw(String uId){LotteryResult lotteryResult = doDraw(uId);// 通知方法eventManager.notify(EventManager.EventType.MQ, lotteryResult);eventManager.notify(EventManager.EventType.Message, lotteryResult);return lotteryResult;}/*** 真正的业务方法* @param uId* @return*/protected abstract LotteryResult doDraw(String uId);}
  • 4.测试使用
    @Testpublic void test() {LotteryService lotteryService = new LotteryServiceImpl();LotteryResult result = lotteryService.draw("2765789109876");logger.info("测试结果:{}", JSON.toJSONString(result));}

状态模式

  • 描述的是一个行为下的多种状态变更,比如我们最常见的一个网站的页面,在你登录与不登录下展示的内容是略有差异的(不登录不能展示个人信息),而这种登录与不登录就是我们通过改变状态,而让整个行为发生了变化
  • 1.新建一个状态抽象类State,定义各个状态变更的方法
public abstract class State {/*** 活动提审** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result arraignment(String activityId, Enum<Status> currentStatus);/*** 审核通过** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result checkPass(String activityId, Enum<Status> currentStatus);/*** 审核拒绝** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus);/*** 撤审撤销** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus);/*** 活动关闭** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result close(String activityId, Enum<Status> currentStatus);/*** 活动开启** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result open(String activityId, Enum<Status> currentStatus);/*** 活动执行** @param activityId    活动ID* @param currentStatus 当前状态* @return 执行结果*/public abstract Result doing(String activityId, Enum<Status> currentStatus);}
  • 2.新建两个状态实现类:编辑状态EditingState,提审状态CheckState,同时在各自对应的方法中实现相应逻辑
public class EditingState extends State {public Result arraignment(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Check);return new Result("0000", "活动提审成功");}public Result checkPass(String activityId, Enum<Status> currentStatus) {return new Result("0001", "编辑中不可审核通过");}public Result checkRefuse(String activityId, Enum<Status> currentStatus) {return new Result("0001", "编辑中不可审核拒绝");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {return new Result("0001", "编辑中不可撤销审核");}public Result close(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Close);return new Result("0000", "活动关闭成功");}public Result open(String activityId, Enum<Status> currentStatus) {return new Result("0001", "非关闭活动不可开启");}public Result doing(String activityId, Enum<Status> currentStatus) {return new Result("0001", "编辑中活动不可执行活动中变更");}
}
public class CheckState extends State {public Result arraignment(String activityId, Enum<Status> currentStatus) {return new Result("0001", "待审核状态不可重复提审");}public Result checkPass(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Pass);return new Result("0000", "活动审核通过完成");}public Result checkRefuse(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Refuse);return new Result("0000", "活动审核拒绝完成");}@Overridepublic Result checkRevoke(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Editing);return new Result("0000", "活动审核撤销回到编辑中");}public Result close(String activityId, Enum<Status> currentStatus) {ActivityService.execStatus(activityId, currentStatus, Status.Close);return new Result("0000", "活动审核关闭完成");}public Result open(String activityId, Enum<Status> currentStatus) {return new Result("0001", "非关闭活动不可开启");}public Result doing(String activityId, Enum<Status> currentStatus) {return new Result("0001", "待审核活动不可执行活动中变更");}
}
  • 3.新建一个状态控制类StateHandler,用来控制状态变更
public class StateHandler {private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<Enum<Status>, State>();public StateHandler() {stateMap.put(Status.Check, new CheckState());     // stateMap.put(Status.Close, new CloseState());   // stateMap.put(Status.Doing, new DoingState());  stateMap.put(Status.Editing, new EditingState()); // stateMap.put(Status.Open, new OpenState());   // stateMap.put(Status.Pass, new PassState());    // stateMap.put(Status.Refuse, new RefuseState()); }public Result arraignment(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).arraignment(activityId, currentStatus);}public Result checkPass(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).checkPass(activityId, currentStatus);}public Result checkRefuse(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus);}public Result checkRevoke(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus);}public Result close(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).close(activityId, currentStatus);}public Result open(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).open(activityId, currentStatus);}public Result doing(String activityId, Enum<Status> currentStatus) {return stateMap.get(currentStatus).doing(activityId, currentStatus);}}
  • 4.测试使用
    @Testpublic void test_Editing2Arraignment() {String activityId = "100001";// 初始化状态ActivityService.init(activityId, Status.Editing);StateHandler stateHandler = new StateHandler();// 获取对应状态操作类 并执行其中的方法Result result = stateHandler.arraignment(activityId, Status.Editing);logger.info("测试结果(编辑中To提审活动):{}", JSON.toJSONString(result));logger.info("活动信息:{} 状态:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus()));}

策略模式(重点)

  • 允许在运行时选择算法的行为。它定义了一系列算法,并将每个算法封装在独立的类中,使它们可以互相替换。通过使用策略模式,可以使算法的变化独立于使用算法的客户端。 策略模式的主要目的是将算法的定义与使用分离,
  • 1.新建一个优惠券接口ICouponDiscount,该接口定义了一个优惠方法
public interface ICouponDiscount<T> {/*** 优惠券金额计算* @param couponInfo 券折扣信息;直减、满减、折扣、N元购* @param skuPrice   sku金额* @return           优惠后金额*/BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);
}
  • 2.优惠券接口有n个对应实现类:满减MJCouponDiscount,直减ZJCouponDiscount,每个实现类有自己的计算方式 此处还用到了泛型
public class MJCouponDiscount implements ICouponDiscount<Map<String,String>> {/*** 满减计算* 1. 判断满足x元后-n元,否则不减* 2. 最低支付金额1元*/@Overridepublic BigDecimal discountAmount(Map<String,String> couponInfo, BigDecimal skuPrice) {String x = couponInfo.get("x");String o = couponInfo.get("n");// 小于商品金额条件的,直接返回商品原价if (skuPrice.compareTo(new BigDecimal(x)) < 0) return skuPrice;// 减去优惠金额判断BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;return discountAmount;}
}
public class ZJCouponDiscount implements ICouponDiscount<Double > {/*** 直减计算* 1. 使用商品价格减去优惠价格* 2. 最低支付金额1元*/@Overridepublic BigDecimal discountAmount(Double  couponInfo, BigDecimal skuPrice) {BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;return discountAmount;}
}
  • 3.新建一个控制类,用于控制控制使用不同优惠方式Context
public class Context<T>{private ICouponDiscount<T> couponDiscount;public Context(ICouponDiscount<T> couponDiscount) {this.couponDiscount = couponDiscount;}public BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice) {return couponDiscount.discountAmount(couponInfo, skuPrice);}}
  • 4.测试使用
    @Testpublic void test_zj() {Context<Double> context = new Context<Double>(new ZJCouponDiscount());BigDecimal discountAmount = context.discountAmount(10D, new BigDecimal(100));logger.info("测试结果:直减优惠后金额 {}", discountAmount);Context<Map<String, String>> context1 = new Context<Map<String, String>>(new MJCouponDiscount());HashMap<String, String> map = new HashMap<>();map.put("x", "100");map.put("n", "30");BigDecimal discountAmount1 = context1.discountAmount(map, new BigDecimal(100));logger.info("测试结果:满减优惠后金额 {}", discountAmount1);}

模板方法模式(重点)

  • 模板模式的核心设计思路是通过在,抽象类中定义抽象方法的执行顺序,并将抽象方法设定为只有子类实现,但不设计独立访问的方法
  • 1.新建一个抽象父类NetMall,在父类中定义一个可对外调用的方法generateGoodsPoster,同时定义三个待实现的方法(由后续子类实现[模板])
public abstract class NetMall {protected Logger logger = LoggerFactory.getLogger(NetMall.class);String uId;   // 用户IDString uPwd;  // 用户密码public NetMall(String uId, String uPwd) {this.uId = uId;this.uPwd = uPwd;}/*** 生成商品推广海报** @param skuUrl 商品地址(京东、淘宝、当当)* @return 海报图片base64位信息*/public String generateGoodsPoster(String skuUrl) {if (!login(uId, uPwd)) return null;             // 1. 验证登录Map<String, String> reptile = reptile(skuUrl);  // 2. 爬虫商品return createBase64(reptile);                   // 3. 组装海报}// 模拟登录protected abstract Boolean login(String uId, String uPwd);// 爬虫提取商品信息(登录后的优惠价格)protected abstract Map<String, String> reptile(String skuUrl);// 生成商品海报信息protected abstract String createBase64(Map<String, String> goodsInfo);
}
  • 2.创建实现类JDNetMall,针对上面待实现的三个方法实现
public class JDNetMall extends NetMall {public JDNetMall(String uId, String uPwd) {super(uId, uPwd);}public Boolean login(String uId, String uPwd) {logger.info("模拟京东用户登录 uId:{} uPwd:{}", uId, uPwd);return true;}public Map<String, String> reptile(String skuUrl) {String str = HttpClient.doGet(skuUrl);Pattern p9 = Pattern.compile("(?<=title\\>).*(?=</title)");Matcher m9 = p9.matcher(str);Map<String, String> map = new ConcurrentHashMap<String, String>();if (m9.find()) {map.put("name", m9.group());}map.put("price", "5999.00");logger.info("模拟京东商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);return map;}public String createBase64(Map<String, String> goodsInfo) {BASE64Encoder encoder = new BASE64Encoder();logger.info("模拟生成京东商品base64海报");return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());}
}
  • 3.测试使用
    @Testpublic void test_NetMall() {NetMall netMall = new JDNetMall("1000001","*******");String base64 = netMall.generateGoodsPoster("https://item.jd.com/100008348542.html");logger.info("测试结果:{}", base64);}

访问者模式

  • 访问者要解决的核心事项是,在一个稳定的数据结构下,例如用户信息、雇员信息等,增加易变的业务访问逻辑
  • 1.新建一个用户抽象类user,和两个对应实现类老师Teacher、学生Student
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;}// 核心访问方法public abstract void accept(Visitor visitor);
}public class Teacher extends User {public Teacher(String name, String identity, String clazz) {super(name, identity, clazz);}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();}
}public class Student extends User {public Student(String name, String identity, String clazz) {super(name, identity, clazz);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}/*** 排名* @return*/public int ranking() {return (int) (Math.random() * 100);}
}
  • 2.新建一个访问者接口Visitor,以及对应的两个实现类校长Principal和家长Parent
public interface Visitor {// 访问学生信息void visit(Student student);// 访问老师信息void visit(Teacher teacher);}public class Principal implements Visitor {private Logger logger = LoggerFactory.getLogger(Principal.class);public void visit(Student student) {logger.info("学生信息 姓名:{} 班级:{}", student.name, student.clazz);}public void visit(Teacher teacher) {logger.info("学生信息 姓名:{} 班级:{} 升学率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());}}public class Parent implements Visitor {private Logger logger = LoggerFactory.getLogger(Parent.class);public void visit(Student student) {logger.info("学生信息 姓名:{} 班级:{} 排名:{}", student.name, student.clazz, student.ranking());}public void visit(Teacher teacher) {logger.info("老师信息 姓名:{} 班级:{} 级别:{}", teacher.name, teacher.clazz, teacher.identity);}}
  • 3.数据看板DataView
public class DataView {List<User> userList = new ArrayList<User>();public DataView() {userList.add(new Student("谢飞机", "重点班", "一年一班"));userList.add(new Student("windy", "重点班", "一年一班"));userList.add(new Student("大毛", "普通班", "二年三班"));userList.add(new Student("Shing", "普通班", "三年四班"));userList.add(new Teacher("BK", "特级教师", "一年一班"));userList.add(new Teacher("娜娜Goddess", "特级教师", "一年一班"));userList.add(new Teacher("dangdang", "普通教师", "二年三班"));userList.add(new Teacher("泽东", "实习教师", "三年四班"));}// 展示public void show(Visitor visitor) {for (User user : userList) {user.accept(visitor);}}
}
  • 4.测试验证,不同视角下展示的数据不一致
    @Testpublic void test(){DataView dataView = new DataView();logger.info("\r\n家长视角访问:");dataView.show(new Parent());     // 家长logger.info("\r\n校长视角访问:");dataView.show(new Principal());  // 校长}

解释器模式。

最后大哥图片镇楼
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/299273.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Allavsoft for Mac v3.27.0.8852注册激活版 优秀的视频下载工具

Allavsoft for Mac是一款功能强大的多媒体下载和转换工具&#xff0c;支持从各种在线视频网站和流媒体服务下载视频、音频和图片。它具备批量下载和转换功能&#xff0c;可将文件转换为多种格式&#xff0c;以适应不同设备的播放需求。此外&#xff0c;Allavsoft还提供视频编辑…

彩虹易支付接口配置

支付通道配置 基本概念 彩虹易支付系统有强大的支付接口扩展能力&#xff0c;首先需要明白以下几个概念。 支付方式&#xff1a; 支付方式用于定义发起支付的调用值&#xff08;在前台开发文档里面显示&#xff09;与支付方式名称。目前系统自带6种支付方式&#xff0c;它们的…

【C++】map set 底层刨析

文章目录 1. 红黑树的迭代器2. 改造红黑树3. map 的模拟实现4. set 的模拟实现 在 C STL 库中&#xff0c;map 与 set 的底层为红黑树&#xff0c;那么在不写冗余代码的情况下使用红黑树同时实现 map 与 set 便是本文的重点。 1. 红黑树的迭代器 迭代器的好处是可以方便遍历&…

openGauss学习笔记-255 openGauss性能调优-使用Plan Hint进行调优-Hint的错误、冲突及告警

文章目录 openGauss学习笔记-255 openGauss性能调优-使用Plan Hint进行调优-Hint的错误、冲突及告警 openGauss学习笔记-255 openGauss性能调优-使用Plan Hint进行调优-Hint的错误、冲突及告警 Plan Hint的结果会体现在计划的变化上&#xff0c;可以通过explain来查看变化。 …

Flutter Don‘t use ‘BuildContext‘s across async gaps.

Flutter提示Don‘t use ‘BuildContext‘s across async gaps.的解决办法—flutter里state的mounted属性

JAVAEE之Spring Boot日志

1. 日志概述 1.1 学习日志的原因 ⽇志对我们来说并不陌生, 从JavaSE部分, 我们就在使用 System.out.print 来打印日志了. 通过打印日志来发现和定位问题, 或者根据日志来分析程序的运行过程. 在Spring的学习中, 也经常根据控制台的日志来分析和定位问题. 随着项⽬的复杂…

【图论】【分类讨论】LeetCode3017按距离统计房屋对数目

本文涉及的知识点 图论 分类讨论 本题同解 【差分数组】【图论】【分类讨论】【整除以2】3017按距离统计房屋对数目 LeetCode3017按距离统计房屋对数目 给你三个 正整数 n 、x 和 y 。 在城市中&#xff0c;存在编号从 1 到 n 的房屋&#xff0c;由 n 条街道相连。对所有 …

Python 基于列表实现的通讯录管理系统(有完整源码)

目录 通讯录管理系统 PersonInformation类 ContactList类 menu函数 main函数 程序的运行流程 完整代码 运行示例 通讯录管理系统 这是一个基于文本的界面程序&#xff0c;用户可以通过命令行与之交互&#xff0c;它使用了CSV文件来存储和读取联系人信息&#xff0c;这…

Docker实战教程 第1章 Linux快速入门

2-1 Linux介绍 为什么要学Linux 三个不得不学习 课程需要&#xff1a;Docker开发最好在Linux环境下。 开发需要&#xff1a;作为一个后端程序员&#xff0c;是必须要掌握Linux的&#xff0c;这是找工作的基础门槛。 运维需要&#xff1a;在服务器端&#xff0c;主流的大型服…

Java栈和队列的实现

目录 一.栈(Stack) 1.1栈的概念 1.2栈的实现及模拟 二.队列(Queue) 2.1队列的概念 2.2队列的实现及模拟 2.3循环队列 2.4双端队列&#xff08;Deque&#xff09; 一.栈(Stack) 1.1栈的概念 栈:一种特殊的线性表&#xff0c;其 只允许在固定的一端进行插入和删除元素操作…

element-ui result 组件源码分享

今日简单分享 result 组件的源码实现&#xff0c;主要从以下三个方面&#xff1a; 1、result 组件页面结构 2、result 组件属性 3、result 组件 slot 一、result 组件页面结构 二、result 组件属性 2.1 title 属性&#xff0c;标题&#xff0c;类型 string&#xff0c;无默…

11-1(2)-CSS 背景+CSS 精灵图

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 一、CSS 背景1 背景颜色2 背景色半透明3 背景图片4 背景平铺5 背景图片位置6 …

linux操作系统安装及命令初识,上岸蚂蚁金服

310 包&#xff09; desktop 1800个包左右 内容必须大于 768M 系统设置 分区设置 挂载点 /boot / swap 交换分区–占用磁盘容量 网络配置 网卡配置 设置为ON 主机名配置 Begin installation 设置 root 用户密码 命令初识 命令 选项 参数&#xff1a; 命令选项参数…

JavaEE 初阶篇-生产者与消费者模型(线程通信)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 生产者与消费者模型概述 2.0 在生产者与消费者模型中涉及的关键概念 2.1 缓冲区 2.2 生产者 2.3 消费者 2.4 同步机制 2.5 线程间通信 3.0 实现生产者与消费者模…

iOS开发进阶(十三):脚手架创建iOS项目

文章目录 一、前言二、xcode-select 命令三、拓展阅读 一、前言 项目初期&#xff0c;需要搭建项目基本框架&#xff0c;为此离不开辅助工具&#xff0c;即脚手架。当然&#xff0c;IDE也可以实现新建空白项目&#xff0c;但是其新建后的项目结构可能不符合预期设计&#xff0…

AWS-EKS 给其他IAM赋予集群管理权限

AWS EKS 设计了权限管理系统&#xff0c;A用户创建的集群 B用户是看不到并且不能管理和使用kubectl的&#xff0c;所以我们需要共同管理集群时就需要操场共享集群访问给其他IAM用户。 两种方式添加集群控制权限&#xff08;前提&#xff1a;使用有管理权限的用户操作&#xff…

顺序表相关习题

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

Java笔试题总结

HashSet子类依靠()方法区分重复元素。 A toString(),equals() B clone(),equals() C hashCode(),equals() D getClass(),clone() 答案:C 解析: 先调用对象的hashcode方法将对象映射为数组下标,再通过equals来判断元素内容是否相同 以下程序执行的结果是&#xff1a; class X{…

【项目实战经验】DataKit迁移MySQL到openGauss(上)

前言 本文将分享DataKit迁移MySQL到openGauss的项目实战&#xff0c;供广大openGauss爱好者参考。 1. 下载操作系统 https://www.openeuler.org/zh/download https://support.huawei.com/enterprise/zh/doc/EDOC1100332931/1a643956 https://support.huawei.com/enterprise…

CSS基础选择器 小案例复习(画三个小盒子)

&#xff08;大家好&#xff0c;前面我们学习了基础的选择器&#xff0c;俗话说&#xff1a;温故而知新。所以今天我们将通过小案例来复习前面学过的小知识点。另&#xff0c;十分感谢大家对我文章的支持❤️&#xff09; 通过这个案例复习两个地方&#xff1a; 类选择器的使用…