JAVA设计模式 - 策略模式

文章介绍了如何通过策略模式来封装一系列针对考勤设备的操作,如新增、删除用户,重启设备等,实现不同设备操作的接口抽象。具体通过创建一个抽象的第三方设备类以及具体的设备实现类(如丹航设备),并利用Spring上下文获取依赖,将设备命令转换为MQ消息进行发送。这样,在接入新设备时,只需新增设备实现类,无需修改原有代码,提高了代码的可维护性和扩展性。
摘要由CSDN通过智能技术生成
简介

定义一系列的算法,把它们一个个封装起来,并且使他们可相互替换。本模式使得算法的变化可以独立于使用它的客户。

实际项目中使用(部分代码)

业务场景:通过页面功能按钮操作下发相应的指令到考勤设备(下发/获取/修改/删除人员或者考勤信息、经纬度信息、打卡范围、重启等等)。目前系统已有一种考勤(存储设备的指令到表里,定时任务获取未执行的该设备指令进行发送),现在准备接入一款新的考勤设备。

代码实现
/**
 * 抽象第三方设备类
 *
 * @Author htl
 */
public abstract class AbstractThirdPartyEquipment {
    /**
     * 通过spring上下文取得已实例化的bean
     */
    protected final BizProjectPersonInfoMapper bizProjectPersonInfoMapper = ApplicationContextUtil.getApplicationContext().getBean(BizProjectPersonInfoMapper.class);
    protected final BizProjectPersonInfoService bizProjectPersonInfoService = ApplicationContextUtil.getApplicationContext().getBean(BizProjectPersonInfoService.class);
    protected final BizEnterPrisePersonInfoMapper bizEnterPrisePersonInfoMapper = ApplicationContextUtil.getApplicationContext().getBean(BizEnterPrisePersonInfoMapper.class);
    protected final BizAttendanceSettingMapper bizAttendanceSettingMapper = ApplicationContextUtil.getApplicationContext().getBean(BizAttendanceSettingMapper.class);
    protected final PhysicalFileMapper physicalFileMapper = ApplicationContextUtil.getApplicationContext().getBean(PhysicalFileMapper.class);
    protected final FacePinPersonMapper facePinPersonMapper = ApplicationContextUtil.getApplicationContext().getBean(FacePinPersonMapper.class);
    protected final FaceServeLogMapper faceServeLogMapper = ApplicationContextUtil.getApplicationContext().getBean(FaceServeLogMapper.class);
    protected final FaceDeviceItemMapMapper faceDeviceItemMapMapper = ApplicationContextUtil.getApplicationContext().getBean(FaceDeviceItemMapMapper.class);
    protected final AmqpTemplate amqpTemplate = ApplicationContextUtil.getApplicationContext().getBean(AmqpTemplate.class);

    /**
     * 新增用户
     *
     * @param cmd
     */
    public abstract void addUser(FaceCommand cmd);

    /**
     * 删除用户
     *
     * @param cmd
     */
    public abstract void delUser(FaceCommand cmd);

    /**
     * 重启设备
     *
     * @param cmd
     */
    public abstract void reboot(FaceCommand cmd);

    /**
     * 获取用户信息
     *
     * @param cmd
     */
    public abstract void getUserDetails(FaceCommand cmd);

    /**
     * 修改用户
     *
     * @param cmd
     */
    public abstract void editUser(FaceCommand cmd);

    /**
     * 设置设备位置围栏
     *
     * @param cmd
     */
    public abstract void setFence(FaceCommand cmd);

}
/**
 * 丹航设备的实现
 *
 * @Author htl
 */
@Slf4j
public class DanNavigationEquipmentImpl extends AbstractThirdPartyEquipment {

    /**
     * 丹航指令常量
     */
    public final static String REBOOT = "reboot";
    public final static String ADDUSER = "addUser";
    public final static String DEL_USER = "delUser";
    public final static String EDIT_USER = "editUser";
    public final static String GET_USER_DETAIL = "getUserDetails";
    public final static String SET_FENCE = "setGeofence";

