如何用java写出无副感化的代码

    添加时间:2013-7-9 点击量:

        搞java的同窗们可能对无副感化这个概念斗劲陌生,这是函数式编程中的一个概念,无副感化的意思就是: 一个函数(java里是办法)的多次调用中,只要输入参数的值雷同,输出成果的值也必定雷同,并且在这个函数履行过程中不会改变法度的任何外部状况(比如全局变量,对象中的属性,都属于外部状况),也不依附于法度的任何外部状况。


        比如下面的两个办法,就可以认为是无副感化的。



    /
    

    @author leo

    /
    public class NoSideEffect {

    public static int add(int a, int b) {
    return a+b;
    }

    public boolean isEmpty(String s) {
    if (s == null || s.trim().length() == 0) {
    return true;
    }
    return false;
    }
    }


    下面是有副感化的例子:



    /
    

    @author leo

    /
    public class HasSideEffect {

    public int baseValue;

    public int getCount(int addtion) {
    return baseValue + addtion;
    }
    }


        无副感化的请求可以很严格,在Fp(functional programing)的说话如lisp clojure中,无副感化的办法中连print都不克不及有,因为他们认为连屏幕也是外部状况,我小我感觉在写java的无副感化代码时,可以放宽一点,这个度可以本身把握,本身用着爽就ok。


        “线程安然”是java中一个斗劲常见的概念,线程安然的类,是说不管几许个线程并发接见这个对象,都不会产生不成预期的错误,他们的发挥解析跟单线程接见时一样,是安然靠得住的。 无副感化和线程安然的概念有类似之处,无副感化的办法,必然是线程安然的,这两个概念都可以或许帮助写出并发友爱的代码,无副感化的编程还有一个益处,代很清爽,因为这个办法里的代码只跟输入输出有关系, 习惯了写无副感化的代码,可以或许设计出更稳健的法度。 大师可以想象,若是一个体系的代码,处处是状况,处处有千丝万缕的接洽,这个体系的可保护性会是怎么样的,写过几年代码的人,可能都邑碰着过这种让人头疼的项目。


    下面介绍几点我小我堆集的关于java无副感化编程的经验:


    1. 应用静态办法


        我经常把一些常用的对象办法,甚至小项目中的营业办法写成utils类的静态办法,这些办法尽量写成无副感化的,如许的成果是,数据和操纵分隔,我感触感染用起来斗劲好。




      1 public class AgentUtils {
    
    2
    3 private static long lastMsgIdTime = 0L;
    4
    5 public static synchronized String createNewMsgId(String clientId) {
    6 long now = System.currentTimeMillis();
    7 if (now <= lastMsgIdTime) {
    8 now = lastMsgIdTime + 1;
    9 }
    10 Date nowTime = new Date(now);
    11 String timeStr = DateFormatUtils.format(nowTime, yyyyMMddHHmmssSSS);
    12 lastMsgIdTime = now;
    13 return clientId + _ + timeStr;
    14 }
    15
    16 public static TASK_REPORT_req createTaskReportAndUpdateLocalState(TASK_ASSIGN_req task, WorkItemState workItemState) {
    17 TASK_REPORT_req req = new TASK_REPORT_req(MsgType.TASK_REPORT);
    18 req.imei = task.imei;
    19 req.taskId = task.taskId;
    20 req.testType = task.testType;
    21 req.workItemState = workItemState;
    22 TaskQueue.LocalTestWorkState(req.taskId, req.imei, workItemState);
    23 return req;
    24 }
    25
    26 // private static Gson gson = new GsonBuilder().setDateFormat(yyyy-MM-dd HH:mm:ss:SSS).create();
    27
    28 public static Meta getMeta(String message) {
    29 Gson gson = new GsonBuilder().setDateFormat(yyyy-MM-dd HH:mm:ss:SSS).create();
    30 BaseMsg warp = gson.Json(message, BaseMsg.class);
    31 return warp.meta;
    32 }
    33
    34 public static Gson getGson() {
    35 Gson gson = new GsonBuilder().setDateFormat(yyyy-MM-dd HH:mm:ss:SSS).create();
    36 return gson;
    37 }
    38
    39 /
    40 @param LocalOrRemote 0 :local 1:remote
    41 @param serverApkPath
    42 @return
    43 /
    44 public static String downloadAPK(int LocalOrRemote, String serverApkPath, String taskId) throws IOException {
    45 File src = new File(serverApkPath);
    46 String localFileName = buildFullPath(AgentConf.i.apk_file_base_dir, taskId + _ + src.getName());
    47
    48 //应用scp实现
    49 if (LocalOrRemote == 0) {
    50 FileUtils.copyFileToDirectory(src, new File(AgentConf.i.apk_file_base_dir));
    51 return localFileName;
    52 }
    53 //remote 应用scp实现
    54 boolean isShellSuccess = false;
    55
    56 String shell = AgentConf.i.apk_download_cmd + + AgentConf.i.server_ip + + serverApkPath + + localFileName;
    57 AgentMain.log.info(exec shell: + shell);
    58 int returncode;
    59 try {
    60 returncode = Runtime.getRuntime().exec(shell).waitFor();
    61 } catch (InterruptedException e) {
    62 e.printStackTrace();
    63 return null;
    64 }
    65 AgentMain.log.info(shell returncode: + returncode);
    66
    67 isShellSuccess = returncode == 0;
    68
    69 // 搜检是否成功,批改queue中状况
    70 if (isShellSuccess) {
    71 return localFileName;
    72 } else {
    73 return null;
    74 }
    75 }
    76 /
    77
    78 @param LocalOrRemote 0 :local 1:remote
    79 @param localReprotPaht
    80 @param serverReportPath
    81 @throws IOException
    82 /
    83
    84 public static void uploadReport(int LocalOrRemote, String localReprotPaht, String serverReportPath) throws IOException {
    85 //应用scp实现
    86 if (LocalOrRemote == 0) {
    87 FileUtils.copyFile(new File(localReprotPaht), new File(serverReportPath));
    88 return;
    89 }
    90 //remote 应用scp实现
    91
    92 String shell = AgentConf.i.report_upload_cmd + + AgentConf.i.server_ip + + localReprotPaht + + serverReportPath;
    93 AgentMain.log.info(exec shell: + shell);
    94 int returncode;
    95 try {
    96 returncode = Runtime.getRuntime().exec(shell).waitFor();
    97 } catch (InterruptedException e) {
    98 throw new IOException(e.getMessage(), e);
    99 }
    100 AgentMain.log.info(shell returncode: + returncode);
    101 return;
    102 }
    103 }


    View Code

    这种做法破损了面向对象编程的原则,不过我感觉面向对象和面向过程,函数式编程都是对象,不是宗教,不须要严格的遵守,若是发了然更合适本身的对象,不消去死守原则。 


    2. 不成变对象


        所有字段都设置成final的,只许赋值一次,包管对象不会被改变




     1 /
    
    2
    3 @author leo
    4
    5 /
    6 public class TaskData {
    7 public TaskData(long taskId, String pkgName,Date dAt) {
    8 this.taskId = taskId;
    9 this.pkgName = pkgName;
    10 if (dAt == null) {
    11 SimpleDateFormat sdf = new SimpleDateFormat(yyyy-MM-dd HH:mm:ss);
    12 try {
    13 dAt = sdf.parse(2001-01-01 01:01:01);
    14 } catch (ParseException e) {
    15 Main.log.error(e);
    16 }
    17 }
    18 this.dAt = dAt;
    19
    20 }
    21 public final long taskId;
    22 public final String pkgName;
    23 /最后一次处理惩罚的时候/
    24 public final Date dAt;
    25 public final Date now = new Date();
    26
    27 @Override
    28 public String toString() {
    29 return TaskData[taskId: + this.taskId + ,pkgName: + this.pkgName + ];
    30 }
    31 }


    View Code

        这是一种极端的防止副感化呈现的做法,实际应用中可以做些让步,本身意识到就好了。


    3. 讲求的设计,按捺的写代码


        呵呵,这一条如同跟空话一样。不管是面向什么编程模式,软件设计本身更首要,没有好的设计抽象,不成能写出好代码,架构代码和营业代码要有清楚的划分,营业和营业之间的代码尽量的削减耦合,不要写出有千丝万缕接洽的代码, 这些都是设计的原则, 无副感化编程 也一样,只是一个帮助做出好设计的编程原则, 我一向感觉,设计就是束缚, 好的设计,就是要络续的给全部体系的代码添加束缚,某个处所,不克不及做什么,某个处所,只能做什么, 若是没有束缚,谁谁都可以上天入地,这不成能是一个好保护的软件。 


        关于设计,我给不出具体的代码,对本身代码有寻求的人,会络续进步本身做设计的才能,没有寻求的人,你手把手教他他都邑感觉你烦。


        关于无副感化编程,感触感染想说的不少,能说清楚的不久不多,建议写java的同窗们能学一门动态说话或者函数式编程的说话,比如ruby python clojure,有挺多值得我们鉴戒,瞎写瞎看吧 呵呵,迎接拍砖,等有新设法了我再来完美。

    我所有的自负皆来自我的自卑,所有的英雄气概都来自于我的软弱。嘴里振振有词是因为心里满是怀疑,深情是因为痛恨自己无情。这世界没有一件事情是虚空而生的,站在光里,背后就会有阴影,这深夜里一片寂静,是因为你还没有听见声音。—— 马良《坦白书》
    分享到: