想要寫出跟知名開源庫同樣的代碼,首先你須要知道……

想要寫出跟知名開源庫同樣的代碼,首先你須要知道……

原文  http://www.epubit.com.cn/article/626java

在工做初期,咱們可能會有這樣的感受,本身的代碼接口設計混亂、代碼耦合較爲嚴重、一個類的代碼過多等,當本身回過頭再看這些代碼時可能都會感慨怎麼寫成那樣。再看那些知名的開源庫,他們大多有着整齊的代碼、清晰簡單的接口、職責單一的類,這個時候咱們一般會捶胸頓足而感嘆:何時老夫能寫出這樣的代碼!編程

其實在作開發這些年中,我漸漸的感受到,其實國內一些初、中級工程師寫的東西不規範或者說不夠清晰的緣由是缺少一些指導原則。他們手中揮舞着面向對象的大旗,寫出來的東西卻充斥着面向過程的氣味。也許是他們不知道有這些原則,也許是他們知道可是不能很好運用到實際代碼中,亦或是他們沒有在實戰中體會到這些原則可以帶來的優勢,以致於他們對這些原則並無足夠的重視。json

面向對象六大原則

在此以前,有一點須要你們知道,熟悉這些原則並非說你寫出的程序就必定靈活、清晰,只是爲你的優秀代碼之路鋪上了一層柵欄,在這些原則的指導下你才能避免陷入一些常見的代碼泥沼,從而讓你專心寫出優秀的東西。設計模式

下面咱們就以Android網絡框架SimpleNet爲例來學習這六大面向對象的基本原則,體會這些原則在開發過程當中帶來的強大能量。緩存

1 單一職責原則

單一職責原則的英文名稱是Single Responsibility Principle,簡稱是SRP,簡單地說就是一個類只作一件事。這個設計原則備受爭議卻又極其重要。只要你想和別人爭執、慪氣或者是吵架,這個原則是屢試不爽的。由於單一職責的劃分界限並非如馬路上的行車道那麼清晰,不少時候都是須要靠我的經驗來界定。固然,最大的問題就是對職責的定義,什麼是類的職責,以及怎麼劃分類的職責。服務器

試想一下,若是你遵照了這個原則,那麼你的類就會劃分得很細,每一個類都有比較單一的職責,這不就是高內聚、低耦合麼!固然,如何界定類的職責這須要你的我的經驗了。網絡

在SimpleNet中,我以爲很可以體現SRP原則的就是HttpStack這個類族了。HttpStack定義了一個執行網絡請求的接口,代碼以下:架構

public interface HttpStack {
  /**
   * 執行Http請求,而且返回一個Response
   */ 
  public Response performRequest(Request<?> request);
}

從上述程序中能夠看到,HttpStack只有一個performRequest函數,它的職責就是執行網絡請求而且返回一個Response。它的職責很單一,這樣在須要修改執行網絡請求的相關代碼時,只須要修改實現HttpStack接口的類,而不會影響其餘的類的代碼。若是某個類的職責包含有執行網絡請求、解析網絡請求、進行gzip壓縮、封裝請求參數等,那麼在你修改某處代碼時就必須謹慎,以避免修改的代碼影響了其餘的功能。當你修改的代碼可以基本上不影響其餘的功能。這就在必定程度上保證了代碼的可維護性。注意,單一職責原則並非說一個類只有一個函數,而是說這個類中的函數所作的工做是高度相關的,也就是高內聚。HttpStack抽象了執行網絡請求的具體過程,接口簡單清晰,也便於擴展。框架

優勢ide

(1)類的複雜性下降,實現什麼職責都有清晰明確的定義。

(2)可讀性提升,複雜性下降,那固然可讀性提升了。

(3)可維護性提升,可讀性提升,那固然更容易維護了。

(4)變動引發的風險下降,變動是必不可少的,若是接口的單一職責作得好,一個接口修改只對相應的實現類有影響,對其餘的接口無影響,這對系統的擴展性、維護性都有很是大的幫助。

2 里氏替換原則

面向對象的語言的三大特色是繼承、封裝、多態,里氏替換原則就是依賴於繼承、多態這兩大特性。里氏替換原則簡單來講就是全部引用基類、接口的地方必須能透明地使用其子類的對象。通俗點講,只要父類能出現的地方子類就能夠出現,並且替換爲子類也不會產生任何錯誤或異常,使用者可能根本就不須要知道是父類仍是子類。可是,反過來就不行了,有子類出現的地方,父類未必就能適應。