    @Override
    public void addUser(FaceCommand cmd) {
        //获取FacePinPerson的id,当中用户id
        List<FacePinPerson> pinPersonList = facePinPersonMapper.select(new FacePinPerson().setDeleted(false)
                .setPersonId(cmd.getPersonId()).setProPerId(cmd.getProPerId())
                .setProjectId(cmd.getProjectId()).setDeviceNo(cmd.getHandledDevice()));
        FacePinPerson facePinPerson = pinPersonList.get(0);
        FacePinPersonDTO dto = facePinPersonMapper.selectPersonInfo(facePinPerson);

        //图片完整路径 设备【验证模式】为【人脸或卡】时可以不传照片,非【人脸或卡】模式这个字段为必传
        String filePath = ApplicationContextUtil.getApplicationContext()
                .getBean(ApplicationProperties.class).getFileServer() + dto.getPersonImageAdr();
        //转base64进行下发
//        String faceTemplate = imageCoding(filePath);

        //组装MQ数据
        String jsonDataString = new JSONObject(ImmutableMap.of(
                "cmd", ADDUSER,
                "equipmentSn", dto.getDeviceNo(),
                "userId", dto.getId(),
                "faceTemplate", filePath,
                "name", dto.getName()
        )).toJSONString();
        sendMessage(jsonDataString, cmd.getId().toString());
        log.info("丹航设备:{},新增人员指令 -> MQ队列", cmd.getId());
    }

    @Override
    public void delUser(FaceCommand cmd) {
        //获取FacePinPerson的id,当中用户id
        List<FacePinPerson> pinPersonList = facePinPersonMapper.select(new FacePinPerson().setDeleted(false)
                .setPersonId(cmd.getPersonId()).setProPerId(cmd.getProPerId())
                .setProjectId(cmd.getProjectId()).setDeviceNo(cmd.getHandledDevice()));
        //MQ
        String jsonDataString = new JSONObject(ImmutableMap.of(
                "cmd", DEL_USER,
                "equipmentSn", cmd.getHandledDevice(),
                "userId", pinPersonList.get(0).getId()))
                .toJSONString();
        sendMessage(jsonDataString, cmd.getId().toString());
        log.info("丹航设备:{},删除人员指令 -> MQ队列", cmd.getId());
    }

    @Override
    public void reboot(FaceCommand cmd) {
        //MQ
        String jsonDataString = new JSONObject(ImmutableMap.of(
                "cmd", REBOOT,
                "equipmentSn", cmd.getHandledDevice()))
                .toJSONString();
        sendMessage(jsonDataString, cmd.getId().toString());
        log.info("丹航设备:{},重启指令 -> MQ队列", cmd.getId());
    }

    @Override
    public void getUserDetails(FaceCommand cmd) {
        //获取FacePinPerson的id,当中用户id
        List<FacePinPerson> pinPersonList = facePinPersonMapper.select(new FacePinPerson().setDeleted(false)
                .setPersonId(cmd.getPersonId()).setProPerId(cmd.getProPerId())
                .setProjectId(cmd.getProjectId()).setDeviceNo(cmd.getHandledDevice()));
        FacePinPerson facePinPerson = pinPersonList.get(0);
        //MQ
        String jsonDataString = new JSONObject(ImmutableMap.of(
                "cmd", GET_USER_DETAIL,
                "equipmentSn", cmd.getHandledDevice(),
                "userId", facePinPerson.getId()))
                .toJSONString();
        sendMessage(jsonDataString, cmd.getId().toString());
        log.info("丹航设备:{},读取用户数据指令 -> MQ队列", cmd.getId());
    }

    @Override
    public void editUser(FaceCommand cmd) {
        //获取FacePinPerson的id,当中用户id
        List<FacePinPerson> pinPersonList = facePinPersonMapper.select(new FacePinPerson().setDeleted(false)
                .setPersonId(cmd.getPersonId()).setProPerId(cmd.getProPerId())
                .setProjectId(cmd.getProjectId()).setDeviceNo(cmd.getHandledDevice()));
        if (pinPersonList.size() == 0) {
            return;
        }
        FacePinPerson facePinPerson = pinPersonList.get(0);
        FacePinPersonDTO dto = facePinPersonMapper.selectPersonInfo(facePinPerson);
        //图片完整路径
        String filePath = ApplicationContextUtil.getApplicationContext()
                .getBean(ApplicationProperties.class).getFileServer() + dto.getPersonImageAdr();
        //转base64进行下发
//        String faceTemplate = imageCoding(filePath);
        //组装MQ数据
        String jsonDataString = new JSONObject(ImmutableMap.of(
                "cmd", EDIT_USER,
                "equipmentSn", dto.getDeviceNo(),
                "userId", dto.getId(),
                "faceTemplate", filePath,
                "name", dto.getName()
        )).toJSONString();
        sendMessage(jsonDataString, cmd.getId().toString());
        log.info("丹航设备:{},修改人员指令 -> MQ队列", cmd.getId());
    }

