策略模式,对象行为型
1. 概念
策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口 和 具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略。本模式使得算法可独立于使用它的用户而变化
2. 结构
Strategy: 抽象策略类:策略是一个接口,该接口定义若干个算法标识,即定义了若干个抽象方法(如下图的FrameCodec())
Context: 环境类 /上下文类:上下文是依赖于接口的类(是面向策略设计的类,如下图Context类),即上下文包含用策略(接口)声明的变量(如下图的strategy成员变量)。上下文提供一个方法(如下图Context类中的的ProtocolService698Impl()方法),持有一个策略类的引用,最终给客户端调用。该方法委托策略变量调用具体策略所实现的策略接口中的方法(实现接口的类重写策略(接口)中的方法,来完成具体功能)
ConcreteStrategy: 具体策略类:具体策略是实现策略接口的类(如下图的ApduData类和Frame698类)。具体策略实现策略接口所定义的抽象方法,即给出算法标识的具体方法。(说白了就是重写策略类的方法!)
抽象类策略
这里还用到了《泛型》,在大量使用这个接口的时候,可以避免进行类型的强制转换,提高代码的运行效率。通读效果也更好
package com.hzwq.protocol.basic;
import java.nio.ByteBuffer;
/**
* 协议编解码接口
*/
public interface FrameCodec<T> {
/**
* 对协议对象进行编码,获取协议对象的二进制字节
* @return 二进制字节
*/
ByteBuffer encodeBytes();
/**
* 对二进制字节进行解码,封装协议对象
* @param unDecode 二进制字节
* @return 协议对象
*/
T decodeBytes(ByteBuffer unDecode);
}
具体实现类
package com.hzwq.protocol.elec698.data;
import java.nio.ByteBuffer;
import java.util.Arrays;
import com.hzwq.protocol.basic.FrameCodec;
import com.hzwq.protocol.basic.TypeParser;
import com.hzwq.protocol.elec698.exception.Frame698Exception;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@NoArgsConstructor
public class ApduData implements FrameCodec<ApduData> {
@Getter
private ApduDataType type;
private ApduDataValue value;
public ApduData(ApduDataValue value) {
this.value = value;
ApduDataType methodType = Arrays.stream(ApduDataType.values())
.filter(type -> type.getImplClz().equals(value.getClass())).findAny().get();
this.type = methodType;
}
@Override
public ByteBuffer encodeBytes() {
byte typeCode = (byte) type.getCode();
ByteBuffer content = value.encodeBytes();
ByteBuffer full = ByteBuffer.allocate(1+content.remaining());
full.put(typeCode);
full.put(content);
full.flip();
return full;
}
@Override
public ApduData decodeBytes(ByteBuffer unDecode) {
int typeCode = unDecode.get() & 0xff;
ApduDataType type = TypeParser.getType(ApduDataType.values(),typeCode);
this.type = type;
try {
this.value = type.getImplClz().newInstance().decodeBytes(unDecode);
} catch (InstantiationException | IllegalAccessException e) {
throw new Frame698Exception("APDU data 解析失败",e);
}
return this;
}
public Object getValue(){
if(this.value instanceof ApduDataBizValue){
return ((ApduDataBizValue<?>)this.value).getValue();
} else {
return this.value;
}
}
protected void setType(ApduDataType type){
this.type = type;
}
protected void setValue(ApduDataValue value){
this.value = value;
}
public String toString() {
return "ApduData(type=" + this.type + ", value=" + this.value + ")";
}
}
package com.hzwq.protocol.elec698;
import java.nio.ByteBuffer;
import java.util.Arrays;
import com.hzwq.protocol.basic.FrameCodec;
import com.hzwq.protocol.elec698.exception.Frame698Exception;
import com.hzwq.protocol.elec698.util.CrcUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* 698协议数据链路层帧头与APDU部分
*/
@ToString
public class Frame698Header implements FrameCodec<Frame698Header> {
/** 内容长度,2字节,传输帧中除起始字符和结束字符之外的帧字节数 */
private int length;
/** 控制域,1字节 */
private HeaderControl control;
/** 服务器地址 */
@Getter
private SA sa;
/** 客户机地址 */
@Getter
@Setter
private int clientAddr;
/** 帧头校验,2字节 */
private byte[] hcs;
/** APDU长度 */
@Setter
private int apduLength;
private Frame698Header() {}
/**
* 通过解码字节值创建对象
*/
public static Frame698Header createByDecode(ByteBuffer unDecode){
return new Frame698Header().decodeBytes(unDecode);
}
/**
* 创建帧头与APDU对象,默认不进行分帧与扰码,客户机地址为0
* @param sa 服务器地址对象
* @param apdu APDU对象
*/
public Frame698Header(SA sa,HeaderControl control) {
this.sa = sa;
this.control = control;
}
@Override
public ByteBuffer encodeBytes() {
// SA
ByteBuffer saBytes = sa.encodeBytes();
int saLength = saBytes.remaining();
// 控制域
ByteBuffer ctrlBytes = control.encodeBytes();
// 内容长度
this.length = this.apduLength + saLength + 8;
// 长度域
byte[] lenBytes = new byte[2];
lenBytes[0] = (byte)(this.length & 0xff);
lenBytes[1] = (byte)((this.length >>> 8) & 0x3f);
// 帧头部分
int headLen = saLength + 6;
ByteBuffer headBuffer = ByteBuffer.allocate(headLen);
headBuffer.put(lenBytes);
headBuffer.put(ctrlBytes);
headBuffer.put(saBytes);
headBuffer.put((byte)this.clientAddr);
// 帧头校验值 HCS
byte[] headBytes = headBuffer.array();
CrcUtil.appendFcs(headBytes);
// 返回结果
headBuffer.position(headBuffer.capacity());
headBuffer.flip();
return headBuffer;
}
@Override
public Frame698Header decodeBytes(ByteBuffer unDecode) {
// 长度域,14、15位是保留位, 仅取0-13位, 低位字节对应0-7位, 高位字节包含8-13位
byte lenLowByte= unDecode.get(0);
byte lenHighByte= unDecode.get(1);
this.length = ((lenHighByte & 0x3f) << 8) | (lenLowByte & 0xff);
// 服务器地址定义
byte addrDefByte = unDecode.get(3);
this.sa = SA.createByDecodeSAFlag(addrDefByte);
// 服务器地址长度
int saLength = this.sa.getLength();
// 获取帧头部分
int headLen = 7+saLength;
byte[] headBytes = new byte[headLen];
unDecode.get(headBytes);
// 帧头校验
boolean fcsPass = CrcUtil.checkFcs(headBytes);
if(!fcsPass){
throw new Frame698Exception(Arrays.toString(headBytes)+",HCS校验失败");
}
// 控制域
this.control = HeaderControl.createByDecode(headBytes[2]);
// 服务器地址
byte[] addrBytes = new byte[saLength];
System.arraycopy(headBytes, 4, addrBytes, 0, saLength);
this.sa.decodeServerAddr(addrBytes);
// 客户机地址
this.clientAddr = headBytes[4+saLength] & 0xff;
// HCS
this.hcs = new byte[2];
System.arraycopy(headBytes, headLen-2, this.hcs, 0, 2);
return this;
}
}
上下文类
也叫做上下文类或环境类,起承上启下封装作用。我的理解就是,你最终要用上面实现功能的
package com.hzwq.protocol.service.impl;
import java.nio.ByteBuffer;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.hzwq.protocol.service.ProtocolService;
public class ProtocolService698Impl implements ProtocolService {
@Override
public String encodedData(String params) {
// 入参反序列化,params 解析为json对象
JSONObject json = JSON.parseObject(params);
// 从json对象中获取构造协议对象所需参数
JSONObject function = json.getJSONArray("functions").getJSONObject(0);
// 构造APDU
JSONObject apduJson = function.getJSONObject("apdu");
Frame698Apdu apdu = ApduMethodBuilder.build(apduJson);
// 构造整帧
JSONObject headerJson = function.getJSONObject("header");
Frame698 frame698 = buildFrame(headerJson, apdu);
// 调用协议对象编码方法,生成字节值
ByteBuffer buf = frame698.encodeBytes();
// // 将字节值转换为16进制字符串
String hex = BytesUtil.bytesToHex(buf.array());
// 封装返回对象
JSONObject result = new JSONObject();
result.put("code", "200");
result.put("data", hex);
return result.toJSONString();
}
}
3. 策略模式优缺点
1)优点
策略模式提供了对“开闭原则”的完美支持,用户可以在不 修改原有系统的基础上选择算法或行为,也可以灵活地增加 新的算法或行为。
策略模式提供了管理相关的算法族的办法。
策略模式提供了可以替换继承关系的办法。
使用策略模式可以避免使用多重条件转移语句。
2)缺点
客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
策略模式将造成产生很多策略类,可以通过使用享元模式在一 定程度上减少对象的数量。
4. 策略模式适用场景
在以下情况下可以使用策略模式:
如果在一个系统里面有许多类,它们之间的区别仅在于它们 的行为,那么使用策略模式可以动态地让一个对象在许多行 为中选择一种行为。
一个系统需要动态地在几种算法中选择一种。
如果一个对象有很多的行为,如果不用恰当的模式,这些行 为就只好使用多重的条件选择语句来实现。
不希望客户端知道复杂的、与算法相关的数据结构,在具体 策略类中封装算法和相关的数据结构,提高算法的保密性与 安全性。
原文链接:https://blog.youkuaiyun.com/qq_54773252/article/details/121032404