廢話在前:隻有總結,才能進步。
近日有人問起,“你認為如何能提高代碼的品質”,雖然都說出來了,但缺乏條理,特總結于此。
首先,應明确什麼樣的代碼算是品質高的。然後才能知道如何去做。
我覺得,高品質的代碼應該至少包括:
1.可讀性。
2.可維護性。
代碼的可讀性同樣有助于代碼的可維護性。有時候在一個項目裡,你修改的代碼未必是自己寫的,如果代碼寫的可讀性很差,那将是非常痛苦的。
一、可讀性
1.包名、類名、成員變量名、方法名、局部變量名等的命名應仔細斟酌。盡量做到讀一段代碼,就像是讀一句話一樣,在閱讀的過程中就能了解其中的邏輯。
2.包下面應包含哪些類,這也是有講究的。不能太随意。從包中應該可以大概看出類的功能或所在層次。
3.一個方法隻要實作一個功能。這一點很重要,首先利于閱讀,方法的名字可以告訴你它的功能。
4.方法的實作不宜過長,個人寫代碼的習慣是,一個方法的長度不會超過電腦的一屏。也就是不用拖動滾動條便可把實作盡收眼底。
二、可維護性
1.代碼寫的要靈活,尤其是一些配置檔案。比如寫一個下載下傳程式,檔案的放置路徑就不能在代碼中寫死。因為一來以後要變動的話,還要來改程式,并重新部署;二來開發的環境與正式的環境有所不同,寫死也不利于部署。
最典型的例子就是資料庫的連接配接資訊,url、使用者名、密碼等,開發環境與正式環境一般不會相同,把這些資訊寫在配置檔案裡為好。
2.提高的代碼複用,以提高代碼的維護性。
舉個反例吧。我們的系統中有很多生成Excel檔案并在浏覽器端下載下傳的功能要求,由于這些功能由不同的人來完成,結果類似的程式寫了二十幾份,都是互相拷貝而來。
當時我們用的是POI的低版本,後來進行了POI的更新,有一些API不能使用了,于是改了二十幾個類。如果把這些功能複用一些,也許隻要修改一個類檔案就搞定了。
3.多用一些設計模式,提高代碼的層次感。使得修改代碼的時候,影響範圍達到盡量的小。這個就有難度了。平時要慢慢積累才行。
常用的模式 工廠模式、模闆模式、擴充卡模式、政策模式、代理模式(動态代理) 等。這些模式在平時的工作中經常用到,應該熟練運用才行。
下面是一些代碼片段:
private void checkJob(JobConfig job) {
JobConfig original = find(job);
if (original != null) { // 已存在的任務 進行替換
if (original.equals(job)) { // 任務沒有變更
} else if (replace(original, job)) { // 替換作業成功 則加入到新作業清單中
allJobs.remove(original);
}
} else if (schedule(job)) { // 新添加的任務
log.info(Log4jHelper.getLogMain(job) + "派發成功。");
}
}
private JobConfig find(JobConfig job) {
for (JobConfig j : allJobs) {
// 找到相同作業ID的任務
if (j.getJobId().equals(job.getJobId())) {
return j;
}
}
return null;
}
private boolean replace(JobConfig original, JobConfig job) {
return remove(original) && schedule(job);
}
private boolean remove(JobConfig job) {
try {
return schedulerService.remove(job);
}
catch (SchedulerException e) {
e.printStackTrace();
log.error(Log4jHelper.getLogMain(job) + "移除失敗。");
}
return false;
}
private boolean schedule(JobConfig job) {
try {
schedulerService.schedule(job);
}
catch (Exception e) {
e.printStackTrace();
log.error(Log4jHelper.getLogMain(job) + "派發失敗。");
return false;
}
return true;
}
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.Predicate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import com.lim.base.exception.AppException;
/**
* A collection of class management utility methods.
*
*/
@SuppressWarnings("unchecked")
public class ClassUtils {
/**
* 獲得泛型類泛型參數類型.
* 例如:
* getGenericArgumentsType(List<String>.class)傳回Class<String>.
* @param <T>
* @param cls
* @return
*/
public static <T> Class<T> getGenericArgsType(Class<?> cls) {
return getGenericArgumentsType(cls, 0);
}
/**
* 獲得泛型類泛型參數類型.
* @param <T>
* @param cls
* @param pos
* @return
*/
public static <T> Class<T> getGenericArgumentsType(Class<?> cls, int pos) {
return (Class<T>) ((ParameterizedType) cls.getGenericSuperclass()).getActualTypeArguments()[pos];
}
/**
* 在指定的類路徑查找符合條件的類.<br/>
* @param classPathPattern
* @param predicate
* @return
*/
public static Class<?>[] getClasses(String classPathPattern, Predicate predicate) {
PathMatchingResourcePatternResolver resolver = null;
resolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metaFactory = null;
metaFactory = new CachingMetadataReaderFactory(resolver);
try {
Resource[] resources = resolver.getResources("classpath*:" + classPathPattern);
ArrayList<Class<?>> clazzArr = new ArrayList<Class<?>>();
for (Resource res : resources) {
if (!res.isReadable()) {
continue;
}
MetadataReader metadataReader = metaFactory.getMetadataReader(res);
String className = metadataReader.getClassMetadata().getClassName();
Class<?> clazz = ClassUtils.loadClass(className);
if (predicate.evaluate(clazz)) {
clazzArr.add(clazz);
}
}
return clazzArr.toArray(new Class<?>[0]);
}
catch (IOException e) {
throw new AppException(e);
}
catch (LinkageError e) {
throw new AppException(e);
}
}
/**
* 在指定的類路徑查找指定父類/接口類的所有子類.
* Example:
* Class<ITagChecker>[] checkerClz = ClassUtils.getSubClasses("com/linkage/ess/**<pre>/*</pre>.class", ITagChecker.class);
* @param <T>
* @param classPathPattern
* @param superClass
* @return
*/
public static <T> Class<? extends T>[] getSubClasses(String classPathPattern,
final Class<T> superClass) {
return (Class<? extends T>[]) getClasses(classPathPattern, new Predicate() {
@Override
public boolean evaluate(Object arg0) {
Class<?> clazz = (Class<?>) arg0;
return !clazz.isInterface() && superClass.isAssignableFrom(clazz);
}
});
}
/**
* Create a new instance given a class name(要求有預設構造函數).
*
* @param className
* A class name
* @return A new instance
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @exception Exception
* If an instantiation error occurs
*/
public static <T> T newInstance(Class<T> clz) {
try {
return clz.newInstance();
}
catch (InstantiationException e) {
e.printStackTrace();
throw new AppException(e);
}
catch (IllegalAccessException e) {
e.printStackTrace();
throw new AppException(e);
}
}
/**
* 帶有參數的執行個體建立.
* @param <T>
* @param impl
* @param param
* @param params
* @return
*/
public static <T> T newInstance(Class<T> impl, Object... params) {
List<Class<?>> lstClass = new ArrayList<Class<?>>(params.length + 1);
for (Object obj : params) {
lstClass.add(obj.getClass());
}
Class<?> paramClasses[] = lstClass.toArray(new Class<?>[0]);
return newInstance(impl, paramClasses, params);
}
/**
* 帶有參數的執行個體建立.
* @param <T>
* @param impl
* @param paramClasses
* @param params
* @return
*/
public static <T> T newInstance(Class<T> impl, Class<?> paramClasses[], Object params[]) {
try {
Constructor<T> constructor = impl.getConstructor(paramClasses);
return constructor.newInstance(params);
}
catch (InstantiationException e) {
e.printStackTrace();
throw new AppException(e);
}
catch (IllegalAccessException e) {
e.printStackTrace();
throw new AppException(e);
}
catch (IllegalArgumentException e) {
throw new AppException(e);
}
catch (InvocationTargetException e) {
throw new AppException(e);
}
catch (SecurityException e) {
throw new AppException(e);
}
catch (NoSuchMethodException e) {
throw new AppException(e);
}
}
/**
* 使用類型的執行個體建立.(要求有預設構造函數).
* @param className
* @return
*/
public static Object newInstance(String className) {
try {
return loadClass(className).newInstance();
}
catch (InstantiationException e) {
e.printStackTrace();
throw new AppException(e);
}
catch (IllegalAccessException e) {
e.printStackTrace();
throw new AppException(e);
}
}
/**
* Load a class given its name. BL: We wan't to use a known
* ClassLoader--hopefully the heirarchy is set correctly.
*
* @param className
* A class name
* @return The class pointed to by <code>className</code>
* @exception ClassNotFoundException
* If a loading error occurs
*/
public static Class<?> loadClass(String className) {
try {
return getClassLoader().loadClass(className);
}
catch (ClassNotFoundException e) {
throw new AppException(e);
}
}
public static Method getMethod(String className, String name, Class<?>... parameterTypes) {
return getMethod(loadClass(className), name, parameterTypes);
}
public static Method getMethod(Class<?> cls, String name, Class<?>... parameterTypes) {
try {
return cls.getMethod(name, parameterTypes);
}
catch (SecurityException e) {
throw new AppException(e);
}
catch (NoSuchMethodException e) {
throw new AppException(e);
}
}
/**
* Return a resource URL. BL: if this is command line operation, the
* classloading issues are more sane. During servlet execution, we
* explicitly set the ClassLoader.
*
* @return The context classloader.
* @exception MalformedURLException
* If a loading error occurs
*/
public static URL getResource(String resource) {
return getClassLoader().getResource(resource);
}
public static InputStream loadResource(String path) {
try {
return new ClassPathResource(path).getInputStream();
}
catch (IOException e) {
e.printStackTrace();
throw new AppException(e);
}
}
/**
* Return the context classloader. BL: if this is command line operation,
* the classloading issues are more sane. During servlet execution, we
* explicitly set the ClassLoader.
*
* @return The context classloader.
*/
public static ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
public static Field getDeclaredField(Class<?> cls, String fieldName) {
try {
return cls.getDeclaredField(fieldName);
}
catch (Exception e) {
throw new AppException(e);
}
}
public static Object invokeQuietly(Object target, Method m) {
try {
return m.invoke(target);
}
catch (IllegalArgumentException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
catch (InvocationTargetException e) {
e.printStackTrace();
if (e.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) e.getTargetException();
}
}
return null;
}
}