經過代碼示例來學習面向對象六大原則

或許有的掘友們發現了,在閱讀 Android 系統底層源碼或者開源框架源碼時,發現內部大量的設計模式,若是你對設計模式不懂的話,那麼閱讀源碼真的是步履維艱。那麼這篇文章咱們先來學習面向對象的六大原則,設計模式大概 23 種,後面咱們一步一步來學習它。html

單一職責原則

單一職責原則的英文名稱是 Single Responsibility Principle ,縮寫是 SRP 。 SRP 的定義是:就一個類而言,應該僅有一個引發變化的緣由。簡單的來講,就是一個類中應該是一組相關性很高的函數、數據的封裝。單一職責的劃分界限也並非那麼的清晰,不少時候都是靠我的經驗來給定界限,固然,最大的的問題就是對職責的定義,什麼是類的職責,以及怎麼劃分類的職責。java

下面咱們就以 圖片加載庫 的例子代碼來對類的職責簡單說明下,在設計一個圖片加載庫以前,咱們須要先大概畫下 UML 類圖,有了 UML 圖以後寫代碼就能更加的清晰。android

從上面 UML 類圖能夠看出 ImageLoader 只負責加載圖片,MemoryCache 實現 IImageCache 負責往內存中存/取緩存,到這裏也許有的同窗對單一職責有了必定概念了,相信看完下面的代碼,你已經對單一職責掌握的差很少了,直接上代碼git

public class ImageLoader {


    /** * 內存緩存 */
    private IImageCache mMemoryCache;
    
        /** * 圖片下載 */
    private IDownloader mImageDownloader;

    /** * 線程池 */
    private ExecutorService mExecutorService;

    /** * 主線程管理 */
    private Handler mHandler = new Handler(Looper.getMainLooper());

    private static ImageLoader instance;

    public static ImageLoader getInstance() {
        if (instance == null)
            instance = new ImageLoader();

        return instance;
    }

    public ImageLoader() {
         //圖片緩存
       this. mMemoryCache = new MemoryCache();
       //圖片下載
       this.mImageDownloader = new HttpURLConnectionDownloaderImp();
        //線程池,線程數據量爲 CPU 的數量
       this.mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    /** * 加載圖片 */
    public void loadImage(final String url, final ImageView imageView) {
        Bitmap bitmap = mMemoryCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }

        imageView.setTag(url);

        //若是內存緩存中沒有圖片,就開啓網絡請求去下載
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap imager = mImageDownloader.downLoader(url);
                if (imager == null) return;
                if (imageView.getTag().equals(url)) {
                    displayImage(imager, imageView);
                }
                mMemoryCache.put(url,imager);
            }
        });

    }

    /** * 顯示圖片 * * @param downBitmap * @param imageView */
    private void displayImage(final Bitmap downBitmap, final ImageView imageView) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                imageView.setImageBitmap(downBitmap);
            }
        });
    }

}
複製代碼
public class MemoryCache implements IImageCache {

    /** * 初始化內存緩存 */
    private LruCache<String, Bitmap> mMemoryLru;

    public MemoryCache() {
        init();
    }

    private void init() {
        int currentMaxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        //內存緩存的大小
        int cacheSize = currentMaxMemory / 4;
        mMemoryLru = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
    }

    @Override
    public void put(String url, Bitmap bitmap) {
        mMemoryLru.put(url,bitmap);
    }

    @Override
    public Bitmap get(String url) {
        return mMemoryLru.get(url);
    }
}

複製代碼

經過上面代碼能夠看出 ImageLoader 負責圖片加載的邏輯,而 MemoryCache 負責緩存,這 2 個類職責分明,就像公司裏面不一樣部門幹不一樣的活同樣。可是,若是這 2 類寫在一塊兒的話,缺點一下就出來了,不只功能職責不分明,並且代碼也比較臃腫,耦合過重。 如今雖然代碼結構變得清晰,職責也分明瞭,可是可擴展性還須要進一步優化,下面咱們就來慢慢優化吧。github

開閉原則

