前言
? ? ? ?重復的代碼一直都是可維護性的大敵,重構的重要任務之一也就是要去除掉重復的代碼,有效的減少重復代碼,可以大大提高軟件的擴展性。
? ? ? ?在Android開發中,很容易產生重復的代碼。因為Android是組件,模板式開發,每個頁面都是獨立用Activity或Fragment實現,布局文件都是用XML方式去寫,所以很容易造成代碼的重復,雖然二個頁長的差不多,但畢竟是二個Activity,于是就拷一份出來,改吧改吧就成了。
? ? ? ?那么我們如何做才能去掉重復的代碼呢?
一、使用include標簽引用重復布局
二、使用style定義樣式
三、使用ViewStub減少整體布局的重復
四、多使用應用資源
五、代碼的抽象與繼承
六、總結
一、使用include標簽引用重復布局
? ? ? ?標簽是減少布局重復的利器,它的作用是把另外一個布局文件全部無修改式的嵌入到標簽所在的位置。這與C/C++語言的預處理指令#include是一樣的。在WEB框架式開發中,也非常常用HTML模板,其中也有類似的include。目的也是減少代碼的重復。
? ? ? ?要想把include用的恰到好處,首先要做的就是把整體布局模塊化,從整體的布局出發,找出可復用的局部布局或布局組合,把它們放入單獨一個布局文件中,然后在其他的地方就可以include了。
例如:
test.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16dp" android:gravity="center" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:text="1" /> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#BFBFBF"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16dp" android:gravity="center" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:text="2" /> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#BFBFBF"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16dp" android:gravity="center" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:text="3" /> </LinearLayout>
? ? ? ?這段布局是我們正常情況下的寫法,從布局中我們可以看出View標簽代碼是一樣的,那么我們就可以把View單獨寫在一個xml文件里,然后用include標簽引用它即可。
簡化后的布局代碼如下:
test_1.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16dp"android:gravity="center"android:layout_marginTop="5dp"android:layout_marginBottom="5dp"android:text="1" /><include layout="@layout/divider_view"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16dp"android:gravity="center"android:layout_marginTop="5dp"android:layout_marginBottom="5dp"android:text="2" /><include layout="@layout/divider_view"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16dp"android:gravity="center"android:layout_marginTop="5dp"android:layout_marginBottom="5dp"android:text="3" />
</LinearLayout>
divider_view.xml
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="0.5dp"android:background="#BFBFBF"></View>
? ? ? ?怎么樣,是不是比原來的布局代碼簡潔直觀多了,而且實現出來的效果也一樣。當然這是單個View重復,那么多個View組重復也是一樣的。
? ? ? ?使用include標簽還有一個好處,就比如你要修改View的樣式,在未使用include之前,你需要把test.xml中的所有View都要修改一遍,但是使用include之后,你只需要修改divider_view.xml中的View即可。
二、使用style定義樣式
? ? ? ?在我們開發應用的過程中,會有很多控件的樣式是一樣的,我們一般會用復制-粘貼來使用,這樣就會有大量重復代碼出現,而且如果我們某一天要修改該控件的樣式了,那我們需要找到所有這些控件,一個一個的修改樣式,但是我們如果使用style來定義樣式的話,不僅會減少大量重復代碼,而且修改樣式也變的非常靈活。
? ? ? ?就比如test_1.xml布局文件中有三個Button控件,只有text內容不同,那么我們如何把Button的共同點提取到style中呢?很簡單,在styles.xml下 新增一對<style>標簽,為該style命名,如命名為DemoBtn,然后在<style>標簽內 使用<item>標簽來存放控件的屬性,例如 android:layout_width="match_parent" ? ? ,用<item>來表示就是 <item name="android:layout_width">match_parent</item>,name是控件的屬性名,標簽之間則是屬性值
按照這樣的方法,把所有共同屬性都添加到DemoBtn這一style中即可。
<style name="DemoBtn"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:textSize">16dp</item> <item name="android:gravity">center</item> <item name="android:layout_marginTop">5dp</item> <item name="android:layout_marginBottom">5dp</item>
</style>
? ? ? ?最后回到布局中,把三個Button的共同屬性全都刪除掉,換為 style ="@style/DemoBtn" ? ,就能達到原來的效果,1行代碼代替6行代碼,代碼現在更加美觀了。
test_2.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button style="@style/DemoBtn" android:text="1" /> <include layout="@layout/divider_view"/> <Button style="@style/DemoBtn" android:text="2" /> <include layout="@layout/divider_view"/> <Button style="@style/DemoBtn" android:text="3" /> </LinearLayout>
? ? ? ?經過include標簽引用和style樣式提取,在看我們的布局文件,是不是感覺很清爽了。而且需要修改樣式的話,只需要在style中修改就可以了。
三、使用ViewStub減少整體布局的重復
? ? ? ?前面是找出布局中的可復用布局組合,include以減少重復。但有些時候是反過來的,也就是說有幾個頁面,它們整體的一樣的,但是某個局部是不同的,這個時候include就不行了。這個時候就可以使用一樣的整體布局+ViewStub來做布局。
? ? ? ?ViewStub是一個輕量級別的,不可見的View,當ViewStub被設為visible時,或者顯示調用layout()時,才會去把它所指向的布局渲染出來,所以它非常適合處理整體相同,局部不同的情況。關于ViewStub的使用可以參考Android實戰技巧:ViewStub的應用。 具體的策略是:
- 規劃整體布局,抽象出共同的布局,把可變的布局識別出來
- 寫整體布局,對于可變的局部布局用ViewStub替代
- 用一個基Fragment來操作整體布局。
- 創建基Fragment的子類,每個子類,用真正的布局來替換ViewStub。
四、多使用應用資源
? ? ? ?這點是非常重要的,Android的強大之處在于,所有的資源的指定都可以用引用,而非直接寫死,直接寫死就會出現重復代碼,比如顏色,背影,字串,布局,ID,度量(dimen),風格等等。那么,我們在使用的時候,也盡可能的使用引用,這樣非常易于復用,修改和定制,從而也就更方便復用。
五、代碼的抽象與繼承
? ? ? ?從代碼上去除重復的代碼就是用通用的重構技巧,比如提煉方法,抽象基類,提煉常量等。
六、總結
? ? ? ?其實代碼的去重復的關鍵都在于要分析出可變與不可變,共性和特性,這是抽象與封裝的基礎。這個沒有直接可操作性的建議,只能靠自己平時多多積累,以及遇到問題時多多思考。
? ? ? ?另外,就是對于重復的定義是達到三次及三次以上。如果僅出現二次,并且,無可能出現別一次,這個時候其時,要不要去重復有在商榷,寫第二次時,花時間重構,與拷貝或重新實現,其實代碼差不多。當發現第三次實現某個東西時,就要考慮好好的重構一下,減少重復。
? ? ? ?還有,就是,在項目開始不要考慮的太多,不要過度設計。孤認為,不過度設計,更不要過早優化,就根據需求和進度和發展狀況綜合來看。當出現了重復,需要重構時就立馬去做,這樣就不會出問題。但如果沒能及時去做重構,欠了債,那么由于涉及代碼,功能和模塊都比較多時,再去重構,難度大,風險也大。就好比房屋的維護保養,當出現灰塵時就去打掃,很容易,個把小時就搞定了,但如果一直拖著,一年才打掃一次,可能要十天半個月才能完事。