大學畢業4年-回顧和總結(6)-技術研發-重構之法

大部分的公司,開發項目都是做坊式的,沒有產品和項目的需求分析,進而作出技術架構和詳細設計。
不少人,聽到上級和老闆的一個想法,就開始寫代碼,邊寫邊改,甚至推倒重來。

最終,致使的常見結果之一,項目代碼混亂,新員工甚至老員工,對項目理解比較吃力。
若是你去改造代碼,改好了,沒有任何功勞。改出問題了,領導、測試、產品,極可能會說你不行。
這一點,是讓不少程序員糾結的地方。

我我的仍是傾向重構的,先熟悉項目整體環境,從易到難。
項目開發,從外部看,就是一個個的功能。
從內部看,不就是一個個函數和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小時以上。
在寫做以前,大腦裏面一直在不斷地「打草稿」,寫的過程當中,須要不斷髮散,要有條理,還要嚴謹,站得住腳。

一些問題的根源,也許是個人身價仍是過低了,更多時間忙着搞錢,一些有價值的事情,只能慢慢地去作。 
相關文章
相關標籤/搜索