仍是以HttpStack爲例,SimpleNet定義了HttpStack來表示執行網絡請求這個抽象概念。在執行網絡請求時,只須要定義一個HttpStack對象,而後調用performRequest便可,至於HttpStack的具體實現由更高層的調用者指定。這部分代碼在RequestQueue類中,示例以下:

 /**
   * @paramcoreNums線程核心數
   * @paramhttpStack http執行器
   */
  protected RequestQueue(intcoreNums, HttpStackhttpStack) {
    mDispatcherNums = coreNums;
    mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack();
  }

HttpStackFactory類的createHttpStack函數負責根據API版本建立不一樣的HttpStack,實現代碼以下:

// 根據API版本選擇HttpClient或者HttpURLConnection
public final class HttpStackFactory {
  // API 9
  private static final int GINGERBREAD_SDK_NUM = 9;

  /**
   * 根據SDK版本號來建立不一樣的Http執行器,即SDK 9以前使用HttpClient,以後則使用HttlUrlConnection
   * @return
   */
  public static HttpStackcreateHttpStack() {
     intruntimeSDKApi = Build.VERSION.SDK_INT;
     if (runtimeSDKApi>= GINGERBREAD_SDK_NUM) {
      return new HttpUrlConnStack();
    }
    return new HttpClientStack();
  }
}

上述代碼中,RequestQueue類中依賴的是HttpStack接口,而經過HttpStackFactory的createHttpStack函數返回的是HttpStack的實現類HttpClientStack或HttlUrlConnStack。這就是所謂的里氏替換原則,任何父類、父接口出現的地方子類均可以出現,這不就保證了可擴展性嗎!

任何實現HttpStack接口的類的對象均可以傳遞給RequestQueue實現網絡請求的功能,這樣SimpleNet執行網絡請求的方法就有不少種可能性,而不是隻有HttpClient和HttpURLConnection。例如,用戶想使用OkHttp做爲SimpleNet的執行引擎,那麼建立一個實現了HttpStack接口的OkHttpStack類,而後在該類的performRequest函數中執行網絡請求,最終將OkHttpStack對象注入RequestQueue便可。

細想一下,不少應用框架不就是這樣實現嗎?框架定義一系列相關的邏輯骨架與抽象,使得用戶能夠將本身的實現注入到框架中,從而實現變化萬千的功能。

優勢

(1)代碼共享,減小建立類的工做量,每一個子類都擁有父類的方法和屬性。

(2)提升代碼的重用性。

(3)提升代碼的可擴展性,實現父類的方法就能夠「隨心所欲」了,不少開源框架的擴展接口都是經過繼承父類來完成的。

(4)提升產品或項目的開放性。

缺點

(1)繼承是侵入性的。只要繼承,就必須擁有父類的全部屬性和方法。

(2)下降代碼的靈活性。子類必須擁有父類的屬性和方法,讓子類自由的世界中多了些約束。

(3)加強了耦合性。當父類的常量、變量和方法被修改時,必須要考慮子類的修改,並且在缺少規範的環境下,這種修改可能帶來很是糟糕的結果——大片的代碼須要重構。

3 依賴倒置原則

依賴倒置原則這個名字看着有點很差理解,「依賴」還要「倒置」,這究竟是什麼意思?依賴倒置原則的幾個關鍵點以下:

(1)高層模塊不該該依賴低層模塊,二者都應該依賴其抽象。

(2)抽象不該該依賴細節。

(3)細節應該依賴抽象。

在Java語言中,抽象就是指接口或抽象類,二者都是不能直接被實例化的。細節就是實現類、實現接口或繼承抽象類而產生的類就是細節,其特色就是能夠直接被實例化,也就是能夠加上一個關鍵字 new 產生一個對象。依賴倒置原則在 Java 語言中的表現就是:模塊間的依賴經過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是經過接口或抽象類產生的。軟件先驅們老是喜歡將一些理論定義得很抽象,弄得不是那麼容易理解,其實就是一句話:面向接口編程,或者說是面向抽象編程,這裏的抽象指的是接口或者抽象類。面向接口編程是面向對象精髓之一。

