策略模式-基础篇

策略模式,对象行为型

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值