1. 程式人生 > >設計模式-00 6大設計原則

設計模式-00 6大設計原則

1 單一職責原則
單一職責的定義是:就一個類而言,應阿蓋僅有一個引起他變化的原因。簡單來說,一個類中應該是一組相關性和高的函式,資料的封裝。但是由於每個人的理解不一樣。那麼劃分類的界限也不一樣。
下面是一個示例:

/**
 * 圖片載入類
 */

public class ImageLoader {
    //圖片快取
    LruCache<String,Bitmap> mImageCache;

    //執行緒池,執行緒數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    public
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) { return bitmap.getRowBytes()*bitmap.getHeight()/1024; } }; } public void displayImage(final String url, final ImageView imageView){ Bitmap bitmap = mImageCache.get(url); if (bitmap != null
) { imageView.setImageBitmap(bitmap); return; } imageView.setTag(url); mExecutorService.submit(new Runnable() { @Override public void run() { Bitmap bitmap = downloadImage(url); if(bitmap==null){ return; } if(imageView.getTag().equals(url)){ imageView.setImageBitmap(bitmap); } mImageCache.put(url,bitmap); } }); } public Bitmap downloadImage(String imageUrl){ Bitmap bitmap = null; try{ URL url = new URL(imageUrl); final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); bitmap = BitmapFactory.decodeStream(connection.getInputStream()); connection.disconnect(); }catch(Exception e){ e.printStackTrace(); } return bitmap; } }

上面是一個圖片載入的類。使用該類直接呼叫31行方法displayImage(final String url, final ImageView imageView)就能給ImageView賦值。那麼該類中把快取相關的邏輯也加入其中。如第七行定義了LruCache型別的變數。並在16行的initImageCache()進行了初始化。那麼可以把ImageCache單獨提取到一個類中。這樣就可以降低程式碼的耦合。下面是分開後的程式碼:

使用單一原則修改後

/**
 * 圖片快取類
 */

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) {
                return bitmap.getRowBytes()*bitmap.getHeight()/1024;
            }
        };
    }

    public void put(String url,Bitmap bitmap){
        mImageCache.put(url,bitmap);
    }

    public Bitmap get(String url){
        return  mImageCache.get(url);
    }

}
/**
 * 圖片載入類
 */

public class ImageLoader {
    //圖片快取
    ImageCache mImageCache = new ImageCache() ;

    //執行緒池,執行緒數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());


    public void displayImage(final String url, final ImageView imageView){
     Bitmap bitmap = mImageCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        imageView.setTag(url);

        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(url);
                if(bitmap==null){
                    return;
                }
                if(imageView.getTag().equals(url)){
                    imageView.setImageBitmap(bitmap);
                }

                mImageCache.put(url,bitmap);

            }
        });

    }

    public Bitmap downloadImage(String imageUrl){
        Bitmap bitmap = null;
        try{
            URL url = new URL(imageUrl);
            final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(connection.getInputStream());
            connection.disconnect();

        }catch(Exception e){
            e.printStackTrace();
        }
        return  bitmap;
    }


}

其實就是把ImageCache這個類提取出來。使程式碼更簡潔。用設計原則來說這是單一職責原則

2 開閉原則
開閉原則的定義是:軟體中的物件應該是對於擴充套件是開放的。但是對於修改是關閉的。因為在該原來版本的程式碼時,如果修改了原來的程式碼可能會破壞原來的程式碼。

還是原來的程式碼,現在又加入了新的快取方式,SD卡快取。

/**
 * SD卡快取
 */

public class DiskCache {
    static String cacheDir = "sdcard/cache/";

    //從快取中獲取圖片
    public Bitmap get(String url){
        return BitmapFactory.decodeFile(cacheDir+url);
    }

