想讓你的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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM