深入netty之四模板方法模式在decode和encode中的應(yīng)用

任何的數(shù)據(jù)協(xié)議,只要是稱得上是協(xié)議,就會(huì)有固定的格式。
比如,如下的一個(gè)數(shù)據(jù)協(xié)議,應(yīng)該是一個(gè)相當(dāng)復(fù)雜的數(shù)據(jù)協(xié)議:

數(shù)據(jù)協(xié)議demo
數(shù)據(jù)協(xié)議demo

這個(gè)協(xié)議,可以用“包頭+包體+包尾”這樣個(gè)格式。其中,包頭和包尾的數(shù)據(jù)長(zhǎng)度是固定的,變化的只是包體長(zhǎng)度。
來解析或者編碼這樣一個(gè)數(shù)據(jù)協(xié)議,我們首先想到的是應(yīng)用模板方法模式,因?yàn)椴还馨w的解析怎么變,包頭和包尾的解析是不變的,因此,我們可以把對(duì)包頭和包尾的解析放到父類里,而把對(duì)包體的解析延遲到子類完成?;谶@樣一個(gè)編程思路,就是模板方法模式的應(yīng)用了。
下面來看它的具體實(shí)現(xiàn):
首先是解析包頭-

@Slf4j
public abstract class CashboxBaseDecoder {

    //只給讀寫錢箱參數(shù)命令返回解析使用
    @Setter
    private CashboxParamHandleType handleType;

    @Setter
    private CashboxCommandType commandType;
     //已經(jīng)獲取了7個(gè)字節(jié)
    private CashboxBaseData decodeHeader(ByteBuf buffer) {
        log.info("開始解析數(shù)據(jù)包頭...");
        logByteBuf(buffer, log);
        CashboxBaseData result;
        CashboxBaseData baseData = new CashboxBaseData();
        baseData.setBeginDLE(buffer.getByte(0));
        baseData.setStx(buffer.getByte(1));
        baseData.setLength(buffer.getShortLE(2));
        baseData.setType(this.commandType);
        //解析返回結(jié)果和錯(cuò)誤碼
        CashboxBaseResultData cashboxBaseResultData = new CashboxBaseResultData(baseData);
        cashboxBaseResultData.setResult(getById(buffer.getByte(4)).get());
        cashboxBaseResultData.setErrorCode(buffer.getShortLE(5));
        result = cashboxBaseResultData;
        return result;
    }

值得注意的是,在解析包頭的方法decodeHeader中,除了解析了包頭的4個(gè)字節(jié)以外,還解析了包體的3個(gè)字節(jié),因?yàn)榘w開頭的3個(gè)字節(jié)-結(jié)果和錯(cuò)誤碼,也在每一個(gè)數(shù)據(jù)包中是一樣的,所以,雖然這3個(gè)字節(jié)在包體中,我們?nèi)匀话阉诺搅税^中解析。
接著解析包尾-

private void decodeTail(CashboxBaseData baseData, ByteBuf buffer) throws DataAlteredException{
    log.info("開始解析數(shù)據(jù)包尾...");
    int beginPort = 4 + baseData.getLength();
    baseData.setId(buffer.getMediumLE(beginPort));
    baseData.setEndDLE(buffer.getByte(beginPort + 3));
    baseData.setEtx(buffer.getByte(beginPort + 4));
    baseData.setCrc(buffer.getUnsignedShortLE(beginPort + 5));
    System.out.println(getCheckCode(buffer, baseData.getLength()));
    if (baseData.getCrc() != getCheckCode(buffer, baseData.getLength())) {
        throw new DataAlteredException(ErrorCode.ERROR_PROTO_CHECKSUM, "CRC校驗(yàn)不正確!");
    }
}

包尾解析7個(gè)字節(jié),任何數(shù)據(jù)包都一樣。最后,做一下CRC校驗(yàn)。
對(duì)包體的解析在父類里無法實(shí)現(xiàn),抽象到子類中實(shí)現(xiàn)-

protected abstract CashboxBaseData decodeBody(CashboxBaseData baseData, ByteBuf buffer, CashboxParamHandleType handleType);

最后是對(duì)整個(gè)數(shù)據(jù)包的解析,先調(diào)用對(duì)包頭的解析,然后把結(jié)果賦給對(duì)包體解析的方法-

public final CashboxBaseData decode(ByteBuf buffer) throws DataAlteredException{
    CashboxBaseData baseData = decodeBody(decodeHeader(buffer), buffer, handleType);

后調(diào)用對(duì)包尾的解析-

    decodeTail(baseData, buffer);
        log.debug("解析結(jié)果, 公共數(shù)據(jù):"+baseData.toString());
        return baseData;
    }
}

這就完成了對(duì)父類的編碼。
下面,我們來看看子類的實(shí)現(xiàn)。
第一個(gè)子類是“初始化結(jié)果返回”:

@Slf4j
public class CashboxInitialiseResultDecoder extends CashboxBaseDecoder{
    @Override
    protected CashboxBaseData decodeBody(CashboxBaseData baseData, ByteBuf buffer, CashboxParamHandleType handleType) {
        log.info("進(jìn)入初始化結(jié)果返回?cái)?shù)據(jù)解析...");
        return baseData;
    }
}

初始化結(jié)果返回的包體沒有任何內(nèi)容。
下面一個(gè)子類是“獲取固件版本結(jié)果返回”:

@Slf4j
public class CashboxInitialiseResultDecoder extends CashboxBaseDecoder{
    @Override
    protected CashboxBaseData decodeBody(CashboxBaseData baseData, ByteBuf buffer, CashboxParamHandleType handleType) {
        log.info("解析固件版本號(hào)結(jié)果...");
        CashboxVersionReadResultData data = new CashboxVersionReadResultData((CashboxBaseResultData) baseData);
        data.setVersion(buffer.getCharSequence(RESULT_PORT_BEGIN, data.getLength() - SIZE_OF_DATA_4_CODE, Charset.forName("utf-8")).toString());
        return data;
    }
}

獲取固件版本號(hào)的包體里只有一個(gè)數(shù)據(jù),就是版本號(hào),是字符串形式的。
以上就是整個(gè)數(shù)據(jù)包解析應(yīng)用模板方法模式的解決過程,其類圖如下所示:

類圖
類圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容