採用依賴倒置原則能夠減小類間的耦合性,提升系統的穩定性,下降並行開發引發的風險,提升代碼的可讀性和可維護性。

優勢

(1)可擴展性好。

(2)耦合度低。

4 開閉原則

開閉原則是Java世界裏最基礎的設計原則,它指導咱們如何創建一個穩定的、靈活的系統。開閉原則的定義是:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。在軟件的生命週期內,由於變化、升級和維護等緣由須要對軟件原有代碼進行修改時,

可能會給舊代碼引入錯誤。所以,當軟件須要變化時,咱們應該儘可能經過擴展的方式來實現變化,而不是經過修改已有的代碼來實現。

在軟件開發過程當中,永遠不變的就是變化。開閉原則是使咱們的軟件系統擁抱變化的核心原則之一。對擴展開放,對修改關閉這樣的高層次的歸納,即在須要對軟件進行升級、變化時應該經過擴展的形式來實現,而非修改原有代碼。固然這只是一種比較理想的狀態,是經過擴展仍是經過修改舊代碼須要根據代碼自身來定。

在SimpleNet中,開閉原則體現得比較好的是Request類族的設計。咱們知道,在開發C/S應用時,服務器返回的數據格式多種多樣,有字符串類型、xml、Json等。而解析服務器返回的Response的原始數據類型則是經過Request類來實現的,這樣就使得Request類對於服務器返回的數據格式有良好的擴展性,即Request的可變性太大。

例如,返回的數據格式是Json,那麼,使用JsonRequest請求來獲取數據,它會將結果轉成JsonObject對象,咱們看看JsonRequest的核心實現:

// 返回的數據類型爲Json的請求, Json對應的對象類型爲JSONObject
public class JsonRequest extends Request<JSONObject> {

  public JsonRequest(HttpMethod method, String url,
     RequestListener<JSONObject> listener) {
     super(method, url, listener);
}

// 將Response的結果轉換爲JSONObject
@Override
public JSONObjectparseResponse(Response response) {
    String jsonString = new String(response.getRawData());
    try {
      return new JSONObject(jsonString);
    } catch (JSONException e) {
    e.printStackTrace();
    }
    return null;
  }
}

JsonRequest經過實現Request抽象類的parseResponse解析服務器返回的結果,這裏將結果轉換爲JSONObject,而且封裝到Response類中。

例如,SimpleNet添加對圖片請求的支持,即要實現相似ImageLoader的功能。這個時候個人請求返回的數據是Bitmap圖片。所以,我須要在該類型的Request中獲得的結果是Request,但支持一種新的數據格式不能經過修改源碼的形式,這樣可能會爲舊代碼引入錯誤,可是,你又必須實現功能擴展。這就是開閉原則的定義:對擴展開放,對修改關閉。咱們看看SimpleNet是如何作的:

public class ImageRequest extends Request<Bitmap> {

  public ImageRequest(HttpMethod method, String url,
    RequestListener<Bitmap> listener) {
    super(method, url, listener);
  }

  // 將數據解析爲Bitmap
  @Override
  public Bitmap parseResponse(Response response) {
      return BitmapFactory.decodeByteArray(response.rawData,
       0, response.rawData.length);
  }

}

ImageRequest類的parseResponse函數中將Response中的原始數據轉換爲Bitmap便可。當咱們須要添加其餘數據格式時,只須要繼承自Request類,而且在parseResponse方法中將數據轉換爲具體的形式便可。這樣經過擴展的形式來應對軟件的變化或者說用戶需求的多樣性,既避免了破壞原有系統,又保證了軟件系統的可擴展性。依賴於抽象,而不依賴於具體,使得對擴展開放,對修改關閉。開閉原則與依賴倒置原則、里氏替換原則同樣,實際上最終都遵循一句話:面向接口編程。

優勢

(1)增長穩定性。

(2)可擴展性高。

5 接口隔離原則

客戶端不該該依賴它不須要的接口;一個類對另外一個類的依賴應該創建在最小的接口上。根據接口隔離原則,當一個接口太大時,咱們須要將它分割成一些更細小的接口,使用該接口的客戶端僅需知道與之相關的方法便可。

可能描述起來不是很好理解,咱們仍是以示例來增強理解吧。