    //將圖片快取到記憶體中
    public void put(String url,Bitmap bitmap){
        FileOutputStream fileOutputStream = null;
        try{
            fileOutputStream = new FileOutputStream(cacheDir+url);
            bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if(fileOutputStream!=null){
                try{
                    fileOutputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

快取方式改變後,又要重新更改ImageLoader中獲取快取的方式。

 //是否使用SD卡快取
    boolean isUsedDisCache  = false;
 //SD卡快取
    DiskCache mDiskCache = new DiskCache();

通過這種加標記的方式,來判斷當前要快取的方式。當呼叫ImageLoader類時,需要指定哪一種快取。

 //判斷是否是SD快取
        Bitmap bitmap = isUsedDisCache?mDiskCache.get(url):mImageCache.get(url);

修改ImageLoader後

/**
 * 圖片載入類
 */

public class ImageLoader {
    //記憶體快取
    ImageCache mImageCache = new ImageCache() ;

    //SD卡快取
    DiskCache mDiskCache = new DiskCache();

    //執行緒池,執行緒數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    //是否使用SD卡快取
    boolean isUsedDisCache  = false;


    public void displayImage(final String url, final ImageView imageView){
        //判斷是否是SD快取
        Bitmap bitmap = isUsedDisCache?mDiskCache.get(url):mImageCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        imageView.setTag(url);

        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(url);
                if(bitmap==null){
                    return;
                }
                if(imageView.getTag().equals(url)){
                    imageView.setImageBitmap(bitmap);
                }

                mImageCache.put(url,bitmap);

            }
        });

    }

    public Bitmap downloadImage(String imageUrl){
        Bitmap bitmap = null;
        try{
            URL url = new URL(imageUrl);
            final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(connection.getInputStream());
            connection.disconnect();

        }catch(Exception e){
            e.printStackTrace();
        }
        return  bitmap;
    }


}

這種每次加入新的快取時都要修改原來的程式碼,這樣很可能會引入bug.而且會使原來的程式碼邏輯變得原來越複雜。
使用介面開閉原則修改後

/**
 * 抽象快取
 */

public interface ImageCache {
    Bitmap get(String url);

    void put(String url, Bitmap bmp);
}
/**
 *檔案快取
 */
public class DiskCache implements ImageCache {
    static String cacheDir = "sdcard/cache/";

    //從快取中獲取圖片
    public Bitmap get(String url){
        return BitmapFactory.decodeFile(cacheDir+url);
    }

    //將圖片快取到記憶體中
    public void put(String url,Bitmap bitmap){
        FileOutputStream fileOutputStream = null;
        try{
            fileOutputStream = new FileOutputStream(cacheDir+url);
            bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if(fileOutputStream!=null){
                try{
                    fileOutputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}
/**
 * 記憶體快取
 */

public class MemoryCache implements ImageCache {
    //圖片快取
    LruCache<String,Bitmap> mImageCache;

    public MemoryCache(){
        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) {
                return bitmap.getRowBytes()*bitmap.getHeight()/1024;
            }
        };
    }


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

    @Override
    public void put(String url, Bitmap bmp) {
        mImageCache.put(url,bmp);
    }
}
/**
 * 圖片載入類
 */

public class ImageLoader {
    //預設是記憶體快取
    ImageCache mImageCache = new MemoryCache() ;

    //執行緒池,執行緒數量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    //是否使用SD卡快取
    boolean isUsedDisCache  = false;

    public void setImageCache(ImageCache cache){
        mImageCache = cache;
    }


    public void displayImage(final String url, final ImageView imageView){
        //判斷是否是SD快取
        Bitmap bitmap = mImageCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }

        //圖片沒快取 提交到執行緒池中現在圖片
        submitLoadRequest(url,imageView);

    }

    private void submitLoadRequest(final String imageUrl,final ImageView imageView){
        imageView.setTag(imageUrl);

        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap = downloadImage(imageUrl);
                if(bitmap==null){
                    return;
                }
                if(imageView.getTag().equals(imageUrl)){
                    imageView.setImageBitmap(bitmap);
                }

                mImageCache.put(imageUrl,bitmap);

            }
        });
    }

    public Bitmap downloadImage(String imageUrl){
        Bitmap bitmap = null;
        try{
            URL url = new URL(imageUrl);
            final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            bitmap = BitmapFactory.decodeStream(connection.getInputStream());
            connection.disconnect();

        }catch(Exception e){
            e.printStackTrace();
        }
        return  bitmap;
    }


}

概括來說就是用一個抽象的ImageCache物件在setImageCache中賦予具體快取方式。然後直接用這種快取

3 里氏替換原則
定義
所有引用基類的地方必須能透明的使用其子類物件

示例

public abstract class View {
    public abstract  void draw();
    public void measure(int width,int height){

    }
}
public class Button extends View{
    @Override
    public void draw() {

    }
}
public class TextView extends View {
    @Override
    public void draw() {

    }
}

所有View的子類都可以呼叫draw()方法。

public class Window {
    public void show(View child){
        child.draw();
    }
}

優缺點
優點:

  • 程式碼重用,減少建立類的成本,每個子類都擁有父類的方法和屬性
  • 子類和父類基本相似,但又與父類有所區別。
  • 提高程式碼的可擴充套件性。

缺點:

  • 繼承是侵入性的,只要繼承就必須擁有父類的所有屬性和方法。
  • 可能造成子類程式碼冗餘,靈活性降低,因為子類必須擁有父類的屬性和方法。

4 依賴倒置原則
依賴倒置原則指代了一種特定的解耦形式。使得高層次的模組不依賴於低層次的模組的實現細節的目的。
主要思想:

  • 高層模組不應該依賴於底層模組,兩者都應該依賴於抽象。
  • 抽象不應該依賴細節。
  • 細節應該依賴抽象。

模組間的依賴通過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是通過介面或者抽象類產生的

概括來說就是面向介面程式設計
就像開閉原則中用到的修改之前 ImageLoader中的ImageCache 直接用這種方式賦值.

//預設是記憶體快取
    ImageCache mImageCache = new MemoryCache() ;
public void setImageCache(ImageCache cache){
        mImageCache = cache;
    }

mImageCache在初始化的時候賦予了預設值。而mImageCache實際被賦值是在setImageCache中完成。可以看出mImageCache是依賴ImageCache這個抽象。而非具體的Memorycache.

5 介面隔離原則
定義
客戶端不應該依賴它不需要的介面。
也就是類間的依賴關係應該建立在最小的介面上。

示例
社會上的職業有很多種,比如農民,老師,司機等。

/**
 *工作
 */

public interface Job {
    /**
     * 飼養動物
     */
    void feedAnimal();

    /**
     * 開車
     */
    void driveCar();

    /**
     * 教知識
     */
    void teach();

    /**
     * 保衛國家
     */
    void defendCountry();

}

將龐大,臃腫的介面拆分成更小的和更具體的介面。

/**
 *司機
 */

public interface Drivier {
    void driveCar();
}
/**
 * 老師
 */

public interface Teacher {
    void teach();
}

那麼一個常人不可能會做這麼多的工作。所以實現了Job介面。大部分的方法都是不能空方法。


/**
 * 普通人
 */

public class NomalPerson implements Job{

    @Override
    public void feedAnimal() {

    }

    @Override
    public void driveCar() {

    }

    @Override
    public void teach() {

    }

    @Override
    public void defendCountry() {

    }
}

只實現這個一個介面更簡潔。

/**
 * 普通人
 */

public class NomalPerson implements Teacher{

    @Override
    public void teach() {

    }

}

6 迪米特原則
迪米特原則也稱為最少知識原則,通俗的說就是一個物件應該對其他物件有最少的瞭解。雷與雷之間的關係越密切,耦合度越大,當一個類發生改變時,對另外一個類的影響也就越大。

示例
租房子的時候,會把你需要找的房源型別告知中介,中介把所有的房型給你。然後你把這些房型經過篩選。知道自己滿意的房子。

/**
 *房間
 */
public class Room {
    public float area;//面積
    public float price; //價格

    public Room(float area, float price) {
        this.area = area;
        this.area = price;
    }

    @Override
    public String toString() {
        return "Room{" +
                "area=" + area +
                ", price=" + price +
                '}';
    }
}
/**
 *中介
 */

public class Mediator {
    List<Room> rooms = new ArrayList<>();
    public Mediator(){
        for(int i=0;i<5;i++){
            //模擬多套可租的房子
            rooms.add(new Room(i+14,(14+i)*150));
        }
    }

    public List<Room> getAllRooms() {
        return rooms;
    }
};
/**
 *租戶
 */

public class Tenant {

    public static final float maxPrice = 2300;
    public static final float minArea = 14;

    public void rentRoom(Mediator mediator){
        List<Room> rooms = mediator.getAllRooms();
        for(Room room:rooms){
            if(isSuitable(room)){
                System.out.println("找到房間了"+room);
            }
        }
    }

    private boolean isSuitable(Room room) {
        //滿意的房子必須要大於最小面積並低於最低價格
        return room.area>minArea&&room.price<maxPrice;
    }
}

關係類圖
這裡寫圖片描述

從程式碼中可以看出,租戶不僅依賴中介,還要與Room打交道。這樣導致Tenant 和Room的耦合較高。
進行修改後:

/**
 *中介
 */

public class Mediator {
    List<Room> rooms = new ArrayList<>();

    public static final float maxPrice = 2300;
    public static final float minArea = 14;

    public Mediator(){
        for(int i=0;i<5;i++){
            //模擬多套可租的房子
            rooms.add(new Room(i+14,(14+i)*150));
        }
    }

    //把房源進行刪選
    public void  filter(){
        for(Room room:rooms){
            if(isSuitable(room)){
               notifyTenant(room);
            }
        }
    }

    //通知租戶 
    public void notifyTenant(Room room){
        Tenant tenant = new Tenant();
        tenant.getMessage(room);
    }

    private boolean isSuitable(Room room) {
        //滿意的房子必須要大於最小面積並低於最低價格
        return room.area>minArea&&room.price<maxPrice;
    }
};

當中介找到合適的房源中介通知租戶。Tenant不在和Room相關聯。

/**
 *租戶
 */

public class Tenant {

    public void getMessage(Room room){
        System.out.println("找到房間了"+room);
    }

}

修改後的關係圖
這裡寫圖片描述

相關推薦

設計模式-00 6設計原則

1 單一職責原則 單一職責的定義是:就一個類而言,應阿蓋僅有一個引起他變化的原因。簡單來說,一個類中應該是一組相關性和高的函式,資料的封裝。但是由於每個人的理解不一樣。那麼劃分類的界限也不一樣。 下面是一個示例: /** * 圖片載入類 */ p

設計模式6設計原則

單一職責原則 單一職責原則(Single Responsibility Principle, SRP)的定義是: 應該有且僅有一個原因引起類或介面的變更。即一個類或介面只負責一個功能領域中的相應職責。 單一職責原則提出了一個編寫程式的標準, 它使類的複雜性降低、提高了程式碼的可讀性、可維護性和可擴充套件性

設計模式6基本原則

一.單一職責原則  單一職責原則(Single Responsibility Principle, SRP):一個類只負責一個功能領域中的相應職責,或者可以定義為:就一個類而言,應該只有一個引起它變化的原因。單一職責原則是最簡單的面向物件設計原則,它用於控制類的粒度大小。

java設計模式6設計原則day01

一、 單一職責原則 1.1 單一職責原則的英文名稱是Single Responsibility Principle,簡稱是SRP 1.2 There should never be more than one reason for a class t

24種設計模式6原則

外部與一個子系統的通訊必須通過一個統一的門面(Facade)物件進行,這就是門面模式。 醫院的例子 用一個例子進行說明,如果把醫院作為一個子系統,按照部門職能,這個系統可以劃分為掛號、門診、劃價、化驗、收費、取藥等。看病的病人要與這些部門打交道,就如同一個子系統的客戶端與一個子系統的各個類打交道一

設計模式:6設計原則(PHP版本)

英文名稱 屬性信息 map 圖片 AC src pri 負責 模式 1.單一職責 單一職責原則的英文名稱是Single Responsibility Principle,簡稱是SRP,單一職責原則的定義是:應該有且僅有一個原因引起類的變更。    樣例1:

6設計原則和23種設計模式

前言:   最近看了一下《Android原始碼設計模式解析與實戰》這本書,對設計原則和設計模式有了一定的瞭解,故在此記錄一下相關的簡單定義。 6大設計原則        (S-O-L-I-D)–>穩固 單一職責原則(SRP ):對於一個類而言,應該有且僅有

