答應我,別再if/else走天下了可以嗎


哎,曾幾何時 

想當年,其實我也特別鍾情於 if/else連環寫法,上來就是一頓SAO操作,比如舉個好理解的簡單栗子

一般來說我們正常的後臺管理系統都有所謂的角色的概念,不同管理員權限不一樣,能夠行使的操作也不一樣,比如:

  • 系統管理員( ROLE_ROOT_ADMIN):有 A操作權限

  • 訂單管理員( ROLE_ORDER_ADMIN):有 B操作權限

  • 普通用戶( ROLE_NORMAL):有 C操作權限

比如一個用戶進來,我們需要根據不同用戶的角色來判斷其有哪些行爲,這時候SAO代碼出現了:

    public class JudgeRole {

        public String judge( String roleName ) {
            String result = "";
            if (roleName.equals("ROLE_ROOT_ADMIN")) { // 系統管理員有A權限
                result = "ROLE_ROOT_ADMIN: " + "has AAA permission";
            } else if ( roleName.equals("ROLE_ORDER_ADMIN") ) { // 訂單管理員有B權限
                result = "ROLE_ORDER_ADMIN: " + "has BBB permission";
            } else if ( roleName.equals("ROLE_NORMAL") ) { // 普通用戶有C權限
                result = "ROLE_NORMAL: " + "has CCC permission";
            } else {
                result = "XXX";
            }

            return result;
        }
    }

這樣當系統裏有幾十個角色時,那幾十個 if/else嵌套可以說是非常酸爽了…… 這樣一來非常不優雅,別人閱讀起來很費勁;二來則是以後如果再複雜一點,或者想要再加條件的話不好擴展;而且代碼一改,以前的老功能肯定還得重測,豈不瘋了……

所以,如果在不看下文的情況下,你一般會如何去對付這些令人頭痛的if/else語句呢?

當然有人會說用 switch/case來寫是否會優雅一些呢?答案是:毛區別都沒有

接下來簡單講幾種改進方式,別再 if/else走天下了

 有枚舉爲啥不用 

什麼角色能幹什麼事,這很明顯有一個對應關係,所以學過的枚舉爲啥不用呢?

首先定義一個公用接口 RoleOperation,表示不同角色所能做的操作:

    public interface RoleOperation {
        
        // 表示某個角色可以做哪些op操作
        String op();  

    }

接下來我們將不同角色的情況全部交由枚舉類來做,定義一個不同角色有不同權限的枚舉類 RoleEnum

    public enum RoleEnum implements RoleOperation {

        // 系統管理員(有A操作權限)
        ROLE_ROOT_ADMIN {
            @Override
            public String op() {
                return "ROLE_ROOT_ADMIN:" + " has AAA permission";
            }
        },

        // 訂單管理員(有B操作權限)
        ROLE_ORDER_ADMIN {
            @Override
            public String op() {
                return "ROLE_ORDER_ADMIN:" + " has BBB permission";
            }
        },

        // 普通用戶(有C操作權限)
        ROLE_NORMAL {
            @Override
            public String op() {
                return "ROLE_NORMAL:" + " has CCC permission";
            }
        };

    }

接下來調用就變得異常簡單了,一行代碼就行了, if/else也灰飛煙滅了:

    public class JudgeRole {

        public String judge( String roleName ) {

            // 一行代碼搞定!之前的if/else沒了!
            return RoleEnum.valueOf(roleName).op();

        }

    }

而且這樣一來,以後假如我想擴充條件,只需要去枚舉類中加代碼即可,而不是去改以前的代碼,這豈不很穩!

除了用枚舉來消除 if/else,工廠模式也可以實現

 有工廠模式爲啥不用 

不同分支做不同的事情,很明顯就提供了使用工廠模式的契機,我們只需要將不同情況單獨定義好,然後去工廠類裏面聚合即可。

首先,針對不同的角色,單獨定義其業務類:

    // 系統管理員(有A操作權限)
    public class RootAdminRole implements RoleOperation {

        private String roleName;

        public RootAdminRole( String roleName ) {
            this.roleName = roleName;
        }

        @Override
        public String op() {
            return roleName + " has AAA permission";
        }

    }

    // 訂單管理員(有B操作權限)
    public class OrderAdminRole implements RoleOperation {

        private String roleName;

        public OrderAdminRole( String roleName ) {
            this.roleName = roleName;

        }

        @Override
        public String op() {
            return roleName + " has BBB permission";
        }

    }

    // 普通用戶(有C操作權限)
    public class NormalRole implements RoleOperation {

        private String roleName;

        public NormalRole( String roleName ) {
            this.roleName = roleName;
        }

        @Override
        public String op() {
            return roleName + " has CCC permission";
        }

    }

接下來再寫一個工廠類 RoleFactory對上面不同角色進行聚合:

    public class RoleFactory {

        static Map<String, RoleOperation> roleOperationMap = new HashMap<>();

        // 在靜態塊中先把初始化工作全部做完
        static {
            roleOperationMap.put( "ROLE_ROOT_ADMIN", new RootAdminRole("ROLE_ROOT_ADMIN") );
            roleOperationMap.put( "ROLE_ORDER_ADMIN", new OrderAdminRole("ROLE_ORDER_ADMIN") );
            roleOperationMap.put( "ROLE_NORMAL", new NormalRole("ROLE_NORMAL") );
        }

        public static RoleOperation getOp( String roleName ) {

            return roleOperationMap.get( roleName );

        }

    }

接下來藉助上面這個工廠,業務代碼調用也只需一行代碼, if/else同樣被消除了:

    public class JudgeRole {

        public String judge( String roleName ) {

            // 一行代碼搞定!之前的 if/else也沒了!
            return RoleFactory.getOp(roleName).op();
        }

    }

這樣的話以後想擴展條件也很容易,只需要增加新代碼,而不需要動以前的業務代碼,非常符合“開閉原則”

來,我們接着來,除了工廠模式,策略模式也不妨試一試


 有策略模式爲啥不用 

策略模式和工廠模式寫起來其實區別也不大!

在上面工廠模式代碼的基礎上,按照策略模式的指導思想,我們也來創建一個所謂的策略上下文類,這裏命名爲 RoleContext

    public class RoleContext {

        // 可更換的策略,傳入不同的策略對象,業務即相應變化
        private RoleOperation operation; 

        public RoleContext( RoleOperation operation ) {
            this.operation = operation;
        }

        public String execute() {
            return operation.op();
        }

    }

很明顯上面傳入的參數 operation就是表示不同的“策略”。我們在業務代碼裏傳入不同的角色,即可得到不同的操作結果:

    public class JudgeRole {

        public String judge( RoleOperation roleOperation ) {
            RoleContext roleContext = new RoleContext( roleOperation );
            return roleContext.execute();

        }

    }

    public static void main( String[] args ) {

        JudgeRole judgeRole = new JudgeRole();

        String result1 = judgeRole.judge(new RootAdminRole("ROLE_ROOT_ADMIN"));

        System.out.println( result1 );

        String result2 = judgeRole.judge(new OrderAdminRole("ROLE_ORDER_ADMIN"));

        System.out.println( result2 );

        String result3 = judgeRole.judge(new NormalRole("ROLE_NORMAL"));

        System.out.println( result3 );

    }

 共   勉 

好了,先講到這裏吧,本文僅僅是拋磚引玉,使用了一個極其簡單的示例來打了個樣,然而其思想可以廣泛地應用於實際複雜的業務和場景,思想真的很重要!寫代碼前還是得多思考一番,考慮是否有更具可擴展性的寫法!

發佈了74 篇原創文章 · 獲贊 33 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章