咱們知道,在SimpleNet的網絡隊列中是會對請求進行排序的。SimpleNet內部使用PriorityBlockingQueue來維護網絡請求隊列,PriorityBlockingQueue須要調用Request類的compareTo函數來進行排序。試想一下,PriorityBlockingQueue其實只須要調用Request類的排序方法就能夠了,其餘的接口它根本不須要,即PriorityBlockingQueue只須要compareTo這個接口,而這個compareTo方法就是咱們上述所說的最小接口。固然,compareTo這個方法並非SimpleNet自己定義的接口方法,而是Java中的Comparable接口,但咱們這裏只是爲了學習,至於哪裏定義的可有可無:

public abstract class Request<T> implements Comparable<Request<T>> {

  /**
   * 排序方法,PriorityBlockingQueue只須要調用元素的compareTo便可進行排序
   */
  @Override
  public intcompareTo(Request<T> another) {
    Priority myPriority = this.getPriority();
    Priority anotherPriority = another.getPriority();
    // 若是優先級相等,那麼按照添加到隊列的序列號順序來執行
    return myPriority.equals(anotherPriority) ?this.getSerialNumber()
        - another.getSerialNumber()
        : myPriority.ordinal() - anotherPriority.ordinal();
} 
// 代碼省略
}

PriorityBlockingQueue類相關代碼 :

public class PriorityBlockingQueue<E> extends AbstractQueue<E>
  implements BlockingQueue<E>, java.io.Serializable {

  // 代碼省略

  // 添加元素時進行排序
  public boolean offer(E e) {
    if (e == null)
      throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    int n, cap;
    Object[] array;
    while ((n = size) >= (cap = (array = queue).length))
        tryGrow(array, cap);
        try {
           Comparator<? super E>cmp = comparator;
          // 沒有設置Comparator,則使用元素自己的compareTo方法進行排序
          if (cmp == null)
             siftUpComparable(n, e, array);
          else
             siftUpUsingComparator(n, e, array, cmp);
          size = n + 1;
          notEmpty.signal();
    } finally {
      lock.unlock();
    }
    return true;
  }

  private static <T> void siftUpComparable(int k, T x, Object[] array) {
    Comparable<? super T> key = (Comparable<? super T>) x;
    while (k > 0) {
    int parent = (k - 1) >>> 1;
      Object e = array[parent];
      // 調用元素的compareTo方法進行排序
      if (key.compareTo((T) e) >= 0)
        break;
      array[k] = e;
      k = parent;
    }
    array[k] = key;
  }
 }

從PriorityBlockingQueue的代碼可知,在元素排序時,PriorityBlockingQueue只須要知道元素是個Comparable對象便可,不須要知道這個對象是否是Request類以及這個類的其餘接口。它只須要排序,所以,只要知道它是實現了Comparable接口的對象便可,Comparable就是它的最小接口,也是經過Comparable隔離了PriorityBlockingQueue類對Request類的其餘方法的可見性。

優勢

(1)下降耦合性。

(2)提高代碼的可讀性。

(3)隱藏實現細節。

6 迪米特原則

迪米特法則也稱爲最少知識原則(Least Knowledge Principle),雖然名字不一樣,但描述的是同一個原則:一個對象應該對其餘對象有最少的瞭解。通俗地講,一個類應該對本身須要耦合或調用的類知道得最少,這有點相似接口隔離原則中的最小接口的概念。類的內部如何實現、如何複雜都與調用者或者依賴者沒有關係,調用者或者依賴者只須要知道它須要的方法便可,其餘的一律不關心。類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另外一個類的影響也越大。

迪米特法則還有一個英文解釋是:Only talk to your immedate friends(只與直接的朋友通訊)。什麼叫作直接的朋友呢?每一個對象都必然會與其餘對象有耦合關係,兩個對象之間的耦合就成爲朋友關係,這種關係的類型有不少,例如組合、聚合、依賴等。

例如,SimpleNet中的Response緩存接口的設計。

/**
 * 請求緩存接口
* @param<K> key的類型
 * @param<V> value類型
 */
public interface Cache<K, V> {
  public V get(K key);
  public void put(K key, V value);
  public void remove(K key);
}

Cache接口定義了緩存類須要實現的最小接口,依賴緩存類的對象只須要知道這些接口便可。例如,須要將Http Response緩存到內存中,而且按照LRU的規則進行存儲。咱們須要LruCache類實現這個功能,代碼以下:

