為什么程序員喜歡用大量的if... ...else if ;不喜歡用設(shè)計模式+反射+自定義注解

面向過程設(shè)計和面向?qū)ο笤O(shè)計的主要區(qū)別是:

   是否在業(yè)務(wù)邏輯層使用冗長的if else判斷。如果你的代碼還在大量使用if else,當然,界面表現(xiàn)層除外,即使你使用Java/C#這樣完全面向?qū)ο蟮恼Z言,也只能說明你的思維停留在傳統(tǒng)的面向過程語言上。 

傳統(tǒng)思維設(shè)計

    業(yè)務(wù)邏輯層為什么會使用大量的if else呢?其實目的也是為了重用,但是這是面向過程編程的重用,程序員只看到代碼重用,因為他看到if else幾種情況下大部分代碼都是重復(fù)的,只有部分不同,因此使用if else可以避免重復(fù)代碼。

好了,廢話不多說!

    假定有這樣的一種情況,需要根據(jù)傳入的參數(shù)類型,選擇不同的短信服務(wù)商來發(fā)送相應(yīng)的短信操作。

傳統(tǒng)的if...else來實現(xiàn)的話,就類似如下代碼:

首先,SMSTypeEnum是一個枚舉類,這個沒啥可講的,有問題自行百度一下

/**
 * 短信類型枚舉類
 *
 * @author somnus
 * @date 2019年09月10日
 */
public enum SMSTypeEnum {
    TENGXUNYUN_SMS("txy","騰訊云短信"),
    SI_XUN_TONG_SMS("sxt","思迅通"),
    ALIYUN_SMS("aliyun","阿里云短信"),
    ZHONG_YU_WEI_XIN_SMS("zywx","中昱維信");
    String type;
    String name;
    SMSEnum(String type,String name){
        this.type=type;
        this.name=name;
    }
}
image.gif

執(zhí)行的main方法,可以看出來這里臃腫。

public static void main(String[] args) {
        String type="aliyun";//阿里云短信
        if(type.equals(SMSTypeEnum.ALIYUN_SMS.getType())){
            //處理阿里云短信...
            System.out.println("執(zhí)行:"+SMSTypeEnum.ALIYUN_SMS.getName());
        }else if(type.equals(SMSTypeEnum.TENGXUNYUN_SMS.getType())){
            //處理騰訊云短信短信...
        }else if(type.equals(SMSTypeEnum.SI_XUN_TONG_SMS.getType())){
            //處理思迅通短信...
        }else if(type.equals(SMSTypeEnum.ZHONG_YU_WEI_XIN_SMS.getType())){
            //處理中昱維信短信...
        }else{
            //...
        }
    }
image.gif

最后的返回結(jié)果:

image
image.gif

?

設(shè)計模式

  經(jīng)常有人說用設(shè)計模式,設(shè)計模式是不錯,但是很難用到,其實如果你使用if else來寫代碼時,就直接在寫業(yè)務(wù)邏輯,只不過使用簡單的判斷語句來作為現(xiàn)實情況的替代者。設(shè)計模式我們就說一下策略模式,想到策略模式就想到了《三國演義》中的錦囊妙計。我們下面就開始通過設(shè)計模式

首先我們來建一個消息實體類:

/**
 * 短信消息封裝
 *
 * @author somnus
 * @date 2019年09月10日
 */
public class SMSInfo {
    /**
     * 主鍵
     */
    private Integer id;
    /**
     * 平臺信息
     */
    private String appId;
    /**
     * 短信類型1,發(fā)送2,校驗
     */
    private Integer msgType;
    /**
     * 類型名稱
     */
    private String msgName;
    /**
     * 短信驗證碼
     */
    private String code;
    /**
     * 模版id
     */
    private String templateId;
    /**
     * 短信內(nèi)容
     */
    private String content;
    /**
     * 創(chuàng)建時間
     */
    private String createTime;

    //省略get,set方法
}
image.gif

我們先建一個策略接口

/**
 * 短信發(fā)送策略接口類
 *
 * @author somnus
 * @date 2019年09月10日
 */
public interface SMSStrategyInterface {
    /**
     * 短信發(fā)送
     * @param smsInfo
     * @return
     */
    public BaseResponse sendSms(SMSInfo smsInfo);
}
image.gif

然后是各短信服務(wù)商的實現(xiàn):

/**
 * 阿里云短信發(fā)送策略處理
 *
 * @author somnus
 * @date 2019年09月10日
 */
public class AliyunSmsStrategy implements SMSStrategyInterface {
    /**
     * 短信發(fā)送
     * @param smsInfo
     * @return
     */
    public BaseResponse sendSms(SMSInfo smsInfo) {
        //以上處理發(fā)送短信邏輯這里就不處理了
        return BaseResponse.ok();
    }
}
image.gif

好了,剩下的就是我們的核心部分了

新建一個ManagerStrategy管理類

/**
 * 短信管理策略類
 *
 * @author somnus
 * @date 2019年09月10日
 */
public class ManagerStrategy {
    //短信策略接口
    SMSStrategyInterface smsStrategyInterface;

    ManagerStrategy(SMSStrategyInterface smsStrategyInterface){
        this.smsStrategyInterface=smsStrategyInterface;
    }

    /**
     * 執(zhí)行短信發(fā)送
     * @param smsInfo
     * @return
     */
    public BaseResponse executeSendSMS(SMSInfo smsInfo){
        return smsStrategyInterface.sendSms(smsInfo);
    }
}
image.gif

