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