// 將請求結果緩存到內存中
public class LruMemCache implements Cache<String, Response> {

  /**
   * Reponse LRU緩存
   */
  private LruCache<String, Response>mResponseCache;

  public LruMemCache() {
    // 計算可以使用的最大內存
    final intmaxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

    // 取八分之一的可用內存做爲緩存
    final intcacheSize = maxMemory / 8;
    mResponseCache = new LruCache<String, Response>(cacheSize) {
      @Override
      protected intsizeOf(String key, Response response) {
        return response.rawData.length / 1024;
      }
    };

  }

  @Override
  public Response get(String key) {
    return mResponseCache.get(key);
  }

  @Override
  public void put(String key, Response response) {
    mResponseCache.put(key, response);
  }

  @Override
  public void remove(String key) {
    mResponseCache.remove(key);
  }
}

在這裏,SimpleNet的直接朋友就是Cache或者LruMemCahce,間接朋友就是LruCache類。SimpleNet只須要直接和Cache類交互便可,並不須要知道LruCache的對象的存在,即真正實現緩存功能的對象是LruCache。這就是迪米特原則,儘可能少地知道對象的信息,只與直接的朋友交互。

優勢

(1)下降複雜度。

(2)下降耦合度。

(3)增長穩定性。

面向對象六大原則在開發過程當中極爲重要,它們給靈活、可擴展的軟件系統提供了更細粒度的指導原則。若是可以很好地將這些原則運用到項目中,再在一些合適的場景運用一些通過驗證過的設計模式,那麼開發出來的軟件在必定程度上可以獲得質量保證。其實這六大原則最終能夠簡化爲幾個關鍵詞:抽象、單一職責、最小化。那麼在實際開發過程當中如何權衡、實踐這些原則,也是須要你們在工做中不斷地思考、摸索。

設計模式

在軟件工程中,設計模式是對軟件設計中廣泛存在、反覆出現的各類問題所提出的通用解決方案。這個術語是由Erich Gamma等人在1990年從建築設計領域引入到軟件工程領域,今後設計模式在面向對象設計領域逐漸被重視起來。

設計模式並不直接用來完成代碼的編寫,而是描述在各類狀況下要如何解決軟件設計問題。面向對象設計模式一般以類或對象來描述其中的關係和相互做用,它們的相互做用可以使軟件系統具備高內聚、低耦合的特性,而且使軟件可以應對變化。

模式的4個要素

1.模式名稱

模式名稱用一兩個詞來描述模式的問題、解決方案和效果。基於一個模式詞彙表,同行、同事之間就能夠經過它們進行交流,文檔中也能夠經過模式名錶明一個設計。模式名能夠幫助咱們思考,便於咱們與其餘人交流設計思想及設計結果。

2.問題

描述了應該在什麼狀況使用設計模式。它解釋了設計問題和問題存在的來龍去脈,它可能描述了特定的設計問題,例如,某個設計不具有良好的可擴展性等,也可能描述了致使不靈活設計的類或對象結構。

3.解決方案

描述了設計的組成成分,它們之間的相互關係及各自的職責和協做方式。由於模式就像一個模板,可應用於多種不一樣場合,因此解決方案並不描述一個具體的設計或實現,而是提供設計問題的抽象描述和怎樣用一個具備通常意義的類或者對象組合來解決這個問題。

4.效果

描述了模式應用的效果及使用模式應權衡的問題。儘管咱們描述設計決策時,並不總提到模式效果,但它們對於評價設計選擇和理解使用模式的代價及好處具備重要意義。軟件效果大多關注對時間和空間的衡量,它們也表述了語言和實現問題。由於複用是面向對象設計的要素之一,因此模式效果包括它對系統的靈活性、擴充性或可移植性的影響,顯式地列出這些效果對理解和評價這些模式頗有幫助。 設計模式爲反覆出現的局部軟件設計問題指出了通用的解決方案,在很大程度上促進了面向對象軟件工程的發展。它將這些常見的設計問題一一總結,將大師們的經驗、教訓、設計經驗分享給全部人,使得即便是剛入門的工程師也可以設計出可擴展、靈活的軟件系統,大大提高了軟件質量。關於設計模式領域的書籍你們能夠參考《設計模式之禪》和《Android源碼設計模式解析與實戰》。

