Java常見設計模式學習(非原創)

文章大綱

一、策略模式
二、觀察者模式
三、工廠模式
四、單例模式
五、其他模式
六、設計模式總結
七、參考文章

 

一、策略模式

現在假設我們有個“鴨子項目”,首先我們用OOP(面向對象)的角度設計這個項目,找到鴨子中共同的特性抽取在父類中並具體實現,不同的特性不實現,由子類具體實現,好下面看代碼:

public abstract class Duck {
    /**
     * 叫聲和游泳爲相同的特性抽取並具體實現
     */
    public void Quack() {
        System.out.println("~~gaga~~");
    }
    public void swim() {
        System.out.println("~~im swim~~");
    }

    /**
     * 外貌爲不同的特性設計爲抽象的方法,有子類具體實現
     */
    public abstract void display();

}

現在我們看它的子類:

public class GreenHeadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("**GreenHead**");
    }
}
public class RedHeadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("**RedHead**");
    }
}

好的,現在我們可以看到使用OOP可以很好的解決目前的問題,但是我們往往是需求不斷,所以我們現在又來一個新需求:添加會飛的鴨子
好辦啊,我們只要在父類中添加一個新的方法:

public abstract class Duck {
    /**
     * 叫聲和游泳爲相同的特性抽取並具體實現
     */
    public void Quack() {
        System.out.println("~~gaga~~");
    }
    public void swim() {
        System.out.println("~~im swim~~");
    }

    /**針對新需求的方法*/
    public void Fly() {
        System.out.println("~~im fly~~");
    }

    /**
     * 外貌爲不同的特性設計爲抽象的方法,有子類具體實現
     */
    public abstract void display();

}

問題來了,這個Fly讓所有子類都會飛了,這是不科學的。
繼承的問題:對類的局部改動,尤其超類的局部改動,會影響其他部分。影響會有溢出效應
好現在我們繼續用OOP的方式去解決,使其子類覆蓋Fly:

public class GreenHeadDuck extends Duck {
    @Override
    public void display() {
        System.out.println("**GreenHead**");
    }

    /**
     * 覆蓋
     * */
    public void Fly() {
        System.out.println("~~no fly~~");
    }
}

可以看到貌似問題是解決了,我們現在的鴨子類只有2種,如果有上百種呢,那不是都要去覆蓋。所以這不是一種好的設計模式

分析問題:
需要新的設計方式,應對項目的擴展性,降低複雜度:
1)分析項目變化與不變部分,提取變化部分,抽象成接口+實現;
2)鴨子哪些功能是會根據新需求變化的?叫聲、飛行…
我們將變化的功能設計成接口,下面看代碼:

public interface FlyBehavior {
    void fly();
}
public interface QuackBehavior {
    void quack();
}

來看看新的Duck類:

public abstract class Duck {
    /**
     * 父類定義行爲出來,但是沒有具體實例化
     */
    FlyBehavior mFlyBehavior;
    QuackBehavior mQuackBehavior;

    public Duck() {
    }

    public void Fly() {
        if (mFlyBehavior!=null) {
            mFlyBehavior.fly();
        }
    }

    public void Quack() {
        if (mQuackBehavior!=null) {
            mQuackBehavior.quack();
        }
    }

    /**
     * 子類可以透過兩個行爲的set方法去動態改變自己的具體行爲
     */
    public void setmFlyBehavior(FlyBehavior mFlyBehavior) {
        this.mFlyBehavior = mFlyBehavior;
    }

    public void setmQuackBehavior(QuackBehavior mQuackBehavior) {
        this.mQuackBehavior = mQuackBehavior;
    }

    public abstract void display();
}

然後在去看看子類:

public class RedHeadDuck extends Duck{

    public RedHeadDuck() {
        mFlyBehavior=new GoodFlyBehavior();
        mQuackBehavior=new GaGaQuackBehavior();
    }
    @Override
    public void display() {
        System.out.println("redDuck");
    }
}
public class GreenHeadDuck extends Duck{

    public GreenHeadDuck() {
        mFlyBehavior=new BadFlyBehavior();
        mQuackBehavior=new GeGeQuackBehavior();
    }
    @Override
    public void display() {
        System.out.println("greenDuck");
    }

}

再來看看接口實現類:

public class BadFlyBehavior implements FlyBehavior{

