近日有人問起,“你認為如何能提高代碼的質(zhì)量”,雖然都說出來了,但缺乏條理,特總結(jié)于此。
首先,應(yīng)明確什么樣的代碼算是質(zhì)量高的。然后才能知道如何去做。
我覺得,高質(zhì)量的代碼應(yīng)該至少包括:
1.可讀性。
2.可維護性。
代碼的可讀性同樣有助于代碼的可維護性。有時候在一個項目里,你修改的代碼未必是自己寫的,如果代碼寫的可讀性很差,那將是非常痛苦的。
一、可讀性
1.包名、類名、成員變量名、方法名、局部變量名等的命名應(yīng)仔細斟酌。盡量做到讀一段代碼,就像是讀一句話一樣,在閱讀的過程中就能理解其中的邏輯。
2.包下面應(yīng)包含哪些類,這也是有講究的。不能太隨意。從包中應(yīng)該可以大概看出類的功能或所在層次。
3.一個方法只要實現(xiàn)一個功能。這一點很重要,首先利于閱讀,方法的名字可以告訴你它的功能。
4.方法的實現(xiàn)不宜過長,個人寫代碼的習(xí)慣是,一個方法的長度不會超過電腦的一屏。也就是不用拖動滾動條便可把實現(xiàn)盡收眼底。
二、可維護性
1.代碼寫的要靈活,尤其是一些配置文件。比如寫一個下載程序,文件的放置路徑就不能在代碼中寫死。因為一來以后要變動的話,還要來改程序,并重新部署;二來開發(fā)的環(huán)境與正式的環(huán)境有所不同,寫死也不利于部署。
最典型的例子就是數(shù)據(jù)庫的連接信息,url、用戶名、密碼等,開發(fā)環(huán)境與正式環(huán)境一般不會相同,把這些信息寫在配置文件里為好。
2.提高的代碼復(fù)用,以提高代碼的維護性。
舉個反例吧。我們的系統(tǒng)中有很多生成Excel文件并在瀏覽器端下載的功能要求,由于這些功能由不同的人來完成,結(jié)果類似的程序?qū)懥硕畮追荩际腔ハ嗫截惗鴣怼?/p>
當(dāng)時我們用的是POI的低版本,后來進行了POI的升級,有一些API不能使用了,于是改了二十幾個類。如果把這些功能復(fù)用一些,也許只要修改一個類文件就搞定了。
3.多用一些設(shè)計模式,提高代碼的層次感。使得修改代碼的時候,影響范圍達到盡量的小。這個就有難度了。平時要慢慢積累才行。
常用的模式 工廠模式、模板模式、適配器模式、策略模式、代理模式(動態(tài)代理) 等。這些模式在平時的工作中經(jīng)常用到,應(yīng)該熟練運用才行。
下面是一些代碼片段:
1
private void checkJob(JobConfig job)
{
2
JobConfig original = find(job);
3
if (original != null)
{ // 已存在的任務(wù) 進行替換
4
if (original.equals(job))
{ // 任務(wù)沒有變更
5
} else if (replace(original, job))
{ // 替換作業(yè)成功 則加入到新作業(yè)列表中
6
allJobs.remove(original);
7
}
8
} else if (schedule(job))
{ // 新添加的任務(wù)
9
log.info(Log4jHelper.getLogMain(job) + "派發(fā)成功。");
10
}
11
}
12
13
private JobConfig find(JobConfig job)
{
14
for (JobConfig j : allJobs)
{
15
// 找到相同作業(yè)ID的任務(wù)
16
if (j.getJobId().equals(job.getJobId()))
{
17
return j;
18
}
19
}
20
return null;
21
}
22
23
private boolean replace(JobConfig original, JobConfig job)
{
24
return remove(original) && schedule(job);
25
}
26
27
private boolean remove(JobConfig job)
{
28
try
{
29
return schedulerService.remove(job);
30
}
31
catch (SchedulerException e)
{
32
e.printStackTrace();
33
log.error(Log4jHelper.getLogMain(job) + "移除失敗。");
34
}
35
return false;
36
}
37
38
private boolean schedule(JobConfig job)
{
39
try
{
40
schedulerService.schedule(job);
41
}
42
catch (Exception e)
{
43
e.printStackTrace();
44
log.error(Log4jHelper.getLogMain(job) + "派發(fā)失敗。");
45
return false;
46
}
47
return true;
48
}
49
1
import java.io.IOException;
2
import java.io.InputStream;
3
import java.lang.reflect.Constructor;
4
import java.lang.reflect.Field;
5
import java.lang.reflect.InvocationTargetException;
6
import java.lang.reflect.Method;
7
import java.lang.reflect.ParameterizedType;
8
import java.net.MalformedURLException;
9
import java.net.URL;
10
import java.util.ArrayList;
11
import java.util.List;
12
13
import org.apache.commons.collections.Predicate;
14
import org.springframework.core.io.ClassPathResource;
15
import org.springframework.core.io.Resource;
16
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
17
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
18
import org.springframework.core.type.classreading.MetadataReader;
19
import org.springframework.core.type.classreading.MetadataReaderFactory;
20
21
import com.lim.base.exception.AppException;
22
23
/** *//**
24
* A collection of class management utility methods.
25
*
26
*/
27
@SuppressWarnings("unchecked")
28
public class ClassUtils
{
29
30
/** *//**
31
* 獲得泛型類泛型參數(shù)類型.
32
* 例如:
33
* getGenericArgumentsType(List<String>.class)返回Class<String>.
34
* @param <T>
35
* @param cls
36
* @return
37
*/
38
public static <T> Class<T> getGenericArgsType(Class<?> cls)
{
39
return getGenericArgumentsType(cls, 0);
40
}
41
42
/** *//**
43
* 獲得泛型類泛型參數(shù)類型.
44
* @param <T>
45
* @param cls
46
* @param pos
47
* @return
48
*/
49
public static <T> Class<T> getGenericArgumentsType(Class<?> cls, int pos)
{
50
return (Class<T>) ((ParameterizedType) cls.getGenericSuperclass()).getActualTypeArguments()[pos];
51
}
52
53
/** *//**
54
* 在指定的類路徑查找符合條件的類.<br/>
55
* @param classPathPattern
56
* @param predicate
57
* @return
58
*/
59
public static Class<?>[] getClasses(String classPathPattern, Predicate predicate)
{
60
PathMatchingResourcePatternResolver resolver = null;
61
resolver = new PathMatchingResourcePatternResolver();
62
MetadataReaderFactory metaFactory = null;
63
metaFactory = new CachingMetadataReaderFactory(resolver);
64
try
{
65
Resource[] resources = resolver.getResources("classpath*:" + classPathPattern);
66
ArrayList<Class<?>> clazzArr = new ArrayList<Class<?>>();
67
for (Resource res : resources)
{
68
if (!res.isReadable())
{
69
continue;
70
}
71
72
MetadataReader metadataReader = metaFactory.getMetadataReader(res);
73
String className = metadataReader.getClassMetadata().getClassName();
74
Class<?> clazz = ClassUtils.loadClass(className);
75
if (predicate.evaluate(clazz))
{
76
clazzArr.add(clazz);
77
}
78
}
79
return clazzArr.toArray(new Class<?>[0]);
80
}
81
catch (IOException e)
{
82
throw new AppException(e);
83
}
84
catch (LinkageError e)
{
85
throw new AppException(e);
86
}
87
}
88
89
/** *//**
90
* 在指定的類路徑查找指定父類/接口類的所有子類.
91
* Example:
92
* Class<ITagChecker>[] checkerClz = ClassUtils.getSubClasses("com/linkage/ess/**<pre>/*</pre>.class", ITagChecker.class);
93
* @param <T>
94
* @param classPathPattern
95
* @param superClass
96
* @return
97
*/
98
public static <T> Class<? extends T>[] getSubClasses(String classPathPattern,
99
final Class<T> superClass)
{
100
101
return (Class<? extends T>[]) getClasses(classPathPattern, new Predicate()
{
102
@Override
103
public boolean evaluate(Object arg0)
{
104
Class<?> clazz = (Class<?>) arg0;
105
return !clazz.isInterface() && superClass.isAssignableFrom(clazz);
106
}
107
});
108
}
109
110
/** *//**
111
* Create a new instance given a class name(要求有默認構(gòu)造函數(shù)).
112
*
113
* @param className
114
* A class name
115
* @return A new instance
116
* @throws ClassNotFoundException
117
* @throws IllegalAccessException
118
* @throws InstantiationException
119
* @exception Exception
120
* If an instantiation error occurs
121
*/
122
public static <T> T newInstance(Class<T> clz)
{
123
try
{
124
return clz.newInstance();
125
}
126
catch (InstantiationException e)
{
127
e.printStackTrace();
128
throw new AppException(e);
129
}
130
catch (IllegalAccessException e)
{
131
e.printStackTrace();
132
throw new AppException(e);
133
}
134
135
}
136
137
/** *//**
138
* 帶有參數(shù)的實例創(chuàng)建.
139
* @param <T>
140
* @param impl
141
* @param param
142
* @param params
143
* @return
144
*/
145
public static <T> T newInstance(Class<T> impl, Object
params)
{
146
List<Class<?>> lstClass = new ArrayList<Class<?>>(params.length + 1);
147
for (Object obj : params)
{
148
lstClass.add(obj.getClass());
149
}
150
Class<?> paramClasses[] = lstClass.toArray(new Class<?>[0]);
151
152
return newInstance(impl, paramClasses, params);
153
}
154
155
/** *//**
156
* 帶有參數(shù)的實例創(chuàng)建.
157
* @param <T>
158
* @param impl
159
* @param paramClasses
160
* @param params
161
* @return
162
*/
163
public static <T> T newInstance(Class<T> impl, Class<?> paramClasses[], Object params[])
{
164
try
{
165
Constructor<T> constructor = impl.getConstructor(paramClasses);
166
return constructor.newInstance(params);
167
}
168
catch (InstantiationException e)
{
169
e.printStackTrace();
170
throw new AppException(e);
171
}
172
catch (IllegalAccessException e)
{
173
e.printStackTrace();
174
throw new AppException(e);
175
}
176
catch (IllegalArgumentException e)
{
177
throw new AppException(e);
178
}
179
catch (InvocationTargetException e)
{
180
throw new AppException(e);
181
}
182
catch (SecurityException e)
{
183
throw new AppException(e);
184
}
185
catch (NoSuchMethodException e)
{
186
throw new AppException(e);
187
}
188
}
189
190
/** *//**
191
* 使用類型的實例創(chuàng)建.(要求有默認構(gòu)造函數(shù)).
192
* @param className
193
* @return
194
*/
195
public static Object newInstance(String className)
{
196
try
{
197
return loadClass(className).newInstance();
198
}
199
catch (InstantiationException e)
{
200
e.printStackTrace();
201
throw new AppException(e);
202
}
203
catch (IllegalAccessException e)
{
204
e.printStackTrace();
205
throw new AppException(e);
206
}
207
}
208
209
/** *//**
210
* Load a class given its name. BL: We wan't to use a known
211
* ClassLoader--hopefully the heirarchy is set correctly.
212
*
213
* @param className
214
* A class name
215
* @return The class pointed to by <code>className</code>
216
* @exception ClassNotFoundException
217
* If a loading error occurs
218
*/
219
public static Class<?> loadClass(String className)
{
220
try
{
221
return getClassLoader().loadClass(className);
222
}
223
catch (ClassNotFoundException e)
{
224
throw new AppException(e);
225
}
226
}
227
228
public static Method getMethod(String className, String name, Class<?>
parameterTypes)
{
229
return getMethod(loadClass(className), name, parameterTypes);
230
}
231
232
public static Method getMethod(Class<?> cls, String name, Class<?>
parameterTypes)
{
233
try
{
234
return cls.getMethod(name, parameterTypes);
235
}
236
catch (SecurityException e)
{
237
throw new AppException(e);
238
}
239
catch (NoSuchMethodException e)
{
240
throw new AppException(e);
241
}
242
}
243
244
/** *//**
245
* Return a resource URL. BL: if this is command line operation, the
246
* classloading issues are more sane. During servlet execution, we
247
* explicitly set the ClassLoader.
248
*
249
* @return The context classloader.
250
* @exception MalformedURLException
251
* If a loading error occurs
252
*/
253
public static URL getResource(String resource)
{
254
return getClassLoader().getResource(resource);
255
}
256
257
public static InputStream loadResource(String path)
{
258
try
{
259
return new ClassPathResource(path).getInputStream();
260
}
261
catch (IOException e)
{
262
e.printStackTrace();
263
throw new AppException(e);
264
}
265
}
266
267
/** *//**
268
* Return the context classloader. BL: if this is command line operation,
269
* the classloading issues are more sane. During servlet execution, we
270
* explicitly set the ClassLoader.
271
*
272
* @return The context classloader.
273
*/
274
public static ClassLoader getClassLoader()
{
275
return Thread.currentThread().getContextClassLoader();
276
}
277
278
public static Field getDeclaredField(Class<?> cls, String fieldName)
{
279
try
{
280
return cls.getDeclaredField(fieldName);
281
}
282
catch (Exception e)
{
283
throw new AppException(e);
284
}
285
}
286
287
public static Object invokeQuietly(Object target, Method m)
{
288
try
{
289
return m.invoke(target);
290
}
291
catch (IllegalArgumentException e)
{
292
e.printStackTrace();
293
}
294
catch (IllegalAccessException e)
{
295
e.printStackTrace();
296
}
297
catch (InvocationTargetException e)
{
298
e.printStackTrace();
299
if (e.getTargetException() instanceof RuntimeException)
{
300
throw (RuntimeException) e.getTargetException();
301
}
302
}
303
304
return null;
305
}
306
}
----2010年08月31日