装饰器模式实现协议转换器

装饰器模式实现协议转换器

序言简谈

在简述装饰器模式之前分享一个小案例,小编在一次项目过程中遇到这么一个问题,当我们系统存在多个单一模块系统业务时,并且系统之前有通用依赖的协议配置,如果每一个业务系统都重复写相关的协议转换的代码是没有任何问题,但是随着业务的复杂程度的增加,我们重复书写相关协议的代码就会增加。所以为了优化这一类问题,我想到了可以使用装饰器模式结合转换器的逻辑实现。

为何使用这两种模式的组合呢?因为对于配置类的相关转换逻辑并不存在复杂逻辑的情况,一般就类似于对象数据拷贝的原理。所以我们只需要确定好单一配置类转换为其对应的协议类逻辑即可。所以我引入了一个对象转换器的概念。对于使用装饰器模式来优化代码的原因在于,如果我们的配置转换成协议的过程中存在对象嵌套的情况,为了单一的协议转换原则,我们只需要将各自协议转换的逻辑实现,不必关心其协议嵌套的问题。因为在装饰器模式下,可以实现原有转换器逻辑,并在此基础上增加其他转换逻辑。

上述这种模式在JAVA IO相关类上就有其具体的表现形式,比如BufferedReader其内部就维护着一个Reader对象。


什么是装饰器模式

装饰器模式(Decorator Pattern)允许想一个现有的对象添加新的功能,同时又不改变其结构。这种设计模式属于结构型模式,它是作为现有的类的一种包装。这种模式创建了一个装饰器类,用来包装原有的类,并在保持方法签名完整性的前提下,提供了额外的功能。

1、介绍

下面我们将以google protobuf通信协议作为例子,将我们java配置类转换为protobuf协议数据。

1.1、转换实现

我们需要一个装换器接口Converter、需要一个抽象转换器装饰器AbstractConverterDecorator其类图如下
装饰器类图

1.2、protobuf结构与java配置类

奖励物品信息协议:

message Pro_GoodsInfo{
     required int32 	typeId		= 1;
	 required int32 	goodsId 	= 2; 
	 required int64 	num			= 3;
}

小镇结构信息

message Pro_FantasyTownCfg{
	required int32 id = 1;//
	required int32 level = 2;//
	optional int32 type = 3;//
	optional int64 reward = 4;//
	optional int64 needPoint = 5;//
	repeated Pro_GoodsInfo dailyRewardGoodInfo = 6;
}

注意事项:

上述小镇协议包含了多个奖励协议

JAVA配置类信息:

奖励类

public class SignInRewardCfg {
	private int id;
	private int type;
	private long num;
	// get set
}

小镇配置类

public class FantasyTownDetailCfg {
    private int id;
    private int type;
    private int level;
    private long needPoint;
    private long reward;
    private List<SignInRewardCfg> dailyRewardGoodInfo;
	// get set
}

1.3、装饰器模式实现

1.3.1、转换器接口Converter
/**
 * 转换器:
 *      转换器将类型S的源对象转换为类型T的目标。
 *      这个接口的实现是线程安全的,并且可以共享。
 *
 * @author spring
 * @date 2023/03/21
 */
@FunctionalInterface
public interface Converter<S, T> {

    /**
     * 将类型S的源对象转换为目标类型T
     *
     * @param source 要转换的源对象,必须的不能为空
     * @return {@link T} 转换后的对象,可能为空
     */
    T convert(S source);


    /**
     * 将类型S的源对象转换为目标类型T
     *
     * @param sourceList 要转换的源对象,必须的不能为空
     * @return {@link List}<{@link T}> 转换后的对象,可能为空
     */
    default List<T> convert(List<S> sourceList) {
        List<T> list = new ArrayList<>();
        for (S s : sourceList) {
            T convert = this.convert(s);
            list.add(convert);
        }
        return list;
    }

}
1.3.2、抽象的协议转换装饰器AbstractConverterDecorator
/**
 * 抽象转换器装饰器
 *      该类有四个泛型参数:
 *          S:表示当前装饰器的源对象
 *          T:表示当前装饰器的转换对象
 *          DS:表示子级元素的源对象
 *          DT:表示子级元素转换后的对象
 * @author spring
 * @date 2023/03/21
 */
public abstract class AbstractConverterDecorator<S, T, DS, DT> implements Converter<S, T> {

    /**
     * 装饰的转换器
     */
    protected Converter<DS, DT> converter;

    public AbstractConverterDecorator(Converter<DS, DT> converter) {
        this.converter = converter;
    }

    /**
     * 将类型S的源对象转换为目标类型T
     *
     * @param source 要转换的源对象,必须的不能为空
     * @return {@link T} 转换后的对象,可能为空
     */
    @Override
    public abstract T convert(S source);

}
1.3.3、奖励转换器SignInRewardCfgConverter
/**
 * 奖励cfg转换器
 *
 * @author spring
 * @date 2023/03/21
 */
public class SignInRewardCfgConverter implements Converter<SignInRewardCfg, Pro_GoodsInfo> {

    /**
     * 将类型S的源对象转换为目标类型T
     *
     * @param source 要转换的源对象,必须的不能为空
     * @return {@link Pro_GoodsInfo} 转换后的对象,可能为空
     */
    @Override
    public Pro_GoodsInfo convert(SignInRewardCfg source) {
        Pro_GoodsInfo.Builder builder = Pro_GoodsInfo.newBuilder();
        builder.setNum(source.getNum()).setGoodsId(source.getId()).setTypeId(source.getType());
        return builder.build();
    }

}
1.3.4、小镇转换器FantasyTownDetailCfgDecorator
/**
 * 小镇配置转换器
 *
 * @author spring
 * @date 2023/03/21
 */