    @Override
    public void fly() {
        System.out.println("bad fly");
    }

}
public class GoodFlyBehavior implements FlyBehavior{

    @Override
    public void fly() {
        System.out.println("good fly");
    }

}
public class NoFlyBehavior implements FlyBehavior{

    @Override
    public void fly() {
        System.out.println("No fly");
    }

}
public class GaGaQuackBehavior implements QuackBehavior{

    @Override
    public void quack() {
        System.out.println("gaga quack");
    }

}
public class GeGeQuackBehavior implements QuackBehavior{

    @Override
    public void quack() {
        System.out.println("gege quack");
    }

}
public class NoQuackBehavior implements QuackBehavior{

    @Override
    public void quack() {
        System.out.println("No Quack");
    }

}

好的,現在我們來分析一下這個設計,在父類中我們定義好FlyBehavior & QuackBehavior 兩個行爲接口,然後在子類構造方法中分別設定對應的具體行爲
現在來測試一下:

 
 

策略模式:分別封裝行爲接口,實現算法族,超類裏放行爲接口對象,在子類裏具體設定行爲對象。原則就是:分離變化部分,封裝接口,基於接口編程各種功能。此模式讓行爲算法的變化獨立於算法的使用者

二、觀察者模式

現在假設我們有一個需要爲A公司實時提供天氣的天氣預報接口項目,好的,首先我們還是以OOP的方式去解決問題,首先我們創建一個天氣臺對象並提供相關方法假設它可以實時爲A公司提供天氣數據,下面看代碼:

public class MeteorologicalStation {
    private float pressure;
    private float temperature;
    private float humidity;
    private ACompany company;

    public MeteorologicalStation(ACompany company) {
        this.company=company;
    }

    public float getPressure() {
        return pressure;
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    /**
     * 實時提供天氣情況
     * */
    public void uploadData(float pressure,float temperature,float humidity){
        company.getMeteorologicalStationData(pressure, temperature, humidity);
    }

}

ACompany爲A公司:

public class ACompany {
    public void getMeteorologicalStationData(float pressure, float temperature, float humidity) {
        System.out.println("pressure: "+pressure+",temperature: "+temperature+",humidity: "+humidity);
    }
}

測試

 
 

好的,到了這裏我們從OOP角度已經初步解決了這個項目,假設現在B公司也需要實時獲取天氣臺提供的天氣情況,如果我們還是繼續使用OOP去設計這個項目,需要在天氣臺接口中添加BCompany,並在實時更新的函數中調用BCompany的獲取天氣的函數,這貌似可以解決這個需求,但是加入後續加入C,D,E..公司,那麼天氣臺接口修改的代碼也是比較大,但是我們知道在通常實際開發中,接口一般不會隨着他人的接入而更改,所以現在我們使用觀察者模式去設計這個項目:

/**
 * 該接口相當於天氣臺管理者,誰想接入我和離開我都必須通過它去管理
 * */
public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifObserver();
}
/**
 * 通過該接口,每個想要接入的公司實現該方法即可。
 */
public interface Observer {
    void getMeteorologicalStationData(float pressure,float temperature,float humidity);
}
public class MeteorologicalStation implements Subject {
    private float pressure;
    private float temperature;
    private float humidity;
    private ArrayList<Observer> observers;

    public MeteorologicalStation(ACompany company) {
        observers = new ArrayList<Observer>();
    }

    public float getPressure() {
        return pressure;
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public void uploadData(float pressure, float temperature, float humidity) {
        this.pressure = pressure;
        this.temperature = temperature;
        this.humidity = humidity;
         notifObserver();
    }

    @Override
    public void registerObserver(Observer o) {
        if (!observers.contains(o)) {
            observers.add(o);
        }
    }

    @Override
    public void removeObserver(Observer o) {
        if (observers.contains(o)) {
            observers.remove(o);
        }
    }

    @Override
    public void notifObserver() {
        for (int i = 0; i < observers.size(); i++) {
            Observer observer = observers.get(i);
            observer.getMeteorologicalStationData(getPressure(), getTemperature(), getHumidity());
        }
    }

}

現在我們來分析天氣臺類,天氣臺實現了Subject,並用集合的形式方便的管理多個客戶的接入與離開,在notifObserver中實時的上傳天氣數據。好的,現在我們看看其他客戶的接入:

public class ACompany implements Observer{
    @Override
    public void getMeteorologicalStationData(float pressure,float temperature,float humidity){
        System.out.println("A pressure: "+pressure+",temperature: "+temperature+",humidity: "+humidity);
    }
}
public class BCompany implements Observer{

