如何用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,有挺多值得我们鉴戒,瞎写瞎看吧 呵呵,迎接拍砖,等有新设法了我再来完美。
我所有的自负皆来自我的自卑,所有的英雄气概都来自于我的软弱。嘴里振振有词是因为心里满是怀疑,深情是因为痛恨自己无情。这世界没有一件事情是虚空而生的,站在光里,背后就会有阴影,这深夜里一片寂静,是因为你还没有听见声音。—— 马良《坦白书》
                     
                  
     
  
 
    
    
搞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,有挺多值得我们鉴戒,瞎写瞎看吧 呵呵,迎接拍砖,等有新设法了我再来完美。
我所有的自负皆来自我的自卑,所有的英雄气概都来自于我的软弱。嘴里振振有词是因为心里满是怀疑,深情是因为痛恨自己无情。这世界没有一件事情是虚空而生的,站在光里,背后就会有阴影,这深夜里一片寂静,是因为你还没有听见声音。—— 马良《坦白书》