設計模式學習筆記(1)-----設計模式6原則

單一職責原則 Single Responsibility Principle(SRP) 每一個介面就承擔一個責任(或者說是一類的責任),儘量做到只有一個原因引起變化 ps:電話機通話的過程可以分為 ,撥打電話->通話->結束通話電話 這裡撥打 和 結束通話 都是物理層面

php面向物件程式設計5原則+6設計模式

一、面向物件程式設計的6大設計原則 單一職責原則——類要職責單一,一個類只需要做好一件事情。 里氏替換原則——子類可以擴充套件父類的功能,但不能改變父類原有的功能(可以實現父類的抽象方法和增加自己特有的方法,不要覆蓋父類的非抽象方法)。 依賴倒置原則——-面向介面程式設計:只

設計模式6設計原則

1. 單一職責原則 定義 單一職責原則(SRP:Single responsibility principle)又稱單一功能原則。它規定一個類應該只有一個發生變化的原因。 闡述 單一職責適用於介面、類、方法。顧名思義,就是要求一個介面或類

設計模式6基本原則之(一)

back center 可維護性 擴展 tex 通過 人員 程序 csdn 版權聲明:本文為博主原創文章,轉載請註明出處

Java中的24種設計模式與7原則

工廠模式 職責 需要 占位符 ati gre template 層次 cto 一、創建型模式 1、抽象工廠模式(Abstract factory pattern): 提供一個接口, 用於創建相關或依賴對象的家族, 而不需要指定具體類.2、生成器模式(Builder pat

6設計原則

宋體 post 法則 inversion 子類 設計 gpo ati clas 1、開閉原則(Open Close Principle) 對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展

6設計原則總結

單一職責原則 單一職責原則的目標是類,通過設計介面,使得類從功能上更加純粹,只有一種職責。 這樣設計介面後,整體會更加清晰明瞭,條理分明。不會有混亂的感覺。 里氏替換原則 總結起來很簡單,父

6設計原則之單一職責原則

方法 接口設計 sta 其他 一個 src 沒有 不同的 可維護性 單一職責原則 如果有一個用戶管理類,類圖如下 我想,任誰也能看的出這個接口設計的有問題,用戶的屬性和用戶的行為沒有分開,應該把用戶的信息抽取成一個業務對象,把用戶的行為抽取成一個業務對象,按照這個思

6設計原則之介面隔離原則

介面隔離原則的定義 什麼是介面. 例項介面,比如定義了一個Person類,然後 Person p = new Pserson(); 產生一個例項,Person類就是 p 的介面 類介面,就是Java中使用 interface 定義的介面 什麼是隔離 隔離要求將介面儘量細化,同時介面中的方

6設計原則之接口隔離原則

設計時 接口 老師 規範 模塊 一個個 on() 獨立 alt 接口隔離原則的定義 什麽是接口. 實例接口,比如定義了一個Person類,然後 Person p = new Pserson(); 產生一個實例,Person類就是 p 的接口 類接口,就是Java中使用 i

6設計原則之迪米特法則

迪米特法則的定義 迪米特法則也稱為最少知識原則,一個物件應該對其他物件有最少的瞭解. 通俗的講,一個類應該對自己需要耦合或呼叫的類知道的最少,被呼叫類的內部是如何複雜都和我沒關係,我就知道你的這些public方法,我就呼叫這麼多,其他的我一概不關心. 迪米特法則對類的低耦合提出了要求 1.只和朋友交流

6設計原則之開閉原則

抽象層 方法 工具 通過 mage jpg bsp 出現 努力 開閉原則的定義 開閉原則的定義: 一個軟件實體,如類、模塊和函數應該對擴展開放,對修改關閉.即一個軟件實體應該通過擴展來實現變化,而不是通過修改已有的代碼來實現變化. 軟件實體包括一下部分 項目或軟件產品中按

設計模式】Java中的23種設計模式與7原則

Java中的23種設計模式與7大原則建立型模式 5抽象工廠模式(Abstract factory pattern): 提供一個介面, 用於建立相關或依賴物件的家族, 而不需要指定具體類.生成器模式(Bu