    @Override
    public void getMeteorologicalStationData(float pressure, float temperature, float humidity) {
        System.out.println("B pressure: "+pressure+",temperature: "+temperature+",humidity: "+humidity);
    }

}

好的我們可以看到無論有多少個公司想要接入,現在只需要實現Observer接口即可。現在我們來測試下:

 
 

看到這裏我們已經知道了觀察者模式的好處,下面我們看看java內置的觀察者:

public class MeteorologicalStation extends Observable {
    private float pressure;
    private float temperature;
    private float humidity;

    public float getPressure() {
        return pressure;
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public void uploadData(float pressure, float temperature, float humidity) {
        this.pressure = pressure;
        this.temperature = temperature;
        this.humidity = humidity;
        WeatherData data=new WeatherData(pressure, temperature, humidity);
        /**
         * setChanged
         * 根據有些特定的需求出現的,設置後喚醒纔有效,
         * 例如我們不需要溫度改變0.5我們也去喚醒客戶,
         * 因此這裏我們可以判斷後在設置
         * */
        this.setChanged();
        this.notifyObservers(data);
    }

     public class WeatherData{
         public float pressure;
         public float temperature;
         public float humidity;
         public WeatherData(float pressure, float temperature, float humidity) {
            this.pressure = pressure;
            this.temperature = temperature;
            this.humidity = humidity;
        }

    }
}

從代碼中可以看到Observable 是一個類 而不是像我們一樣的Subject的接口

this.setChanged();
this.notifyObservers(data);

兩個一起使用纔有效,下面我們看看A,B公司需要怎麼做:

public class ACompany implements Observer{

    @Override
    public void update(Observable arg0, Object arg1) {
        WeatherData data=(WeatherData) arg1;
        System.out.println("A pressure: "+data.pressure+",temperature: "+data.temperature+",humidity: "+data.humidity);
    }
}
public class BCompany implements Observer{

    @Override
    public void update(Observable o, Object arg) {
        WeatherData data=(WeatherData) arg;
        System.out.println("B pressure: "+data.pressure+",temperature: "+data.temperature+",humidity: "+data.humidity);
    }
}

A,B公司只要實現系統的Observer接口,和我們剛纔的設計是一樣的。最後我們在測試一下:

 
 

可以看到和我們之前的效果完全一樣

三、工廠模式

項目需求:生產披薩項目,生產披薩需要經過準備,烘烤,剪切,打包 4個步驟,根據客戶輸入的披薩名字生產對應的披薩。
首先用傳統的方式去做:

public abstract class Pizza {
    protected String name;

    public abstract void prepare();
    public void bake()
    {
        System.out.println(name+" baking;");
    }
    public void cut()
    {
        System.out.println(name+" cutting;");
    }
    public void box()
    {
        System.out.println(name+" boxing;");
    }
    public void setname(String name)
    {
        this.name=name;
    }
}
public class PepperPizza extends Pizza{

    @Override
    public void prepare() {
        super.setname("PepperPizza");
        System.out.println("PepperPizza");
    }

}
public class GreekPizza extends Pizza{

    @Override
    public void prepare() {
        super.setname("GreekPizza");
        System.out.println("GreekPizza");
    }

}
public class CheesePizza extends Pizza{

    @Override
    public void prepare() {
        super.setname("CheesePizza");
        System.out.println("CheesePizza");
    }

}
public class OrderPizza {