開閉原則英文全稱是 Open Close Principle,縮寫 OCP ,它是 Java 世界裏最基礎的設計原則,它指導咱們如何創建一個穩定的、靈活的系統。編程

開閉原則的定義是:軟件中的對象 (類、模塊、函數等) 應該對於擴展是開放的,可是,對於修改是封閉的 這就是開放-關閉原則。設計模式

上一小節的 ImageLoader 職責單一,結構清晰,應該算是一個不錯的開始了,可是 Android 中應用內存是有限制的,當應用從新啓動,那麼原有的緩存就不在了。如今咱們加上本地磁盤緩存,爲了聽從開閉原則的思想,我又對 ImageLoader 從新設計了。緩存

cVE3M.png

public class ImageLoader {
    private String TAG = getClass().getSimpleName();


    /** * 默認內存緩存 */
    private IImageCache mMemoryCache;

    /** * 線程池 */
    private ExecutorService mExecutorService;

    /** * 主線程管理 */
    private Handler mHandler = new Handler(Looper.getMainLooper());

    private static ImageLoader instance;

    public static ImageLoader getInstance() {
        if (instance == null)
            instance = new ImageLoader();

        return instance;
    }

    public ImageLoader() {
        mMemoryCache = new MemoryCache();
        //線程池,線程數據量爲 CPU 的數量
        mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    /** * 用戶配置緩存策略 * * @param imageCache */
    public void setImageCache(IImageCache imageCache) {
        this.mMemoryCache = imageCache;
    }

    /** * 加載圖片 */
    public void loadImage(final String url, final ImageView imageView) {
       .....

    }

    /** * 顯示圖片 * * @param downBitmap * @param imageView */
    private void displayImage(final Bitmap downBitmap, final ImageView imageView) {
     .....

    }

}

複製代碼
//磁盤緩存
public class DiskCache implements IImageCache {

    private DiskLruCache mDiskLruCache;

    private static final int MAX_SIZE = 10 * 1024 * 1024;//10MB
    //IO緩存流大小
    private static final int IO_BUFFER_SIZE = 8 * 1024;

    //緩存個數
    private static final int DISK_CACHE_INDEX = 0;

