zoukankan      html  css  js  c++  java
  • 枚举类的扩展使用

    . 没人看的前言

      枚举相信大家都不陌生,在日常的开发中,我们在大多数情况下使用枚举一般是为了罗列既定的属性值,作用其实与常量差别不大,但枚举的优势在于,可以定义多种类型的多个常量,自由度和扩展度会大大高于普通常量,而且阅读起来会比常量更加直观,因为枚举内的属性不一定全部都要用到,一般在定义枚举时都会添加一个注释key,也就是此枚举值的说明字段。那么既然枚举可自由扩展,在开发中,我们就可以利用枚举来减少繁琐的代码步骤,甚至解决某些难题。

    1.使用枚举扩展属性

      举个常见的例子:

    Excel导出统计数据,其中,Excel内容包括标题、子标题、内容,标题、子标题格式已知

     Excel导出,封装数据时,很多人习惯直接在方法体里拼接,这么做后患无穷,最大的两个影响:可读性差、扩展性差,我们可能经常会需要客户该改需求的情况,如果客户提出想要在Excel里加个字段或者换下位置,可能会让人头大。

     那么利用枚举,我们可以很好的解决这点。先看枚举实例:

     其中,每个枚举值代表一个标题,xxxHead为子标题,xxxField为内容字段,如此一来,Excel的内容便有了一个初步的概图,接下来写入数据也就清晰明了了

    /**
     * @description:分析数据Excel数据枚举
     */
    public enum AnalysisExcelDataType {
    
        DATE("date","/",  dataInEnum.dateHead, dataInEnum.dateField),
        SHOP("shop","店铺首页",  dataInEnum.shopHead, dataInEnum.shopField),
        GOODS("goods","商品页", dataInEnum.goodsHead, dataInEnum.goodsField),
        PAGE("page","落地页面", dataInEnum.pageHead, dataInEnum.pageField),
        CUSTOMER("customer","客户行为", dataInEnum.customerHead, dataInEnum.customerField),
        ORDER("order","下单", dataInEnum.orderHead, dataInEnum.orderField),
        PAY("pay","付款", dataInEnum.payHead, dataInEnum.payField),
        COUPON("coupon","优惠券", dataInEnum.couponHead, dataInEnum.couponField);
    
        private String title;
        private String titleName;
        private String[] headStrData;
        private String[] fieldStrData;
        AnalysisExcelDataType(String title, String titleName, String[] headStrData, String[] fieldStrData){
            this.title = title;
            this.titleName = titleName;
            this.headStrData = headStrData;
            this.fieldStrData = fieldStrData;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public String getTitleName() {
            return titleName;
        }
    
        public void setTitleName(String titleName) {
            this.titleName = titleName;
        }
    
        public String[] getHeadStrData() {
            return headStrData;
        }
    
        public void setHeadStrData(String[] headStrData) {
            this.headStrData = headStrData;
        }
    
        public String[] getFieldStrData() {
            return fieldStrData;
        }
    
        public void setFieldStrData(String[] fieldStrData) {
            this.fieldStrData = fieldStrData;
        }
    }
    
    class dataInEnum{
        public static final String[] dateHead = {"日期"};
        public static final String[] dateField = {"dataTime"};
    
        public static final String[] shopHead = {"PV","UV"};
        public static final String[] shopField = {"shopPvNum","shopUvNum"};
    
        public static final String[] goodsHead = {"PV","UV"};
        public static final String[] goodsField = {"skuPvNum","skuUvNum"};
    
        public static final String[] pageHead = {"PV","UV"};
        public static final String[] pageField = {"pv","uv"};
    
        public static final String[] customerHead = {"加购商品人数","收藏商品人数","收藏店铺人数"};
        public static final String[] customerField = {"cartNum","followSkuNum","followShopNum"};
    
        public static final String[] orderHead = {"下单人数","下单金额","下单订单数","下单件数"};
        public static final String[] orderField = {"ordPins","ordAmount","ordNum","ordQtty"};
    
        public static final String[] payHead = {"付款人数", "付款金额", "付款订单数", "付款件数"};
        public static final String[] payField = {"payPins","payAmount","payNum","payQtty"};
    
        public static final String[] couponHead = {"领券人数", "用券人数", "引入订单量", "引入金额"};
        public static final String[] couponField = {"couponPins","couponUsePins","couponOrders","couponAmount"};
    }

    枚举创建好后,开始写入数据,这里为了可读性和方便(主要~),将标题与内容分开写入,先看标题:

    /**
    *Excel标题、子标题写入
    */
     private static LinkedHashMap createTitle(HSSFSheet sheet){
            String[] head = null;
            String[] field = null;
            //标题
            LinkedHashMap<String, String>  headMap = new LinkedHashMap<>();
            //子标题
            LinkedHashMap<String, String> headMap2 = new LinkedHashMap<>();
            List<Integer> colspanList = new ArrayList<>();
            int cloCount = -1;
            //遍历标题枚举
            for(AnalysisExcelDataType dataType : AnalysisExcelDataType.values()){
                head = dataType.getHeadStrData();
                field = dataType.getFieldStrData();
                for(int i=0;i<head.length;i++){
                    headMap2.put(field[i], head[i]);
                }
                headMap.put(dataType.getTitle(), dataType.getTitleName());
                if(head.length > 1){
                    //标题跨行 每个标题跨的行数为子标题个数
                    sheet.addMergedRegion( new CellRangeAddress(0,0,cloCount+1, cloCount + head.length));
                }
                colspanList.add(head.length);
                cloCount += head.length;
            }
         //样式
            HSSFCellStyle style = ExcelExtUtil.createCellStyle(true, true,(short)11,
                    null, HorizontalAlignment.CENTER);
            //标题
            ExcelExtUtil.writeSheetTitle(sheet, headMap, 0, colspanList, style);
            style = ExcelExtUtil.createCellStyle(true, true,null,
                    null, HorizontalAlignment.CENTER);
            //子标题
            ExcelExtUtil.writeSheetTitle(sheet, headMap2, 1, null,style);
            return headMap2;
        }

    这里使用了poi进行写入,有部分代码因为考虑篇幅没有贴出来,毕竟不是本文重点,如果有需要可以留言。

    好,标题写入完毕,开始写入数据,方式与标题大同小异,利用循环,根据field查询数据内的字段值

    这里的JSONArray就是普通的po集合

     private void createSumExcelData(JSONArray dataArray){
            String[] head = null;
            String[] field = null;
            List<Map<String, Object>> mapList = new ArrayList<>();
            Map<String, Object> map = null;
            JSONObject jsonObject = null;
            HSSFSheet sheet = ExcelExtUtil.createSheet("统计数据");
            //标题数据 同上
            LinkedHashMap<String, String>  headMap = createTitle(sheet);
            for(Object object : dataArray){
                map = new HashMap<>();
                for(AnalysisExcelDataType dataType : AnalysisExcelDataType.values()){
                    head = dataType.getHeadStrData();
                    field = dataType.getFieldStrData();
                   for(int i=0;i<head.length;i++){
                       //根据标题对应字段获取数据中的值
                       jsonObject = JSONObject.parseObject(JSONObject.toJSONString(object));
                       map.put(field[i], jsonObject.get(field[i]));
                    }
                }
                mapList.add(map);
            }
            HSSFCellStyle style = ExcelExtUtil.createCellStyle(false, true,null,
                    null, HorizontalAlignment.CENTER);
            //数据
            ExcelExtUtil.writeSheetData(sheet, headMap, mapList, 2, style);
        }

    如此一来,如果Excel需要变动,那么只需要改动枚举即可,数据封装主体完全不需要改动,是不是很优雅了呢~~

    2.枚举配合多态

     话不多说,让我们先看一个需求例子:

    现需要开发一个消息通知工具类,需要透出统一发送方法和单一发送方法,并支持多渠道消息通知,且渠道间的入参有差异

     我的做法是使用多态,继承关系来实现多渠道消息发送,并提供统一调用入口。

      暂定两个发送渠道:短信、邮件,其中,Dto类如下:

    import lombok.Data;
    /**
     * @author :shenzhikui
     * @description:渠道参数传输对象父类
     * @date :2019/8/12
     */
    //lombok 自动生成getter、setter、toString
    @Data
    public abstract class BaseNotifyDto {
        private String recipient; //接收人
        private String content; //内容
    }
    BaseNotifyDto
    import lombok.Data;
    
    /**
     * @author :shenzhikui
     * @description:短信消息dto
     * @date :2019/8/12
     */
    @Data
    public class SmsNotifyDto extends BaseNotifyDto {
        private String signature;
    }
    SmsNotifyDto
    import lombok.Data;
    
    /**
     * @author :shenzhikui
     * @description:邮件消息dto
     * @date :2019/8/12
     */
    @Data
    public class EmailNotifyDto extends BaseNotifyDto {
        private String subject;
    }
    EmailNotifyDto

    然后,定义消息发送工具类,如下:

    import java.util.*;
    
    /**
     * @author :shenzhikui
     * @description:消息发送父类
     * @date :2019/8/12
     */
    
    public abstract class BaseMessageNotify {
        /**
         * 初始化
         */
        protected abstract void init();
        /**
         * 发送消息
         */
        protected abstract void send(BaseNotifyDto baseNotifyDto);
    }
    BaseMessageNotify
    import lombok.Data;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.mail.SimpleMailMessage;
    import org.springframework.mail.javamail.JavaMailSender;
    
    /**
     * @author: shenzhikui
     * @description: 邮件消息通知类
     * @date: 2019/8/12
     */
    @Slf4j
    @Data
    public class EmailNotify extends BaseMessageNotify {
        //发送者账号
        private final String MAIL_SENDER = PropertiesUtil.getProperty(PropertiesConstant.NOTIFY_EMAIL_SENDER);
       
        @Autowired
        private JavaMailSender javaMailSender;
    
        @Override
        protected void init() {
        //...
        }
    
        /**
         * @author: shenzhikui
         * @description: 发送邮件
         * @date: 2019/8/12
         * @param baseNotifyVo
         * @return void
         */
        @Override
        public void send(BaseNotifyDto baseNotifyVo) {
            try {
                EmailNotifyDto emailNotifyVo = (EmailNotifyDto)baseNotifyVo;
                SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
                //邮件发送人
                simpleMailMessage.setFrom(MAIL_SENDER);
                //邮件接收人
                simpleMailMessage.setTo(emailNotifyVo.getRecipient());
                //邮件主题
                simpleMailMessage.setSubject(emailNotifyVo.getSubject());
                //邮件内容
                simpleMailMessage.setText(emailNotifyVo.getContent());
                javaMailSender.send(simpleMailMessage);
            }catch (Exception e){
                log.error("邮件发送失败", e.getMessage());
                throw new RuntimeException(e);
            }
        }
    }
    EmailNotify
    /**
     * @author: shenzhikui
     * @description: 消息通知服务类
     * @date: 2019/8/12
     */
    public class SmsNotify extends BaseMessageNotify {
    
        @Override
        protected void init() {
        //...
        }
    
        @Override
        protected void send(BaseNotifyDto baseNotifyVo) {
            try {
                SmsNotifyDto smsNotifyVo = (SmsNotifyDto) baseNotifyVo;
                //由于短信调用方法涉及隐私,所以这里就不展示了,这里不是重点~
            }catch (Exception e){
    
            }
        }
    }
    SmsNotify

    ok,到这里,工具类与dto都准备好了,然后提供调用方法,这里,如果使用常规方法(当然肯定有大佬有更好的方法,这里只是举例,轻喷~),其中一个例子,如下:

      /**
         * 单个发送方法
         */
        public static void sendOne(BaseNotifyDto baseNotifyDto){
            if(baseNotifyDto instanceof SmsNotifyDto){
                new SmsNotify().send(baseNotifyDto);
            }else if(baseNotifyDto instanceof EmailNotifyDto){
                new EmailNotify().send(baseNotifyDto);
            }
        }
        /**
         * 多渠道发送方法
         */
        public static void sendAll(List<BaseNotifyDto> baseNotifyDtoList){
            for(BaseNotifyDto baseNotifyDto : baseNotifyDtoList){
                if(baseNotifyDto instanceof SmsNotifyDto){
                    new SmsNotify().send(baseNotifyDto);
                }
                if(baseNotifyDto instanceof EmailNotifyDto){
                    new EmailNotify().send(baseNotifyDto);
                }
            }
        }

    这种方法,不易扩展,并且存在冗余,而且极不优雅~。

    这个时候,枚举的作用就来了,我们定义一个枚举:

    其中,id为渠道值,发送渠道如果支持自定义,那么存放在数据库的格式一般为"1,2,3"这种拼接格式的值

    /**
     * @description: 通知渠道枚举
     * @author: shenzhikui
     * @date: 2019/7/17
     */
    public enum NotifyChannelEnum {
        SMS("短信", "1", new SmsNotify(), new SmsNotifyDto()),
        EMAIL("邮箱", "2", new EmailNotify(), new EmailNotifyDto()),
    
        private String key;
        private String id;
        private BaseMessageNotify notifyType;
        private BaseNotifyDto dtoType;
    
        public String getKey() {
            return key;
        }
    
        public String getId() {
            return id;
        }
    
        public BaseMessageNotify getNotifyType() {
            return notifyType;
        }
    
        public BaseNotifyDto getDtoType() {
            return dtoType;
        }
    
        NotifyChannelEnum(String key, String id, BaseMessageNotify notifyType, BaseNotifyDto dtoType) {
            this.key = key;
            this.id = id;
            this.notifyType = notifyType;
            this.dtoType = dtoType;
        }
    }

    如此,我们根据此枚举来改造方法:

      /**
         * @description: 发送指定渠道消息
         */
        public static void send(BaseNotifyDto baseNotifyDto){
           createNotify(baseNotifyDto).send(baseNotifyDto);
        }
    
        /**
         * @description: 发送多渠道消息
         */
        public static void sendAll(List<BaseNotifyDto> notifyDtoList){
            notifyDtoList.forEach( notifyDto -> createNotify(notifyDto).send(notifyDto));
        }
    
     //*************************** 私有方法 *************************
    
      private static BaseMessageNotify createNotify(BaseNotifyDto baseNotifyDto){
            for(NotifyChannelEnum channelEnum : NotifyChannelEnum.values()){
                if(channelEnum.getDtoType().getClass().equals(baseNotifyDto.getClass())){
                    return channelEnum.getNotifyType();
                }
            }
            throw new RuntimeException("create messageNotify error----no notify");
        }

    好,新的方法更长了,此贴完结。。。。。。。。

    开个玩笑,例子举的可能不是很好,但想要表达的意思还在,新的方法看似很长,但一劳永逸,枚举中每个属性都指定了渠道值,并且指定了渠道对应的dto和发送方法类,这么做的一大好处,就是更好的扩展性,如果新增渠道,只需要新增枚举和方法,而不需要改动原有方法,这在开发中是很重要的。

  • 相关阅读:
    Mysql基本数据操作
    Mysql安装及自动化部署脚本方案
    CSS应用内容补充及小实例
    CSS详解
    python 装饰器
    HTML基础
    python 异常处理
    python生成器及迭代器
    python模块(二)
    python字符串格式化
  • 原文地址:https://www.cnblogs.com/nvsky/p/11344734.html
Copyright © 2011-2022 走看看