    @Override
    public void setFence(FaceCommand cmd) {
        //获取项目考勤的经纬度及半径
        List<BizAttendanceSetting> attendanceSettings = bizAttendanceSettingMapper.select(new BizAttendanceSetting().setAttendanceProject(cmd.getProjectId()).setDeleted(false));
        if (attendanceSettings.size() > 0) {
            BizAttendanceSetting bizAttendanceSetting = attendanceSettings.get(0);
            String position = bizAttendanceSetting.getAttendancePositionCenter();
            Float attendanceRadius = bizAttendanceSetting.getAttendanceRadius();

            //解析经纬度, 保留小数点后8位数
            String[] positions = position.split(",");
            String[] latitudeStr = positions[0].split("\\.");
            String[] longitudeStr = positions[1].split("\\.");
            String latitude = positions[0];
            String longitude = positions[1];
            //判断是否存在小数点
            if (latitudeStr.length > 1 && latitudeStr[1].length() >= 8) {
                latitude = latitudeStr[0] + "." + latitudeStr[1].substring(0, 8);
            }
            if (longitudeStr.length > 1 && longitudeStr[1].length() >= 8) {
                longitude = longitudeStr[0] + "." + longitudeStr[1].substring(0, 8);
            }

            //组装MQ数据
            String jsonDataString = new JSONObject(ImmutableMap.of(
                    "cmd", SET_FENCE,
                    "equipmentSn", cmd.getHandledDevice(),
                    "latitude", latitude,
                    "longitude", longitude,
                    "radius", attendanceRadius
            )).toJSONString();
            sendMessage(jsonDataString, cmd.getId().toString());
            log.info("丹航设备:{},设置设备位置围栏指令 -> MQ队列", cmd.getId());

            //添加face_serve_log
            FaceServeLog faceServeLog = new FaceServeLog()
                    .setEquipment(cmd.getHandledDevice())
                    .setCommandId(cmd.getId())
                    .setSubmitDate(cmd.getCreatedDate())
                    .setTransmitDate(LocalDateTime.now())
                    .setCreatedDate(LocalDateTime.now())
                    .setDeleted(false)
                    .setSchedule("0")
                    .setContent("C:" + cmd.getHandledDevice() + ":DATA UPDATE SET_FENCE" +
                            " latitude=" + latitude + "longitude=" + longitude + "radius=" + attendanceRadius);
            faceServeLogMapper.insert(faceServeLog);
        }
    }

    /**
     * 发送消息
     *
     * @param jsonBody
     * @param cmdId
     */
    private void sendMessage(String jsonBody, String cmdId) {
        Message message = MessageBuilder.withBody( jsonBody.getBytes(StandardCharsets.UTF_8))
                .setContentType(MessageProperties.CONTENT_TYPE_JSON)
                .setMessageId(cmdId)
                .build();

        //MQ队列
        amqpTemplate.send(RabbitmqConstant.DH_FACE_CMD_RECEIVED_EXCHANGE,
                RabbitmqConstant.DH_FACE_CMD_RECEIVED_KEY, message);
    }

    /**
     * 丹航设备-照片编码
     *
     * @param filePath
     * @return
     */
    private String imageCoding(String filePath) {
        try (ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
             FileInputStream fileInputStream = new FileInputStream(filePath)) {
            byte[] bytes = new byte[1024];
            int len;
            while ((len = fileInputStream.read(bytes)) != -1) {
                //写出到字节数组中
                arrayOutputStream.write(bytes, 0, len);
            }
            arrayOutputStream.flush();
            byte[] byteArray = arrayOutputStream.toByteArray();
            //->base64 ->URLEncoder
            String str = "data:image/jpeg;base64," + new BASE64Encoder().encode(byteArray);
            log.info("-------------------{}------------------", "图片转换base64完成");
            return URLEncoder.encode(str, "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException("图片转换base64异常: " + e.getLocalizedMessage());
        }
    }
}
/**
 * 管理考勤机设备的实现类 - 策略管理类
 *
 * @Author htl
 */
public class ThirdPartyEquipmentManage {

    /**
     * key - 品牌  字典表管理, value - 自定义设备实现类
     */
    private static final HashMap<String, AbstractThirdPartyEquipment> PARTY_EQUIPMENT_MAP = new HashMap();
    public static final FaceDeviceItemMapMapper deviceItemMapMapper = ApplicationContextUtil.getApplicationContext().getBean(FaceDeviceItemMapMapper.class);

    //后续需要接入其他品牌的考勤机器,只需要编写好实现类,再到这进行初始化,就OK,不需要修改已有代码. 享元模式
    static {
        PARTY_EQUIPMENT_MAP.put("2", new DanNavigationEquipmentImpl());
    }

