面向對象六大原則----單一職責原則

Java 中面向對象編程六大原則:

單一職責原則 英文名稱是Single Responsibility Principle,簡稱SRP

開閉原則 英文全稱是Open Close Principle,簡稱OCP

里氏替換原則 英文全稱是Liskov Substitution Principle,簡稱LSP

依賴倒置原則  英文全稱是Dependence Inversion Principle,簡稱DIP
接口隔離原則 英文全稱是InterfaceSegregation Principles,簡稱ISP
迪米特原則 英文全稱爲Law of Demeter,簡稱LOD,也稱爲最少知識原則(Least Knowledge Principle)


讓你的代碼更清晰簡單——單一職責原則

單一職責原則的英文名稱是Single Responsibility Principle,簡稱SRP。它的定義是:就一個類而言,應該僅有一個引起它變化的原因。簡單來說,一個類中應該是一組相關性很高的函數、數據的封裝。單一職責的劃分界限並不是總是那麼清晰,很多時候都是需要靠個人經驗來界定。當然,最大的問題就是對職責的定義,什麼是類的職責,以及怎麼劃分類的職責。 

下面以項目中實際代碼來分析問題,在android app中圖片加載是最常見的,於是我們自己動手寫一個ImageLoader(圖片加載)作爲訓練項目。如下代碼:

public class ImageLoader {
    private static ImageLoader Instance = null;
    // 圖片緩存
    LruCache<String, Bitmap> mImageCache;
    // 線程池,線程數量爲CPU的數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());
    //在主線程中顯示圖片
    Handler mHandler = new Handler(){ 
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            ImageView imageView = (ImageView) msg.obj;
            imageView.setImageBitmap((Bitmap) imageView.getTag());
        }
    };

    public static ImageLoader getInstance(){
        if(Instance == null){
            synchronized (ImageLoader.class) {
                if(Instance == null) {
                    Instance = new ImageLoader();
                }
            }
        }
        return Instance;
    }
    private ImageLoader() {
        initImageCache();
    }

    private void initImageCache() {
        // 計算可使用的最大內存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // 取四分之一的可用內存作爲緩存
        final int cacheSize = maxMemory / 4;
        mImageCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                //Bitmap的每一行所佔用的空間數乘以Bitmap的行數
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }

    public  void displayImage(final String url, final ImageView imageView) {
        imageView.setTag(url);
        //先從cache中取圖片
        if(mImageCache.get(url)!=null){
            imageView.setImageBitmap(mImageCache.get(url));
            return;
        }
        mExecutorService.submit(new Runnable() {
            @Override
            public  void run() {
                Bitmap bitmap = downloadImage(url);
                if (bitmap == null) {
                    return;
                }
                if (imageView.getTag().equals(url)) {
                    Message msg = mHandler.obtainMessage();
                    imageView.setTag(bitmap);
                    msg.obj = imageView;
                    mHandler.sendMessage(msg);
                }
                mImageCache.put(url, bitmap);
            }
        });
    }

    public  Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setDoInput(true); //允許輸入流,即允許下載
            conn.setUseCaches(false); //不使用緩衝
            conn.setRequestMethod("GET"); //使用get請求
            InputStream is = conn.getInputStream();   //獲取輸入流,此時才真正建立鏈接
            bitmap = BitmapFactory.decodeStream(is);
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}



上面是一個最簡單的ImageLoader,在顯示圖片之前會去判斷是否有緩存,但是我們也發現它耦合太嚴重啦!簡直就沒有設計可言,更不要說擴展性、靈活性了。所有的功能都寫在一個類裏,這樣隨着功能的增多,ImageLoader類會越來越大,代碼也越來越複雜,修改起來就是進入hell.

這裏我們可以把ImageCahe相關的代碼單獨拿出來,寫成一個單獨的類。

public class ImageCache {
    // 圖片緩存
    LruCache<String, Bitmap> mImageCache;
    public ImageCache(){
        initImageCache();
    }
    private void initImageCache() {
        // 計算可使用的最大內存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // 取四分之一的可用內存作爲緩存
        final int cacheSize = maxMemory / 4;
        mImageCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                //Bitmap的每一行所佔用的空間數乘以Bitmap的行數
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
    }
    
    public void put(String key,Bitmap value){
        mImageCache.put(key,value);
    }
    
    public Bitmap get(String key){
       return mImageCache.get(key);
    }
}

所以ImageLoader代碼需要修改,並且添加了一個ImageCache類用於處理圖片緩存,具體代碼如下:

public class ImageLoader {
    private static ImageLoader Instance = null;
    private ImageCache mImageCache;
    // 線程池,線程數量爲CPU的數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());
    //在主線程中顯示圖片
    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            ImageView imageView = (ImageView) msg.obj;
            imageView.setImageBitmap((Bitmap) imageView.getTag());
        }
    };

    public static ImageLoader getInstance(){
        if(Instance == null){
            synchronized (ImageLoader.class) {
                if(Instance == null) {
                    Instance = new ImageLoader();
                }
            }
        }
        return Instance;
    }
    private ImageLoader() {
       mImageCache = new ImageCache();
    }

    public  void displayImage(final String url, final ImageView imageView) {
        imageView.setTag(url);
        //先從cache中取圖片
        if(mImageCache.get(url)!=null){
            imageView.setImageBitmap(mImageCache.get(url));
            return;
        }
        mExecutorService.submit(new Runnable() {
            @Override
            public  void run() {
                Bitmap bitmap = downloadImage(url);
                if (bitmap == null) {
                    return;
                }
                if (imageView.getTag().equals(url)) {
                    Message msg = mHandler.obtainMessage();
                    imageView.setTag(bitmap);
                    msg.obj = imageView;
                    mHandler.sendMessage(msg);
                }
                mImageCache.put(url, bitmap);
            }
        });
    }

    public  Bitmap downloadImage(String imageUrl) {
        Bitmap bitmap = null;
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setDoInput(true); //允許輸入流,即允許下載
            conn.setUseCaches(false); //不使用緩衝
            conn.setRequestMethod("GET"); //使用get請求
            InputStream is = conn.getInputStream();   //獲取輸入流,此時才真正建立鏈接
            bitmap = BitmapFactory.decodeStream(is);
            conn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }
}

和上述代碼所示,將ImageLoader一拆爲二,ImageLoader只負責圖片加載的邏輯,而ImageCache只負責處理圖片緩存的邏輯,這樣ImageLoader的代碼量變少了,職責也清晰了,當與緩存相關的邏輯需要改變時,不需要修改ImageLoader類,而圖片加載的邏輯需要修改時也不會影響到緩存處理邏輯。

從上述的例子中我們能夠體會到,單一職責所表達出的用意就是“單一”二字。正如上文所說,如何劃分一個類、一個函數的職責,每個人都有自己的看法,這需要根據個人經驗、具體的業務邏輯而定。但是,它也有一些基本的指導原則,例如,兩個完全不一樣的功能就不應該放在一個類中。一個類中應該是一組相關性很高的函數、數據的封裝。工程師可以不斷地審視自己的代碼,根據具體的業務、功能對類進行相應的拆分。

代碼github地址:點擊打開鏈接

更多精彩Android技術可以關注我們的微信公衆號,掃一掃下方的二維碼或搜索關注公共號: Android老鳥

                                                

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章