public class FantasyTownDetailCfgDecorator extends AbstractConverterDecorator<FantasyTownDetailCfg, Pro_FantasyTownCfg, SignInRewardCfg, Pro_GoodsInfo> {

    public FantasyTownDetailCfgDecorator(Converter<SignInRewardCfg,Pro_GoodsInfo> converter) {
        super(converter);
    }

    /**
     * 将类型S的源对象转换为目标类型T
     *
     * @param source 要转换的源对象,必须的不能为空
     * @return {@link Pro_FantasyTownCfg} 转换后的对象,可能为空
     */
    @Override
    public Pro_FantasyTownCfg convert(FantasyTownDetailCfg source) {
        Pro_FantasyTownCfg.Builder builder = Pro_FantasyTownCfg.newBuilder();
        builder.setId(source.getId());
        builder.setLevel(source.getLevel());
        builder.setReward(source.getReward());
        List<SignInRewardCfg> dailyRewardGoodInfo = source.getDailyRewardGoodInfo();
        if(ObjectUtils.isNotEmpty(dailyRewardGoodInfo) && ObjectUtils.nonNull(converter)){
            List<Pro_GoodsInfo> goodsInfoList = converter.convert(dailyRewardGoodInfo);
            builder.addAllDailyRewardGoodInfo(goodsInfoList);
        }
        return builder.build();
    }

}
1.3.5、书写一个统一的转换器管理类ProtoConverters方便管理协议转换器
/**
 * 转换器集:
 *     主要用于管理转换协议的转换器
 *
 * @author spring
 * @date 2023/03/17
 */
public class ProtoConverters {

    /**
     * 构建
     *
     * @return {@link ProtoConverters}
     */
    public static ProtoConverters build() {
        return new ProtoConverters();
    }

    /**
     * 奖励cfg转换成Pro_GoodsInfo
     *
     * @param sign 标志
     * @return {@link Pro_GoodsInfo}
     */
    public Pro_GoodsInfo signInRewardToGoodsInfo(SignInRewardCfg sign) {
        return new SignInRewardCfgConverter().convert(sign);
    }

    /**
     * 列表转换形式:奖励cfg转换成Pro_GoodsInfo
     *
     * @param list 列表
     * @return {@link List}<{@link Pro_GoodsInfo}>
     */
    public List<AiwanPrivateMsgV2.Pro_GoodsInfo> signInRewardToGoodsInfo(List<SignInRewardCfg> list) {
        return new SignInRewardCfgConverter().convert(list);
    }

    /**
     * 小镇建筑详情
     *
     * @param detailCfgList 详细cfg清单
     * @return {@link List}<{@link Pro_FantasyTownCfg}>
     */
    public List<Pro_FantasyTownCfg> fantasyTownDetailListToProtoInfo(List<FantasyTownDetailCfg> detailCfgList) {
        return new FantasyTownDetailCfgDecorator(new SignInRewardCfgConverter()).convert(detailCfgList);
    }

    /**
     * 小镇建筑详情
     *
     * @param detailCfgList 详细cfg清单
     * @return {@link List}<{@link Pro_FantasyTownCfg}>
     */
    public List<Pro_FantasyTownCfg> fantasyTownDetailListToProtoNotRewardInfo(List<FantasyTownDetailCfg> detailCfgList) {
        return new FantasyTownDetailCfgDecorator(null).convert(detailCfgList);
    }

}
1.3.6、测试
/**
 * 转换测试
 *
 * @author spring
 * @date 2023/03/22
 */
public class ConvertTest {

    public static void main(String[] args) {
        // 准备好测试数据
        List<SignInRewardCfg> goodsList = Arrays.asList(new SignInRewardCfg(0, 12, 100), new SignInRewardCfg(0, 6, 100));
        List<FantasyTownDetailCfg> detailCfgList = Arrays.asList(
                new FantasyTownDetailCfg(1, 1, 1, 100, 200, goodsList),
                new FantasyTownDetailCfg(2, 1, 2, 100, 200, goodsList),
                new FantasyTownDetailCfg(3, 1, 3, 100, 200, goodsList),
                new FantasyTownDetailCfg(4, 1, 4, 100, 200, goodsList),
                new FantasyTownDetailCfg(5, 1, 5, 100, 200, goodsList),
                new FantasyTownDetailCfg(6, 1, 6, 100, 200, goodsList)
                );

        // 使用统一转换器将小镇配置转为有奖励的协议信息
        List<AiwanPrivateMsgV2.Pro_FantasyTownCfg> townCfgList = ProtoConverters.build().fantasyTownDetailListToProtoInfo(detailCfgList);
        // 使用统一转换器将小镇配置转换为没有建立的协议信息
        List<AiwanPrivateMsgV2.Pro_FantasyTownCfg> exTownCfgList = ProtoConverters.build().fantasyTownDetailListToProtoNotRewardInfo(detailCfgList);
        System.out.println();
    }

}

运行结果如下

包含奖励的转换:

包含奖励的小镇协议转换结果

不包含奖励的转换:

不包含奖励的小镇协议转换结果

至此我们就完成使用了装饰器模式实现协议转换器的设计。如果我们还有其他跟复杂的数据结构,依旧可以继承抽象装饰器实现各式各样装饰器的装饰处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值