    /**
     * 根据SN获取设备,再根据设备的品牌,获取对应的处理类
     *
     * @param equipmentSn
     * @return
     */
    public static AbstractThirdPartyEquipment getEquipmentImpl(String equipmentSn) {
        FaceDeviceItemMap deviceItemMap = deviceItemMapMapper.selectOne(new FaceDeviceItemMap()
                .setEquipment(equipmentSn).setDeleted(false));
        if (Optional.ofNullable(deviceItemMap).isPresent()) {
            String brand = deviceItemMap.getBrand();
            if (PARTY_EQUIPMENT_MAP.containsKey(brand)) {
                return PARTY_EQUIPMENT_MAP.get(brand);
            }
        }
        return null;
    }
}
    /**
     * 定时任务读取指令,解析下发到MQ里
     */
    @Scheduled(cron = "0/10 * * * * ?")
    public void sendThirdPartyEquipmentCommand() {
        //获取到所有【非原来的设备】并且【未执行】的指令
        List<FaceCommand> faceCommandList = faceCommandMapper.selectThirdPartyEquipmentCommands();

        //区分SqlType
        if (Optional.ofNullable(faceCommandList).isPresent() && faceCommandList.size() > 0) {
            faceCommandList.parallelStream().forEach(cmd -> {
                List<FaceServeLog> serveLogList = faceServeLogMapper.select(new FaceServeLog()
                        .setCommandId(cmd.getId()).setDeleted(false));
                String sqlType = cmd.getSqlType();

                try {
                    //调用第三方设备处理类进行处理
                    AbstractThirdPartyEquipment equipmentImpl = ThirdPartyEquipmentManage.getEquipmentImpl(cmd.getHandledDevice());
                    if (Optional.ofNullable(equipmentImpl).isPresent()) {

                        if (SqlTypeEnum.INSERT.getValue().equals(sqlType)) {
                            equipmentImpl.addUser(cmd);
                        }

                        if (SqlTypeEnum.DELETE.getValue().equals(sqlType)) {
                            equipmentImpl.delUser(cmd);
                        }

                        if (SqlTypeEnum.REBOOT.getValue().equals(sqlType)) {
                            equipmentImpl.reboot(cmd);
                        }

                        if (SqlTypeEnum.QUERY.getValue().equals(sqlType)) {
                            equipmentImpl.getUserDetails(cmd);
                        }

                        if (SqlTypeEnum.UPDATE.getValue().equals(sqlType)) {
                            if (CMD_UPDATE.equals(cmd.getCmd())) {
                                equipmentImpl.editUser(cmd);
                            }

                            if (CMD_FENCE.equals(cmd.getCmd())) {
                                equipmentImpl.setFence(cmd);
                            }
                        }

                        //服务器日志
                        if (serveLogList.size() > 0) {
                            serveLogList.forEach(log -> {
                                log.setSchedule("0");
                                log.setTransmitDate(LocalDateTime.now());
                            });
                        }
                        //下发成功
                        cmd.setHandledRemark("[下发成功, 等待设备响应]");

                        //修改设备最近传送时间
                        List<FaceDeviceItemMap> devices = faceDeviceItemMapMapper.select(new FaceDeviceItemMap().setEquipment(cmd.getHandledDevice()).setDeleted(false));
                        if (devices.size() > 0) {
                            devices.get(0).setLastTransmitDate(LocalDateTime.now());
                            faceDeviceItemMapMapper.updateByPrimaryKeySelective(devices.get(0));
                        }
                    }
                } catch (Exception e) {
                    //日志处理
                    if (serveLogList.size() > 0) {
                        serveLogList.forEach(log -> {
                            log.setSchedule("100");
                            log.setReturnedValue("[下发异常]" + e.getMessage());
                            faceServeLogMapper.updateByPrimaryKeySelective(log);
                        });
                    }
                    cmd.setHandledRemark("[下发异常]" + e.getMessage());

                    log.error("第三方设备解析下发指令异常!{}", e.getLocalizedMessage());
                }

                //修改指令数据
                cmd.setHandled(true);
                cmd.setDeleted(false);
                faceCommandMapper.updateByPrimaryKeySelective(cmd);
                //修改服务器日志
                if (serveLogList.size() > 0) {
                    serveLogList.forEach(log -> faceServeLogMapper.updateByPrimaryKeySelective(log));
                }
            });
        }
    }
总结

后续需要接入其他品牌的考勤机器,只需要编写好实现类,在策略管理类中添加该策略即可,不需要对原来的代码进行改动,避免使用难以维护的多重条件选择语句。

策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派 给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的 策略类里面,作为一个抽象策略类的子类。

策略模式主要优点在于对“开闭原则”的完美支持,在不修改原有系 统的基础上可以更换算法或者增加新的算法,它很好地管理算法族, 提高了代码的复用性,是一种替换继承,避免多重条件转移语句的 实现方式;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值