大部分的公司,開發項目都是做坊式的,沒有產品和項目的需求分析,進而作出技術架構和詳細設計。
不少人,聽到上級和老闆的一個想法,就開始寫代碼,邊寫邊改,甚至推倒重來。
最終,致使的常見結果之一,項目代碼混亂,新員工甚至老員工,對項目理解比較吃力。
若是你去改造代碼,改好了,沒有任何功勞。改出問題了,領導、測試、產品,極可能會說你不行。
這一點,是讓不少程序員糾結的地方。
我我的仍是傾向重構的,先熟悉項目整體環境,從易到難。
項目開發,從外部看,就是一個個的功能。
從內部看,不就是一個個函數和API嗎。
只要輸入和輸出是穩定的,就不會出現重大的失誤,致使牽一髮動全身的不良後果。
下面記錄一些,我我的本身揣摩的一些重構方法,閱讀《重構》這本書,也帶給了我一些啓發。
1.命名規範化
包名、類名、方法名、變量名,取合適的英文單詞。
好比在一個電商項目中。
包名:com.company.shop.front.controller,前端系統的控制層
類名:OrderController,處理訂單的控制器
方法名:findById,根據id查找對象,我我的更喜歡用get。
變量名:List<String> memberIdList = new ArrayList<String>();
會員id的list集合
比較坑的命名:Object tagcode = map.get("tagCode");
變量命名,沒有按照Java駝峯式命名規範來寫,「tagcode」和「tagCode」居然同時存在。
在Java內部代碼變量名,若是錯了還有提示,在Freemarker和JSP等界面中,字符串的值,用錯了,根本沒有提示。
大小寫不統一的bug,還很難發現。
命名要一致:Controller、Service、Dao等,同一我的和不一樣人的,儘量遵循必定的標準,閱讀和修改其餘人代碼也更順利。
2.代碼合理組織
controller:控制器,響應請求,路由控制
service:服務層,處理業務邏輯
dao:數據訪問層
model:數據庫模型
bean:內部用的實體類
util:工具代碼
interceptor:攔截器
其它代碼,能夠按照功能等進行劃分,讓人一眼望去,就知道這個包下的代碼,大體作了什麼事情。
3.項目合理組織
合理拆分項目:
移動端,mobile項目
Web前端,front項目
後端管理:backend項目
合理服務拆分:
商品服務系統:ProductService項目
會員服務系統:UserService項目
登陸服務系統:LoginService項目
訂單服務系統:OrderService項目
公共代碼:Model模型、Util工具類
據說淘寶和京東的電商網站,有幾百上千個服務。
4.使用最小最恰當的做用域
類,大多數類用的是public,public class ProductService,若是隻是在包內部使用,能夠去掉public。
字段,儘量用private, private ProductService productService。若是須要,經過get和set方法,來得到和修改值。
方法/函數,對外被調用的用public,只在類的內部使用的,儘量用private。
public void add(){
doAdd();
}
private void doAdd(){
}
不少人,內部代碼都搞成public,乍一眼看上去,還覺得外部有調用。
必須得看看依賴,才肯定。
對外暴露了過多的接口,不應被調用的被調用了。
5.常量提取
"success",把做用一致而且相同的字符串,提取成常量,統一管理。
其中一種方式:
public class FrontConst {
public static final String COOKIE_LOGINNAME = "loginNameCookie";
public static final String COOKIE_PASSWORD = "passWordCookie";
}
6.重複代碼提取和封裝
業務代碼,提取成私有方法,內部重複使用。
工具代碼,提取到工具類中,能夠複用,好比日期處理、把list轉換成map。
public class BizUtil {
/**
* 把1個集合,轉換成Map。用法示例:Map<String, Dictionary> dictionaryMap = BizUtil.listToMap("BIANMA", dictionarys);
* @param keyName 集合元素惟一字段的名稱
* @param list 集合元素
* @return map
*/
public static <K, V> Map<K, V> listToMap(final String keyName, List<V> list) {
if(CollectionUtils.isEmpty(list)){
return null;
}
return Maps.uniqueIndex(list, new Function<V, K>() {
@Override
public K apply(V v) {
try {
return (K)PropertyUtils.getSimpleProperty(v, keyName);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
return null;
}
}
});
}
/**
* 把1個集合,轉換成Map。用法示例:Map<String, Dictionary> dictionaryMap =
* BizUtil.listToMap(String.class, Dictionary.class, "BIANMA", dictionarys);
*
* @param k
* Map的key的class
* @param v
* Map的value的class,集合list元素的類型
* @param keyName
* 集合元素惟一字段的名稱
* @param list
* 集合元素
* @return map
*/
//這種方式,廢棄了,須要多傳2個參數
public static <K, V> Map<K, V> listToMap(Class<K> k, Class<V> v,
String keyName, List<V> list) {
Map<K, V> map = Maps.newHashMap();
if (CollectionUtils.isNotEmpty(list)) {
for (V val : list) {
try {
PropertyDescriptor pd = new PropertyDescriptor(keyName, v);
Method getMethod = pd.getReadMethod();// 得到get方法
Object o = getMethod.invoke(val);// 執行get方法返回一個Object
if (o != null && o.getClass().equals(k)) {
map.put((K) o, val);
}
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException | IntrospectionException e) {
e.printStackTrace();
}
}
}
return map;
}
7.函數拆分
1個函數,有5個參數,有2種不一樣的使用場景。
同一個函數完成了2件不一樣的事情。
void add(int a,int b,int c);
換成
void addOrder(int a,int b);
void addProduct(int c)。
8.函數合併
2個函數,完成了相似的事情,簡化成1個,
函數是拆分,仍是合併,要具體分析。
9.單一職責原則
一個項目、一個模塊、一個類、一個函數,完成一件事。
若是完成了多件事,須要進行拆分紅多個獨立的函數,至少內部須要進行拆分。
好比:
void add(int a,int b,int c)
能夠拆分紅
void add1(int a,int b)和void add2(int c)
也能夠拆分紅
void add(int a,int b,int c){
add1(a,b);
add2(c);
}
10.控制類和函數的行數
一個類,若是函數太多,每每是承載了太多的功能。
把不緊密相關的功能,堆積在了一塊兒。
一個方法,若是代碼超過了100行,存在問題的可能性就增大了不少。
在我自身的經歷中,不少同事的函數,只要超過100行,一眼望去就能發現問題。
另外,函數代碼過多,修改bug和新增業務邏輯的時候,很容易引入新的問題。
所以,函數拆分、下降做用域,能夠保證過去穩定的代碼邏輯,不會有改動。
11.提早返回
先檢查錯誤,若是有問題,直接返回,以避免嵌套過深。
public String add(){
Member member = this.getMember();
if(StringUtils.isBlank(...){
return error("地址信息有誤!");
}
}
常常出現下面的狀況
if(...){
if(...){
if(...){
}
}
}
return ...;
12.定義枚舉
枚舉是常量的進一步封裝。
public enum OrderPayStatusEnum {
NO("0", "未支付"), YES("1", "已支付");
private String code;
private String remark;
OrderPayStatusEnum(String code, String remark) {
this.code = code;
this.remark = remark;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public static String getPayStatus(String code){
if(StringUtils.isEmpty(code)){
return null;
}
if(code.equals(NO.getCode())){
return NO.getRemark();
}else if(code.equals(YES.getCode())){
return YES.getRemark();
}
return null;
}
}
13.統一API交互接口
後端和移動端,後端和Web前端的交互,都是Result的json字符串。
後端,全部請求,統一都是返回Result。
public class Result {
private Integer code;
private String desc;
private Object data;
}
多人開發的時候,若是沒有架構師之類的角色,各自爲戰,真是亂套。
14.模塊化和依賴
大多數項目,都會有諸如郵件、短信驗證碼、登陸服務、圖片雲服務等。
把配置文件單獨拿出來,能夠手動經過Spring的配置文件,配置bean。
若是須要,引入配置文件,不引入,代碼也不會報錯。
spring-mail-config.xml
<context:property-placeholder location="file:${config_path}/config/mail.properties" ignore-unresolvable="true" />
<bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${mailServerHost}" />
<property name="port" value="${mailServerPort}" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.timeout">25000</prop>
</props>
</property>
<!-- 發送者用戶名 -->
<property name="username" value="${mailUserName}" />
<!-- 發送者密碼 -->
<property name="password" value="${mailPassword}" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<bean id="mailClientServer" class="com.shop.common.mail.MailClientServer">
<property name="javaMailSender" ref="javaMailSender" />
<property name="configuration" ref="freeMarkerConfigurationFactory" />
<property name="mailFromAddress" value="${mailFromAddress}" />
</bean>
還好比,我們的圖片用的阿里雲的OSS。
統一封裝。
配置文件
oss.properties
oss.xml
<context:property-placeholder
location="file:${config_path}/config/oss.properties"
ignore-unresolvable="true" />
<bean id="ossConfig" class="com.shop.common.oss.OssConfig">
<property name="endpoint" value="${oss.endpoint}" />
<property name="bucketName" value="${oss.bucketName}" />
<property name="accessKeyId" value="${oss.accessKeyId}" />
<property name="accessKeySecret" value="${oss.accessKeySecret}" />
<property name="imgDomain" value="${oss.imgDomain}" />
<property name="fileDomain" value="${oss.fileDomain}" />
</bean>
Java代碼
public class OssUtil {}
15.代碼複用
代碼複用,是一個普遍的話題。
前文提到的枚舉、常量、函數拆分,目的之一就是爲了方便代碼複用。
一個優秀的程序員,抵得上十個差的程序員,其中一個緣由就是「代碼複用多」。
老是寫重複的代碼,把程序員這個智力密集型工做,幹成了體力密集型,遲早被累死。
梳理業務需求、作好業務方面的架構設計。
另外,技術方面的架構設計,徹底掌握在技術人員手中,對業務的依賴不大。
開發一個功能時,先想好思路,技術方案,再寫代碼。
架構複用、設計複用、工具類、枚舉、常量。
另外還有一種,很是常見的,流程複用。
前端系統:
1.構造url和參數
2.執行請求,得到數據
3.根據狀態碼,執行不一樣的操做
4.正常狀態,渲染數據,或執行動做
移動端:
流程相似
後端:
1.穩定而且統一的API接口
2.業務邏輯封裝
不管內部怎麼變,接口穩定,就不會影響Web前端和移動端。
在合適的時機,內部項目拆分,項目服務化。
重構的技巧,仍是有不少的,更多仍是看我的對編程的理解。
對於有經驗的程序員來講,《重構》是一本不錯的書,能夠用來快速加深平時對項目代碼的梳理。
對於新手來講,《重構》和設計模式,這種書,受益偏小。
小雷-一個有獨立看法的年輕人
2016年4月16日~清明節剛過,五一又要來了
湖北-武漢~據說最近有幾條公交專線要開通了
最近,有好多好多的想法和話題要寫,時間不太夠。
更準確來講, 時間是有的,寫文章,也要看心情。
寫一篇嚴肅一點的長篇大論,通常都須要1小時以上。
在寫做以前,大腦裏面一直在不斷地「打草稿」,寫的過程當中,須要不斷髮散,要有條理,還要嚴謹,站得住腳。
一些問題的根源,也許是個人身價仍是過低了,更多時間忙着搞錢,一些有價值的事情,只能慢慢地去作。