    public DiskCache(Context context) {
        try {
            File cacheDir = CacheUtils.getDiskCacheDir(context, "bitmapCache");
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            mDiskLruCache = DiskLruCache.open(cacheDir, ImageLoaderUtils.getAppVersion(context), 1, MAX_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void put(String url, Bitmap bitmap) {
        OutputStream outputStream = null;
        DiskLruCache.Snapshot snapshot = null;
        BufferedOutputStream out = null;
        BufferedInputStream in = null;
        String key = ImageLoaderUtils.hashKeyForDisk(url);
        try {
            snapshot = mDiskLruCache.get(key);
            if (snapshot != null) {
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if (editor != null) {
                    outputStream = editor.newOutputStream(DISK_CACHE_INDEX);

                    InputStream inputStream = ImageLoaderUtils.bitmap2InputStream(bitmap, 50);

                    in = new BufferedInputStream(inputStream, IO_BUFFER_SIZE);
                    out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);
                    int b;
                    while ((b = in.read()) != -1) {
                        out.write(b);
                    }
                    editor.commit();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (snapshot != null) {
                    snapshot.close();
                }
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    @Override
    public Bitmap get(String url) {
        //經過key值在緩存中找到對應的Bitmap
        Bitmap bitmap = null;
        String key = ImageLoaderUtils.hashKeyForDisk(url);
        try {
            DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
            if (snapshot == null) return null;
            //獲得文件輸入流
            InputStream fileInputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
            if (fileInputStream != null)
                bitmap = BitmapFactory.decodeStream(fileInputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }


}
複製代碼
public class DoubleCache implements IImageCache {

    private String TAG = getClass().getSimpleName();

    /**
     * 內存緩存
     */
    private IImageCache mMemoryCache;

    /**
     * 磁盤緩存
     */
    private IImageCache mDiskCache;

    public DoubleCache(Context context) {
        this.mMemoryCache = new MemoryCache();
        this.mDiskCache = new DiskCache(context);
    }

    @Override
    public void put(String key, Bitmap bitmap) {
        mMemoryCache.put(key, bitmap);
        mDiskCache.put(key, bitmap);
    }

    @Override
    public Bitmap get(String url) {
        Bitmap bitmap = mMemoryCache.get(url);
        if (bitmap != null) {
            Log.i(TAG,"使用內存緩存");
            return bitmap;
        }
        Log.i(TAG,"使用磁盤緩存");
        return mDiskCache.get(url);
    }
}

複製代碼
public interface IImageCache {

    /** * 存圖片 */
    void put(String url, Bitmap bitmap);

    /** * 獲取圖片 */
    Bitmap get(String url);
}
複製代碼

IImageCache 接口簡單定義了 存儲/獲取 兩個函數,緩存的 url 就是圖片網絡地址,值就是緩存的圖片,通過此次重構咱們擴展了內存/磁盤緩存,細心的同窗可能注意到了, ImageLoader 類中增長了一個 setImageCache (IImageCache cache) 函數,用戶能夠經過該函數來設置緩存,也就是一般說的依賴注入。下面看看怎麼配置:bash

public void config() {
        //使用雙緩存
  ImageLoader.getInstance().setImageCache(new DoubleCache(getApplicationContext()));

        //用戶自定義
  ImageLoader.getInstance().setImageCache(new IImageCache() {
            @Override
    public void put(String url, Bitmap bitmap) {
                
    }

            @Override
    public Bitmap get(String url) {
     return null;
     }
        });
    }
複製代碼

在上述代碼中,經過 setImageCache() 方法注入不一樣的緩存實現,這樣不只可以使 ImageLoader 更簡單,健壯,也使得 ImageLoader 的可擴展性,靈活性能高,MemoryCache 、DiskCache 、DoubleCache 緩存圖片的具體實現徹底同樣,可是,他們的一個特色是都實現了 ImageCache 接口,而且經過 setImageCache() 注入到 IImageCache 中,這樣就實現了變幻無窮的緩存策略,且擴展不會致使內部的修改,哈哈,這就是咱們以前所說的開閉原則。網絡

里氏替換原則

里氏替換原則英文全稱是 Liskov Substitution Principle , 縮寫是 LSP。LSP 的第一種定義是:若是對每個類型爲 S 的對象 O1, 都有類型爲 T 的對象 O2, 使得以 T 定義的全部程序 P 在全部的對象 O1都替換成 O2 時,程序 P 的行爲沒有發生變化,那麼類型 S 是類型 T 的子類型。上面這種描述確實有點很差理解,咱們再來看第二種里氏替換原則定義:全部引用基類的地方必須能透明地使用其子類的對象

咱們知道,面嚮對象語言的三大特色是 繼承,封裝,多態,里氏替換原則就是依賴於 繼承,多態這兩大特性。里氏替換原則通俗來講的話就是,只要父類能出現的地方子類就能夠出現,並且替換爲子類也不會產生任何錯誤或異常,使用者可能根本不用知道是父類仍是子類,可是反過來就不行了,有子類出現的地方,父類就不必定能適應,說了這麼多,其實最終總結就兩個字:抽象

爲了咱們可以深刻理解直接看下面代碼示例吧:

cV5lU.png

//框口類
public class Window{
  public void show(View view){
    view.draw();
  }
}

//創建視圖對象,測量視圖的寬高爲公用代碼,繪製實現交給具體的子類
pubic abstract class View{
  public abstract void draw();
  public vid measure(int width,int height){
    //測量視圖大小
  }
}

public class ImageView extends View{
  draw{
    //繪製圖片
  }
}

... extends View{
  ...
}
複製代碼

上述示例代碼中, Window 依賴於 View , 而 View 定義了一個視圖抽象, measure 是各個子類共享的方法,子類經過重寫 View 的draw 方法實現具備各自特點的功能,在這裏,這個功能就是繪製自身的內容,在任何繼承 View 類的子類均可以傳遞給 show 函數,這就是所說的里氏替換。

里氏替換原則的核心原理是抽象,抽象又依賴於繼承這個特性,在 OOP 當中,繼承的優缺點都至關明顯,優勢:

  1. 代碼複用,減小建立類的成本,每一個子類都擁有父類的方法和屬性;
  2. 子類於父類基本類似,但又與父類有所區別;
  3. 提升代碼的可擴展性;

繼承的缺點:

  1. 繼承是侵入性的,只要繼承就必須擁有父類的全部屬性和方法;
  2. 可能形成子類代碼冗餘,靈活性下降,由於子類必須擁有父類的屬性和方法。

事務都是都利和弊,須合理利用。

繼續拿上面的 ImageLoader 緩存策略來講明裏氏替換原則,用戶只須要指定具體的緩存對象就能夠經過 ImageCache 的 setImageCache() 函數就能夠替換 ImageLoader 的緩存策略,這就使得 ImageLoader 的緩存系統有了無限的可能性,也保證了可擴展性。

開閉和里氏每每是生世相依,不離不棄,經過里氏替換來達到程序的擴展,對修改的關閉效果。然而,這兩個原則都同時強調了一個 OOP 的重要性 - 抽象,所以,在開發過程當中,運用抽象是走向代碼優化的重要一步。

依賴倒置原則

依賴倒置原則英文全稱是 Dependence Inversion Principle, 簡寫 DIP 。依賴倒置原則指代了一種特定的解耦形式,使得高層次的模塊不依賴於底層次模塊的實現細節的目的,依賴模塊被顛倒了。這個概念有點很差理解,這究竟是什麼意思勒?

依賴倒置有幾個關鍵點:

  1. 高層模塊不該該依賴底層模塊,二者都應該依賴起抽象;
  2. 抽象不該該依賴細節;
  3. 細節應該依賴抽象;

在 Java 語言中,抽象就是接口或抽象類,二者都是不能直接被實例化的;細節就是實現類,其特色就是能夠直接實例化,也就是能夠加上一個 new 關鍵字產生一個對象。高層模塊就是調用端,底層模塊就是具體實現類。依賴倒置原則在 Java 語言中的表現就是: 模塊間的依賴經過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是經過接口或抽象類產生的 ,這又是一個將理論抽象化的實例,其實一句話能夠歸納:面向接口編程,或者說是面向抽象編程,面向接口編程是面向對象精髓之一,也就是上面兩節強調的抽象。

這裏咱們仍是以 ImageLoader 來講明,先看下面代碼:

public class ImageLoader {
    private String TAG = getClass().getSimpleName();


    /** * 默認內存緩存(直接依賴於細節,而不是抽象) */
    private MemoryCache mMemoryCache;

    /** * 線程池 */
    private ExecutorService mExecutorService;

    /** * 主線程管理 */
    private Handler mHandler = new Handler(Looper.getMainLooper());

    private static ImageLoader instance;

    public static ImageLoader getInstance() {
        if (instance == null)
            instance = new ImageLoader();

        return instance;
    }

    public ImageLoader() {
        mMemoryCache = new MemoryCache();
        //線程池,線程數據量爲 CPU 的數量
        mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    /** * 用戶配置緩存策略 * * @param imageCache */
    public void setImageCache(MemoryCache imageCache) {
        this.mMemoryCache = imageCache;
    }

...
}
複製代碼

上面代碼 ImageLoader 直接依賴於細節 MemoryCache ,若是框架升級需有多級緩存也就是內存 + SD 卡緩存策略,那麼就又須要改 ImageLoader 中的代碼,以下:

public class ImageLoader {
    private String TAG = getClass().getSimpleName();


    /** * 默認內存緩存(直接依賴於細節,而不是抽象) */
    private DoubleCache mMemoryCache;

    /** * 線程池 */
    private ExecutorService mExecutorService;

    /** * 主線程管理 */
    private Handler mHandler = new Handler(Looper.getMainLooper());

    private static ImageLoader instance;

    public static ImageLoader getInstance() {
        if (instance == null)
            instance = new ImageLoader();

        return instance;
    }

    public ImageLoader() {
        mMemoryCache = new DoubleCache();
        //線程池,線程數據量爲 CPU 的數量
        mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    /** * 用戶配置緩存策略 * * @param imageCache */
    public void setImageCache(DoubleCache imageCache) {
        this.mMemoryCache = imageCache;
    }

...
}
複製代碼

在 ImageLoader 中咱們把默認內存緩存改爲了雙緩存,這樣不只違背了沒有開閉原則,也沒有依賴於抽象,因此下面的代碼纔是正確的:

public class ImageLoader {
    private String TAG = getClass().getSimpleName();
    /** * 默認內存緩存 默認依賴於抽象 */
    private IImageCache mMemoryCache;

    private static ImageLoader instance;

    public static ImageLoader getInstance() {
        if (instance == null)
            instance = new ImageLoader();

        return instance;
    }

    public ImageLoader() {
     ...
    }

    /** * 用戶配置緩存策略 注入抽象類 * * @param imageCache */
    public void setImageCache(IImageCache imageCache) {
        this.mMemoryCache = imageCache;
    }
}
複製代碼

在這裏實現類沒有發生直接的依賴,而是經過抽象發生的依賴。知足了依賴倒置基本原則,想要讓程序更爲靈活,那麼抽象就是邁出靈活的第一步。

接口隔離原則

接口隔離原則英文全稱是 InterfaceSegregation Principles, 縮寫 ISP 。接口隔離原則的目的是系統解耦,從而容易重構、更改和從新部署。說白了就是讓客服端依賴的接口儘量地小,這樣說可能還有點抽象,仍是以一個示例說明一下

未優化的接口

public class DiskCache implements IImageCache {

    private DiskLruCache mDiskLruCache;

    private static final int MAX_SIZE = 10 * 1024 * 1024;//10MB
    //IO緩存流大小
    private static final int IO_BUFFER_SIZE = 8 * 1024;

    //緩存個數
    private static final int DISK_CACHE_INDEX = 0;

   
    @Override
    public void put(String url, Bitmap bitmap) {
       .....
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (snapshot != null) {
                    snapshot.close();
                }
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}
複製代碼

能夠看見上面一段代碼雖然功能達到了要求,可是各類 try...catch 嵌套,不經影響代碼美觀,並且可讀性差。咱們能夠看 Cloaseable 這個類的實現差很少 160 多個實現類,若是每一個類都 close 那不的瘋了,咱們直接抽取一個 CloseUtils 以下:

public class CloaseUtils {

    public static void close(Closeable... closeable) {
        if (closeable != null) {
            try {
                if (closeable.length == 1){
                    closeable[0].close();
                    return;
                }
                for (int i = 0; i < closeable.length; i++) {
                    closeable[i].close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
複製代碼

支持同時關閉一個,或多個實現類的 close。

改造以後的代碼:

public class DiskCache implements IImageCache {

    private DiskLruCache mDiskLruCache;

    private static final int MAX_SIZE = 10 * 1024 * 1024;//10MB
    //IO緩存流大小
    private static final int IO_BUFFER_SIZE = 8 * 1024;

    //緩存個數
    private static final int DISK_CACHE_INDEX = 0;

   
    @Override
    public void put(String url, Bitmap bitmap) {
       .....
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
      		 CloaseUtils.close(snapshot,out,in);
        }
    }
}
複製代碼

是否是清爽多了,一行代碼解決了剛剛差很少 10 行代碼的邏輯。並且這裏基本原理就是依賴於 Closeable 抽象,而不是具體實現類(這不就是咱們剛剛纔說了的依賴倒置原則嘛),而且創建在最小化依賴原則的基礎上,它只須要知道這個對象是否關閉,其它一律不關心,也就是這裏的接口隔離原則。

迪米特原則

迪米特原則英文的全稱爲 Law of Demeter , 縮寫是 LOD , 也稱爲最少知識原則。雖然名字不一樣,但描述的是同一個原則:一個對象應該對其餘對象有最少的的瞭解。通俗的將,一個類應該對本身須要耦合或調用的類知道的最少,類的內部如何實現與調用者或者依賴者沒有關係,調用者或者依賴着只須要知道它須要的方法便可,其餘的可一律不用管。類與類之間關係密切,耦合度就越大,當一個類發生改變時,對另外一個類的影響也越大。

下面以一個租房例子說明:

cZ85X.png

/**房子*/
public class Room {
  //面積
  public float area;
  //價錢
  public float price;
  
  public Room(float area,float price){
    this.area = area;
    this.price = price;
  }
}
複製代碼
/**中介*/
public class Mediator{
  List <Room> mRooms = new ArrayList<Room>();
  
  public Mediator(){
    for(i = 0; i < 5 ; i ++){
      mRoom.add(new Room(14 + i,(14 + i) * 150));
    }
  }
  
  public List<Room> getAllRooms(){
    return mRooms;
  }
}
複製代碼
/**租客**/
public class Tenant {
  public void rentRoom(float roomArea,float roomPrice,Mediator mediator){
    List<Room> rooms = mediator.getAllRooms();
  	for(Room room : rooms){
      if(isSuitable(roomArea,roomPrice,room)){
        Log.i(TAG,"租到房子了");
      bread;
      }
    }
  }
  
  //租金要小於等於指定的值,面積要大於等於指定的值
  public boolean isSuitable(float roomArea,float roomPrice,Room room){
    return room.price <= roomPrice && room.area >= roomArea;
  }
}
複製代碼

上面的代碼中能夠看到,Tenant 不只依賴了 Mediator 類,還須要頻繁得於 Room 類打交道。租客只是找一個房子而已,若是把這些功能都放在 Tenant 類裏面,那中介都沒有存在感了吧?耦合過重了,咱們只須要跟中介通訊就好了,繼續重構代碼;

-2.png

//中介
public class Mediator{
  List<Room> mRooms = new ArrayList<Room>();
  
  /**構造房子**/
  public Mediator(){
    for(i = 0; i < 5 ; i ++){
      mRoom.add(new Room(14 + i,(14 + i) * 150));
    }
  }
  
  public Room rentOut(float area,float price){
    for(Room room : mRooms){
      if(isSuitable(area,price,room)){
        return room;
      }
    }
    return null;
  }
public boolean isSuitable(float area,float price ,Room room){
  return room.price <= price && room.area >= area
	}
}
複製代碼
//租客
public class Tenant{
  /**是否租到房子了*/
  public Room rentRoom(float roomArea,float roomPrice,Mediator mediator){
    return mediator.rentOut(roomArea,roomPrice);
  }
}
複製代碼

根據上面的重構優化,咱們得出結構,租客只須要跟中介通訊,主要關心中介那裏有沒有我須要的房子,而中介勒就去他的資源庫裏面去找,有沒有租客須要的房子,每一個對象作的事兒明確。「只與直接有關係的聯繫」 這簡單的幾個字就可以將咱們從複雜的關係網中抽離出來,使程序耦合度更低,穩定性更好。

總結

從六大原則中咱們得出了重要的結論,就是必定要有抽象的思惟,面向抽象或面向接口編程。在應用開發過程當中,最難的不是完成開發工做,而是後續的維護和迭代工做是否擁有可變性,擴展性,在不破壞系統的穩定性前提下依然保持 二高一低原則(高擴展,高內聚,低耦合) 在經歷多個版本的迭代項目依然保持清晰,靈活,穩定的系統架構。固然這是咱們一個比較理想的狀況,可是咱們須要往這個方向去實現努力,就至關於接口(想法)出來了,咱們要去實現(接口實現類)它,遵循面向對象六大原則就是咱們走向靈活軟件之路所邁出的第一步,加油!

代碼地址

特別感謝

《 Android 源碼設計模式解析與實戰 》

相關文章
相關標籤/搜索