下面我們下個main方法測試一下,這個是可以優(yōu)化的,我們可以結(jié)合下面的反射以及自定義注解的方式進行優(yōu)化

    public static void main(String[] args) {
        ManagerStrategy managerStrategy=new ManagerStrategy(new AliyunSmsStrategy());
        SMSInfo smsInfo=new SMSInfo();
        //省略短信信息處理邏輯
        System.out.println(managerStrategy.executeSendSMS(smsInfo));
    }
image.gif

我們可以看到返回結(jié)果:

image
image.gif

?

反射

JAVA反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意方法和屬性;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為java語言的反射機制。

我們先來新建一個短信反射枚舉類:

/**
 * 反射短信枚舉類
 *
 * @author somnus
 * @date 2019年09月10日
 */
public enum  RefelSMSEnum {

    ALIYUN_TYPE_SMS("aliyun","com.yunyi.common.service.reflect.AliyunSMS");
    public String type;
    public String clazz;
    RefelSMSEnum(String type,String clazz){
        this.type=type;
        this.clazz=clazz;
    }
    //省略get,set方法
}
image.gif

通過修改ManagerStrategy管理類來處理

/**
 * 短信管理反射類
 *
 * @author somnus
 * @date 2019年09月10日
 */
public class ManagerStrategy {
    //通過map方式管理類型以及包名
    private static Map<String,String> strategyMap = new HashMap<String, String>();
    static {
        for(RefelSMSEnum refelSMSEnum:RefelSMSEnum.values()){
            strategyMap.put(refelSMSEnum.type,refelSMSEnum.clazz);
        }
    }

    /**
     * 執(zhí)行短信發(fā)送
     * @param type
     * @return
     */
    public static void executeSendSMS(String type){
        String classPath=strategyMap.get(type);
        if(null==classPath){
            throw new NullPointerException("獲取短信類型不存在");
        }
        try{
            /*
             * 通過反射將RefelSMSEnum中映射的類實例化
             * */
            Class clazz=Class.forName(classPath);
            Method excute =clazz.getDeclaredMethod("sendSms");
            excute.invoke(clazz.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
image.gif

我們建一個main方法來測試一下

 public static void main(String[] args) {
        String type="aliyun";
        ManagerStrategy.executeSendSMS(type);
    }
image.gif

執(zhí)行結(jié)果:

image
image.gif

?

自定義注解

    從JDK5開始,Java增加對元數(shù)據(jù)的支持,也就是注解,注解與注釋是有一定區(qū)別的,可以把注解理解為代碼里的特殊標記,這些標記可以在編譯,類加載,運行時被讀取,并執(zhí)行相應(yīng)的處理。通過注解開發(fā)人員可以在不改變原有代碼和邏輯的情況下在源代碼中嵌入補充信息。

我們新建建一個SMSAnnotation注解類:

/**
 * 短信發(fā)送自定義注解
 *
 * @author somnus
 * @date 2019年09月10日
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SMSAnnotation {
    //短信類型
    String type() default "aliyun";
    //短信類型名稱
    String name();
}
image.gif

我們可以通過以下的方式來為這個 value 傳值:

@Target(value = {ElementType.FIELD})
image.gif

被這個 @Target 注解修飾的注解將只能作用在成員字段上,不能用于修飾方法或者類。其中,ElementType 是一個枚舉類型,有以下一些值:

  • ElementType.TYPE:允許被修飾的注解作用在類、接口和枚舉上
  • ElementType.FIELD:允許作用在屬性字段上
  • ElementType.METHOD:允許作用在方法上
  • ElementType.PARAMETER:允許作用在方法參數(shù)上
  • ElementType.CONSTRUCTOR:允許作用在構(gòu)造器上
  • ElementType.LOCAL_VARIABLE:允許作用在本地局部變量上
  • ElementType.ANNOTATION_TYPE:允許作用在注解上
  • ElementType.PACKAGE:允許作用在包上

@Retention 用于指明當前注解的生命周期,它的基本定義如下:

同樣的,它也有一個 value 屬性:

@Retention(value = RetentionPolicy.RUNTIME
image.gif

這里的 RetentionPolicy 依然是一個枚舉類型,它有以下幾個枚舉值可取:

  • RetentionPolicy.SOURCE:當前注解編譯期可見,不會寫入 class 文件
  • RetentionPolicy.CLASS:類加載階段丟棄,會寫入 class 文件
  • RetentionPolicy.RUNTIME:永久保存,可以反射獲取

在實現(xiàn)類上加上我們的自定義注解:

/**
 * 阿里云短信實現(xiàn)類
 *
 * @author somnus
 * @date 2019年09月10日
 */
@SMSAnnotation(type = "aliyun",name="阿里云短信")
public class AliyunSMS implements SMSStrategyInterface {
    /**
     * 短信發(fā)送
     * @param smsInfo
     * @return
     */
    public BaseResponse sendSms(SMSInfo smsInfo) {
        //省略短信邏輯處理
        return BaseResponse.ok();
    }

}
image.gif
   以上操作執(zhí)行完畢后,繼續(xù)通過修改ManagerStrategy管理類來處理,唯一變的需要指定掃描的包,通過掃描包的方式去查找我們的自定義注解,自定義注解執(zhí)行方式和反射執(zhí)行差不多,所以這里就不做過多的操作。

本片文章是受網(wǎng)上一個惡搞的圖片整理的,知道有更好的處理問題的方式,為什么還要選擇用最笨的和最麻煩的方式去處理呢?火車,動車,飛機,到達的目的地是一樣。只是速度不一樣,火車不是照樣有人做嗎?看完這篇文章,你有什么想說的,歡迎留言交流。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容