• 想让你的java代码更漂亮,用枚举吧


        枚举是java 5之后添加的一个重要特性,这个特性不能提高性能,但是能让java程序员写出更优雅的代码。

        我之前看到过挺多介绍java枚举的不错的帖子,我也来参与以下这个话题。

    1. 枚举基本用法

    1 /**
    2  * 消息的类型,是请求还是回应
    3  * @author leo
    4  *
    5  */
    6 public enum ReqOrRes {
    7     req,res;
    8 }

    这应该是枚举最简单的用法了,即使是这样简单的使用,也能给我们带来方便,

    ReqOrRes.req.name() == "req"  

    ReqOrRes.valueOf("req") == ReqOrRes.req  , 通过name 和valueOf这两个枚举内置的方法,可以方便的将枚举在字符串和枚举之间转化。

    2. 枚举中带方法,将处理逻辑写在枚举类中,将调用框架和业务逻辑分离

    /**
     * 列举调度server与slave之间的所有通信报文类型,每个类型的具体描述见com.zdcin.msgbean包,<br>
     * 每种消息有两个方法供外部调用,根据消息是在哪里产生,对应一方需实现processReq方法,产生消息的一方需要实现processRes方法。
     * @author leo
     *
     */
    public enum MsgType {// MsgType有两个主要方法,processReq,和processRes, 用于完成处理各种类型的请求消息和响应消息的逻辑
    
        /** slave获取clientid 
         * 
         */
        GET_CLIENT_ID {
            @Override
            public void processReq(String json) {// 覆盖父类MsgType中的同名方法,processRes方法在另外一端实现,这个方法的意思是处理请求,并发回响应信息
                GET_CLIENT_ID_req req = MsgUtils.getGson().fromJson(json, GET_CLIENT_ID_req.class);
                GET_CLIENT_ID_res res = new GET_CLIENT_ID_res(req);
                。。。。。
                //用clientid做key找到连接,把消息发回去
                Client.sendMsgToSlave(res.clientId, res);
            }
        },
        /** 已经分配过id的slave每次连接到server都要用该命令通知server建立连接 */
        CONNECT {
            @Override
            public void processReq(String json) {
                CONNECT_req req =  MsgUtils.getGson().fromJson(json, CONNECT_req.class);
                。。。。。
            }
        },
    
        /** 任务指定 */
        TASK_ASSIGN {
            @Override
            public void processRes(String json) {
                //不用实现
            }
        },
    /** 心跳 */
        HEART_BEAT {
            
            @Override
            public void processReq(String json) {
                HEART_BEAT_req req = MsgUtils.getGson().fromJson(json, HEART_BEAT_req.class);
                if ("00X".equalsIgnoreCase(req.meta.clientId)) {
                    return;
                }
                HEART_BEAT_res res = new HEART_BEAT_res(req);
                Client.sendMsgToSlave(req.meta.clientId, res);
            }
    
            @Override
            public void processRes(String json) {
                //接收就行,不用处理
            }
        };
    //------------ 到这里,枚举类型定义结束了,下边是各个枚举都可以用的公共方法
    /** * 消息有在调度server产生的,又在slave上产生的, * 在调度器上产生的消息,slave需要实现processReq方法,并且slave需要返回res消息给调度器,调度器就需要实现processRes方法; * 如果消息是在slave上产生(如主动上报状态的消息),上述流程就反过来。 * @param json */ public void processReq(String json){//父类定义的方法 throw new RuntimeException(this.name() + ".processReq() method not implement."); } /** * 消息有在调度server产生的,又在slave上产生的, * 在调度器上产生的消息,slave需要实现processReq方法,并且slave需要返回res消息给调度器,调度器就需要实现processRes方法; * 如果消息是在slave上产生(如主动上报状态的消息),上述流程就反过来。 * @param json */ public void processRes(String json){//父类定义的方法 throw new RuntimeException(this.name() + ".processRes() method not implement."); } }

    上述枚举类的作用是定义消息类型,同时在processReq和processRes方法中实现处理各种请求和响应消息的逻辑,这只是消息一端的代码,一般消息中processReq和processRes只需要出现一次,在消息的另一段,对称的实现缺少的那个方法就可以。

    这个枚举类用起来大概是这个样子的:

    public class ReciveWork implements Runnable {
    
        private String json;
        private Meta meta;
    
        public ReciveWork(Meta meta, String json) {
            this.meta = meta;
            this.json = json;
        }
    
        @Override
        public void run() {
            try {
                switch (meta.reqOrRes) {
                case req://根据消息是请求类型还是响应类型,来决定是调用 processReq方法还是 processRes方法。
                    meta.msgType.processReq(json);// meta.msgType 就是MsgType中的CONNECT, GET_CLIENT_ID等枚举,这里不确定是哪一个,但是可以确定他们会有processReq方法,这样就可以优雅的动态分发消息给合适的处理者了 
                    break;
    
                case res:
                 // 检查重发队列,如果在队列中,取消重发
                    Client.notifyToStopRetry(meta);
                    // 先检查有对方有没有处理错误,有的话,直接记录日志,不调用后续的处理方法了, 相关的错误恢复工作可以在定时任务中进行。
                    BaseResMsg baseres = MsgUtils.getGson().fromJson(json, BaseResMsg.class);
                    if (baseres.errCode != 0) {
                        Main.log.error("error in " + baseres.meta.msgType.name() + "_res,  [errCode, errMsg]=["
                                + baseres.errCode + ", " + baseres.errMsg + "]");
                    } else {
                        meta.msgType.processRes(json);
                    }
                    
                    break;
                }
            } catch (Exception e) {
                Main.log.error(
                        "error in " + meta.msgType.name() + ".process" + meta.reqOrRes.name() + ", " + e.getMessage(), e);
            }
        }
    
    }

    worker类实现了Runnable接口,可以放在线程池里,多线程并行分发处理。

    现在看,效果还不错吧,如果不用枚举,估计少不了一堆if else分支判断,或者是基于抽象类的模式,每个消息类型写一个子类,去做实现,应该不会比这简单。

    3. 带数值可叠加的枚举

    /**
     * 
     * @author leo
     *
     */
    public enum AppMonitorType {
        /**
         * 读权限
         */
        read(1),
        /**
         * 写权限
         */
        write(2),
        /**
         * 执行权限
         */
        exec(4),
        /**
         * 特殊权限
         */
        spic(8);
        
        private int bitValue;
        private AppMonitorType(int v) {//私有的构造方法,只能类初始化时使用,如exec(4),
            this.bitValue = v;
        }
        public int getBitValue() {// 返回枚举对应的整形值,如 exec(4) 中的4
            return this.bitValue;
        }
        
        //将数值转换成权限列表, 这个方法是静态的, 直接AppMonitoryType.parseBitValue()
        public static List<AppMonitorType> parseBitValue(int bitValue) {
            if (bitValue <= 0) {
                return null;
            }
            List<AppMonitorType> list = new ArrayList<AppMonitorType>();
            for (AppMonitorType type : AppMonitorType.values()) {
                if ((type.bitValue & bitValue) == type.bitValue) {
                    list.add(type);
                }
            }
            return list;
        }
        // 将权限列表转换成数值, 注意这个方法是静态的,用的时候 直接AppMonitorType.getBitValue()
        public static int getBitValue(List<AppMonitorType> list) {
            int i = 0;
            for (AppMonitorType type : list) {
                i |= type.bitValue;
            }
            return i;
        }
    }

    由于涉及一些业务信息,我临时用 read write等代替了原来的枚举, 不过用法是一样的, 这个例子比较简单,不管用什么方式实现都不会很难,当数据量多一些,逻辑复杂一些的时候,枚举的好处应该会体现的更明显。

         你有关于枚举使用的一些奇妙的想法吗,欢迎提出来,大家一起进步,我也想进一步去发现。

    原文地址:http://www.cnblogs.com/zdcin/p/3155057.html

  • 相关阅读:
    Linux 第一个脚本程序
    Linux 8开启FTP功能服务
    PPT 倒计时时钟,用 GIF 动画实现,可直接使用 -- 附 Python 实现代码
    python flask 虚拟环境迁移
    GOLANG学习之路之二
    Golang学习之路之一
    vscode 调试flask项目 解决(socket.gaierror: [Errno 11001] getaddrinfo failed)
    windows下部署 flask (win10+flask+nginx)
    git入门
    配置maven的国内镜像
  • 原文地址:https://www.cnblogs.com/zdcin/p/3155057.html
Copyright © 2020-2023  润新知