    OrderPizza(){
        String type=null;
        Pizza pizza=null;
        do {
            type=getType();
            if (type.equals("greek")) {
                pizza=new GreekPizza();
            }else if (type.equals("cheese")) {
                pizza=new CheesePizza();
            }else if (type.equals("pepper")) {
                pizza=new PepperPizza();
            }else{
                break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    private String getType(){
        String type=null;
        BufferedReader br=null;
        try {
            br=new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            type= br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
        return type;
    }
}

代碼分析:首先我們考慮到生產披薩的4步驟是一樣的,只有名字不一樣,所以我們將名字方法抽象出來,友各個披薩類去實現,然後通過OrderPizza 類去模仿工廠生產的過程。
最後我們來測試一下:

public class test {

    public static void main(String[] args) {
        OrderPizza order1=new OrderPizza();
    }

}
 

然後現在又研究出了幾個新的披薩品種,需要加入到系統中,那麼如果繼續在傳統的方式中修改,我們只需要增加新品種披薩類,然後修改 OrderPizza 中的判斷語句。

我們現在用簡單工廠模式下試一下:
簡單工廠類中各種披薩類還是一樣,修改的是OrderPizza:

public class OrderPizza {

    OrderPizza(SimpleFactoryPizza factory){
        String type=null;
        Pizza pizza=null;
        do {
            type=getType();
            pizza=factory.createPizza(type);
            if (pizza!=null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else{
                break;
            }
        } while (true);
    }

    private String getType(){
        String type=null;
        BufferedReader br=null;
        try {
            br=new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            type= br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
        return type;
    }
}
public class SimpleFactoryPizza {
    public Pizza createPizza(String type){
        Pizza pizza=null;
        if (type.equals("greek")) {
            pizza=new GreekPizza();
        }else if (type.equals("cheese")) {
            pizza=new CheesePizza();
        }else if (type.equals("pepper")) {
            pizza=new PepperPizza();
        }else if (type.equals("beef")) {
            pizza=new BeefPizza();
        }
        return pizza;
    }
}

雖然看似代碼是一樣的,但是我們已經將變化的代碼抽取出來了,在OrderPizza中我們無需再次修改,此時我們已經將變化的和不變化的隔離開來了
然後現在我們需要在紐約,倫敦生產披薩,且各地生成的披薩都各有差異,那麼我們現在使用工廠方法模式試試:

public class LDCheesePizza extends Pizza{

    @Override
    public void prepare() {
        super.setname("LDCheesePizza");
        System.out.println("LDCheesePizza");
    }

}
public class LDGreekPizza extends Pizza{

    @Override
    public void prepare() {
        super.setname("LDGreekPizza");
        System.out.println("LDGreekPizza");
    }

}
public class LDPepperPizza extends Pizza{

    @Override
    public void prepare() {
        super.setname("LDPepperPizza");
        System.out.println("LDPepperPizza");
    }

}

紐約的也一樣,我們再來看看OrderPizza

public abstract class OrderPizza {

    public abstract Pizza createPizza(String type);
    public OrderPizza(){
        String type=null;
        Pizza pizza=null;
        do {
            type=getType();
            pizza=createPizza(type);
            if (pizza!=null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else{
                break;
            }
        } while (true);
    }

    private String getType(){
        String type=null;
        BufferedReader br=null;
        try {
            br=new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            type= br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
        return type;
    }
}

這裏因爲工廠是多樣化的,所以這裏我們將工廠抽象,然後看具體實現:

public class NewYorkOrderPizza extends OrderPizza {

    @Override
    public Pizza createPizza(String type) {
        Pizza pizza=null;
        if (type.equals("greek")) {
            pizza=new LDGreekPizza();
        }else if (type.equals("cheese")) {
            pizza=new LDCheesePizza();
        }else if (type.equals("pepper")) {
            pizza=new LDPepperPizza();
        }
        return pizza;
    }

}
public class LondonOrderPizza extends OrderPizza {

    @Override
    public Pizza createPizza(String type) {
        Pizza pizza=null;
        if (type.equals("greek")) {
            pizza=new NYGreekPizza();
        }else if (type.equals("cheese")) {
            pizza=new NYCheesePizza();
        }else if (type.equals("pepper")) {
            pizza=new NYPepperPizza();
        }
        return pizza;
    }

}

最後我們來測試一下

public class test {

    public static void main(String[] args) {
        OrderPizza order1=new NewYorkOrderPizza();
    }

}
 

最後我們再試試使用抽象工廠模式:

public interface FactoryPizza {
    Pizza createPizza(String type);
}

單獨將變化的工廠抽象

public class OrderPizza {

    OrderPizza(FactoryPizza factory){
        String type=null;
        Pizza pizza=null;
        do {
            type=getType();
            pizza=factory.createPizza(type);
            if (pizza!=null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else{
                break;
            }
        } while (true);
    }

    private String getType(){
        String type=null;
        BufferedReader br=null;
        try {
            br=new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            type= br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
        return type;
    }
}
public class LondonFactoryPizza implements FactoryPizza{

    @Override
    public Pizza createPizza(String type) {
        Pizza pizza=null;
        if (type.equals("greek")) {
            pizza=new LDGreekPizza();
        }else if (type.equals("cheese")) {
            pizza=new LDCheesePizza();
        }else if (type.equals("pepper")) {
            pizza=new LDPepperPizza();
        }
        return pizza;
    }

}
public class NewYorkOrderPizza implements FactoryPizza{

    @Override
    public Pizza createPizza(String type) {
        Pizza pizza=null;
        if (type.equals("greek")) {
            pizza=new NYGreekPizza();
        }else if (type.equals("cheese")) {
            pizza=new NYCheesePizza();
        }else if (type.equals("pepper")) {
            pizza=new NYPepperPizza();
        }
        return pizza;

    }

}

效果和工廠方法模式是一樣的,雖然這裏可能看不出抽象工廠模式與工廠方法模式的差別,但是我們在大的項目就可以看出來了

四、單例模式

巧克力工廠項目,工廠只有一個鍋爐,往鍋爐中添加佐料,如果滿了則其他人不能再次添加,或者必須傾倒後添加,然後將鍋爐燒開,如果鍋爐已經燒開就無需再次燒開,或者必須傾倒後再次燒開
好的,首先我們還是用普通的方式去做:

public class ChocolateFactor {
    private boolean isEmpty;
    private boolean isBoiled;
    public ChocolateFactor() {
        isEmpty=true;
        isBoiled=false;
    }
    public void fill(){
        if (isEmpty) {
            isEmpty=false;
            isBoiled=false;
            System.out.println("鍋爐滿了");
        }
    }

    public void boil(){
        if ((!isEmpty)&&(!isBoiled)) {
            isBoiled=true;
            System.out.println("鍋爐已經沸騰了");
        }
    }

    public void dump(){
        if ((!isEmpty)&&(isBoiled)) {
            isEmpty=true;
            isBoiled=false;
            System.out.println("鍋爐啥都沒有咯");
        }
    }
}

然後我們測試下:

public class test {
    public static void main(String[] args) {
        ChocolateFactor factor=new ChocolateFactor();
        factor.fill();
        factor.boil();
        ChocolateFactor factor1=new ChocolateFactor();
        factor1.fill();
        factor1.boil();
    }
}
 

可以看到即使鍋爐滿了和沸騰了,其他人仍然可以往鍋爐中添加,所以這是不合常理的,現在我們來用單例試試:

public class ChocolateFactor {
    private boolean isEmpty;
    private boolean isBoiled;
    private static ChocolateFactor instance=null;
    private ChocolateFactor() {
        isEmpty=true;
        isBoiled=false;
    }
    public static ChocolateFactor getInstance(){
        if (instance==null) {
            instance=new ChocolateFactor();
        }
        return instance;
    }
    public void fill(){
        if (isEmpty) {
            isEmpty=false;
            isBoiled=false;
            System.out.println("鍋爐滿了");
        }
    }

    public void boil(){
        if ((!isEmpty)&&(!isBoiled)) {
            isBoiled=true;
            System.out.println("鍋爐已經沸騰了");
        }
    }

    public void dump(){
        if ((!isEmpty)&&(isBoiled)) {
            isEmpty=true;
            isBoiled=false;
            System.out.println("鍋爐啥都沒有咯");
        }
    }
}

代碼中可以看到我們將構造方法私有化,提供一個靜態的方法去返回巧克力工廠實例,將巧克力工廠靜態化,使其在內存中只保留一份。現在我們來測試下:

public class test {
    public static void main(String[] args) {
        ChocolateFactor factor=ChocolateFactor.getInstance();
        factor.fill();
        factor.boil();
        ChocolateFactor factor1=ChocolateFactor.getInstance();
        factor1.fill();
        factor1.boil();
    }
}
 

可以看到鍋爐滿了和沸騰之後對其再次操作是無效的。

但是其實這也是存在問題的,現在假設我們在多線程的狀態,線程一走到了if (instance==null) { 後cpu不讓它工作了,開始線程二工作,線程二將整個getInstance走完了,現在cpu又讓線程1執行,這時候,線程1又會去new 一個新的巧克力工廠,這樣又產生了和第一個方式同樣的問題,
在線程中我們學過同步鎖,我們可以簡單的在:

public static synchronized ChocolateFactor getInstance(){
        if (instance==null) {
            instance=new ChocolateFactor();
        }
        return instance;
    }

但是我們知道加入synchronized 是非常耗內存的,所以這也不是我們的最終解決辦法,然後我們再次看到另一個解決辦法:

public class ChocolateFactor {
    private boolean isEmpty;
    private boolean isBoiled;
//  private static ChocolateFactor instance=null;
    private static ChocolateFactor instance=new ChocolateFactor();

    private ChocolateFactor() {
        isEmpty=true;
        isBoiled=false;
    }
//  public static synchronized ChocolateFactor getInstance(){
//      if (instance==null) {
//          instance=new ChocolateFactor();
//      }
//      return instance;
//  }
    public static synchronized ChocolateFactor getInstance(){
        return instance;
    }
    public void fill(){
        if (isEmpty) {
            isEmpty=false;
            isBoiled=false;
            System.out.println("鍋爐滿了");
        }
    }

    public void boil(){
        if ((!isEmpty)&&(!isBoiled)) {
            isBoiled=true;
            System.out.println("鍋爐已經沸騰了");
        }
    }

    public void dump(){
        if ((!isEmpty)&&(isBoiled)) {
            isEmpty=true;
            isBoiled=false;
            System.out.println("鍋爐啥都沒有咯");
        }
    }
}

可以看到和之前的代碼不同的是我們直接在定義變量的時候就直接將工廠new出來,然後在getInstance中直接返回,這種方式成爲懶漢式,但是問題又來了,假如我們不需要使用getInstance,然後在使用這個的類的時候我們就已經在變量中直接將工廠new了出來,所以這樣也是浪費了內存,那麼我們的最終解決辦法是:

public static synchronized ChocolateFactor getInstance(){
        if (instance==null) {
            synchronized (ChocolateFactor.class) {
                if (instance==null) {
                    instance=new ChocolateFactor();
                }
            }
        }
        return instance;
    }

使用雙重判斷加鎖的方式,現在我們還是在多線程的狀態,加入線程1還是在if (instance==null) { cpu不分配給它空間了,線程2開始工作,線程2一鼓作氣將getInstance走完,現在線程1工作了,線程走到
if (instance==null) 方法instance已經不爲null了,所以他將不會再次new 一個,所以這纔是我們的終極方案。

五、其他模式

參考文章:
https://blog.csdn.net/qq_33750826/article/details/73558492

1. 裝飾者模式

裝飾着模式是一種開放-關閉原則的設計意義,開放即爲以後設計添加子類方便,關閉即父類不用更改,具體查看參考文章的第四點

2. 命令模式

具體查看參考文章的第七點

3. 適配器模式

適配器通俗易懂相當於就是一個轉換器,平時手機充電,假如這個頭不能插入手機,我們只需要通過一個轉換器,然後使用轉換器上的插頭即可。具體查看參考文章的第八點

4. 外觀模式

具體查看參考文章的第九點

5. 模板模式

具體查看參考文章的第十點

6. 迭代器模式

具體查看參考文章的第十一點

7. 組合模式

具體查看參考文章的第十二點

8. 狀態模式

具體查看參考文章的第十三點

9. 代理模式

具體查看參考文章的第十四點

10. 複合模式

具體查看參考文章的第十五點

11. 橋接模式

具體查看參考文章的第十六點

12. 生成器模式

具體查看參考文章的第十七點

13. 責任鏈模式

具體查看參考文章的第十八點

14. 解釋器模式

具體查看參考文章的第二十點

15. 中介者模式

具體查看參考文章的第二十一點

16. 備忘錄模式

具體查看參考文章的第二十二點

17. 原型模式

具體查看參考文章的第二十三點

18. 訪問者模式

具體查看參考文章的第二十四點

六、設計模式總結

1. 什麼是設計模式

模式:在某些場景下,針對某類問題的某種通用解決方案
場景:項目環境
問題:約束條件,項目目標等
解決方案:通用、可以複用的設計,解決約束,達到目標

2. 設計模式的三個分類

2.1 創建型模式
對象實例化的模式,創建型模式解耦了對象的實例化過程:
簡單工廠:一個工廠類根據傳入的參量決定創建出哪一種產品類的實例
工廠方法:定義一個創建對象的接口,讓子類決定實例化哪一個類
抽象工廠:創建相關或依賴對象的家族,而無需明確指定具體類
單例模式:某個類只能有一個實例,提供一個全局訪問點
生成器模式:封裝一個複雜對象的構建過程,並可以按步驟構造
原型模式:通過複製現有的實例來創建新的實例

2.2 結構型模式
把類或對象結合在一起形成更大的結構:
適配器模式:將一個類的方法接口轉換成客戶希望的另外一個接口
組合模式:將對象組合成樹形結構以表示“部分-整體”的層次結構
裝飾模式:動態地給對象添加新的功能
代理模式:爲其他對象提供一個代理以控制對這個對象的訪問
蠅量模式:通過共享技術有效地支持大量細粒度的對象
外觀模式:提供統一的方法來訪問子系統的一羣接口
橋接模式:將抽象部分與它的實現部分分離,使它們都可以獨立地變化

2.3 行爲型模式
類和對象如何交互,及劃分責任和算法:
模板模式:定義一個算法結構,而將一些步驟延遲到子類中實現
解釋器模式:給定一個語言, 定義它的文法的一種表示,並定義一個解釋器
策略模式:定義一系列的算法,把它們封裝起來, 並且使它們可相互替換
狀態模式:允許一個對象在其內部狀態改變時改變它的行爲
觀測者模式:對象間的一對多的依賴關係
備忘錄模式:在不破壞封裝性的前提下,保存對象的內部狀態
中介者模式:用一箇中介對象來封裝一系列的對象交互
命令模式:將命令請求封裝爲一個對象,使得可用不同的請求來進行參數化
訪問者模式:在不改變數據結構的前提下,增加作用於一組對象元素新的功能
責任鏈:請求發送者和接收者之間解耦,使的多個對象都有機會處理這個請求
迭代器:一種遍歷訪問聚合對象中各個元素的方法,不暴露該對象的內部結構

3. 設計模式的六大原則

3.1 組合複用原則
多用組合,少用繼承
找到變化部分,抽象,封裝變化
區分“Has-A”與“Is-A”如果HasA表示你有它應該使用組合,IsA表示你是它應該使用繼承。

3.2 依賴倒置原則
依賴:成員變量、方法參數、返回值
要依賴於抽象,不要依賴於具體
高層模塊不應該依賴低層模塊,二者都應該依賴其抽象
抽象不應該依賴具體,具體應該依賴抽象
針對接口編程,不要針對實現編程
以抽象爲基礎搭建的結構比具體類搭建的結構要穩定的多
在java中,抽象指的是接口或者抽象類,具體就是具體的實現類

3.3 開閉原則
對擴展開放,對修改關閉
通過擴展已有軟件系統,可以提供新的功能
修改的關閉,保證穩定性和延續性

3.4 迪米特法則
一個對象應該與其他對象保持最少的瞭解。只與直接朋友交談。
成員變量、方法參數、方法返回值中需要的類爲直接朋友
類與類之間的關係越密切瞭解越多,耦合度越大
儘量降低類與類之間的耦合
外觀模式、中介者模式
接口隔離原則:一個類對另一個類的依賴應該建立在最小的接口上

3.5 里氏替換原則
所有引用基類的地方必須能透明地使用其子類對象
子類在擴展父類功能時不能破壞父類原有的功能
使用繼承時,遵循里氏替換原則:
子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法。
當子類重載父類方法時,方法的形參要比父類方法的參數更寬鬆
當子類實現父類的抽象方法時,方法的返回值要比父類更嚴格
里氏替換原則是設計整個繼承體系的原則

3.6 單一職責原則
類應該只有一個導致類變更的理由
即一個類只負責一項職責
降低類的複雜度
提高系統的可維護性
修改時降低風險溢出

4. 模式使用思考

4.1 保持簡單
儘可能用最簡單的方式解決問題
簡單而彈性的設計,一般使用模式是最好的方法

4.2 設計模式非萬能
模式是通用問題的經驗總結
使用模式時要考慮它對其他部分的影響
不需要預留任何彈性的時候,刪除掉模式
平衡與妥協

4.3 何時需要模式
找出設計中會變化的部分,通常就是需要考慮模式的地方
重構時

4.4 重構的時間就是模式的時間
重構就是改變代碼來改進組織方式的過程
利用模式來重構

七、參考文章

https://blog.csdn.net/qq_33750826/article/details/73558492

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