避免掉進過分設計的怪圈

當你掌握一些設計模式或者手法以後,比較容易出現的問題是過分設計。有的人甚至在一個應用中必定要將23種常見的設計模式運用上,這就本末倒置了。設計模式的四大要素中就明確指出,模式的運用應該根據軟件系統所面臨的問題來決定是否須要使用現有的設計。也就是說,在出現問題或者你預計會出現那樣的問題時才推薦使用特定的設計模式,而不是將各類設計模式套進你的軟件中。

無論在設計、實現、測試之間有多少時間都應該避免過分設計,它會打破你的反饋迴路,使你的設計得不到反饋,從而慢慢陷入危險中。因此你只須要保持簡單的設計,這樣就有時間來測試該設計是否真的可行,而後做出最後的決策。

當設計一款軟件時,從總體高度上設定一種架構模式,肯定應用的總體架構,而後再分析一些重要模塊的設計思路,而且保證它們的簡單性、清晰性,若是有時間可使用Java代碼模擬一個簡單的原型,確保設計是可行的,最後就能夠付諸行動了。切記不要過分地追求設計,適當就好,當咱們發現或者預計到將要出現問題時再判斷是否須要運用設計模式。

反模式是一種文字記錄形式,描述了對某個問題必然產生的消極後果的常看法決方案。因爲管理人員或者開發人員不知道更好的解決方案,缺少決定特定問題所需的經驗或知識,或者說不適合的條件下套用了某個設計模式,這些都會形成反模式。與設計模式相似,反模式描述了一個通常的形式、主要緣由、典型症狀、後果,以及最後如何經過重構解決問題。

反模式是把通常狀況映射到一類特定解決方案的有效方法。反模式的通常形式爲它所針對的哪類問題提供了一個易於辨識的模板。此外,它還清楚地說明了與該問題相關聯的症狀以及致使這一問題的內在緣由。這些模板元素完整地說明了反模式存在的狀況。這個通常形式能夠減小使用設計模式時最爲常見的問題:把特定設計模式應用於不正確的環境。

反模式爲識別軟件行業反覆出現的問題提供了實際經驗,併爲大多數常見的問題提供了詳細的解決方案。反模式對業界常見的問題進行總結,而且告訴你如何識別這些問題以及如何解決。它有效地說明了能夠在不一樣層次上採起的措施,以便改善應用開發過程、軟件系統和對軟件項目的有效管理。

總的來講,設計模式總結了在特定問題下正確的解決方案,而反模式則是告訴你在特定的問題上的錯誤解決方案以及它們的緣由、解決方案,經過最終的解決方案,它可以將腐化的軟件系統拉回正軌。

然而,任何一篇文章甚至一本書都不足以使你快速地成長爲面向對象專家。靈活的軟件設計須要知識、經驗與思考,好的設計一般是經歷了時間的洗禮慢慢演化而來,工程師的成長也是同樣。所以,掌握必要的面向對象、設計模式、反模式等知識,而且在工做中不斷實踐、思考,將使你的軟件設計之路走得更從容、順暢。

本文節選自 《Android開發進階:從小工到專家》

做者簡介

何紅輝 : 前友盟(阿里巴巴集團)Android工程師,CSDN博客專家,活躍於國內各大技術社區,熱愛開源,熱愛技術,熱愛分享。Android事件總線開源庫(AndroidEventBus)、Colorful做者,開發技術前線( www.devtf.cn )站長。

本書特點

阿里巴巴高級工程師、CSDN博客專家、暢銷書做者撰寫,百度、騰訊等專家推薦的精品圖書。

一本只有乾貨,摒棄了之前圖書教程式的寫法好書。

講解知識點再也不是大水滿灌形式,本書主要是結合做者多年開發經驗的總結,把做者之前開發走過的坑和陷阱講解出來,讀者看了之後能夠少走不少彎路,提高本身的開發能力很快,抓住讀者的痛點和需求講解內容,使讀者閱讀後頗有成就感。

除了全面講解了Android開發知識外,還對單元測試、代碼規範、版本控制、重構、架構等重要知識點進行了講解,使得讀者在深刻技術的同時開闊眼界,可以以更專業的方式設計應用軟件,完成從只會實現功能的「碼農」到軟件工程師、設計師的過渡。

相關文章
相關標籤/搜索