常見設計模式理解

 

    設計模式(Design pattern)代表了最佳的實踐,通常被有經驗的面向對象的軟件開發人員所採用。設計模式是軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。這些解決方案是衆多軟件開發人員經過相當長的一段時間的試驗和錯誤總結出來的。

    總而言之,設計模式就是各個大佬在開發中總結出來的能很好解決問題的方式。

    總體來說設計模式分爲三大類:

1.創建型模式:包括工廠模式、抽象工廠模式、單例模式、建造者模式、原型模式

2.結構型模式:包括適配器模式、橋接模式、過濾器模式、組合模式、裝飾器模式、外觀模式、享元模式、代理模式

3.行爲型模式:包括責任鏈模式(、命令模式、解釋器模式、迭代器模式、中介者模式、備忘錄模式、觀察者模式、狀態模式、空對象模式、策略模式、模板模式、訪問者模式

4:J2EE 模式:包括MVC 模式、業務代表模式、組合實體模式、數據訪問對象模式、前端控制器模式、攔截過濾器模式、服務定位器模式、傳輸對象模式

這裏先介紹前三種中常見的設計模式:單例模式、工廠模式、建造者模式、適配器模式、裝飾模式、策略模式、觀察者模式、代理模式。


1.單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。顧名思義,他的意思就是一個類就只能有一個唯一的實例,需要該類的實例時,是無法像平常那樣new出來的,因爲他的構造方法是private的,他的實例只會new一次,之後再使用該類的實例時都不會在new了。

單例模式的實現可以分爲餓漢式和懶漢式,具體實現如下:

餓漢式:

public class EhSingleton {
    public static EhSingleton instance = new EhSingleton();

    private EhSingleton() {
    }

    public static EhSingleton getInstance() {
        return instance;
    }

懶漢式:

public class LhSingleton {
    public static volatile LhSingleton instance;

    private LhSingleton() {
    }

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

可以看出,餓漢式是在類加載的時候就把實例對象給創建了,而懶漢式則是在第一次使用到的時候把它創建出來。可以根據實際的需要選擇對應的方式。


2.工廠模式

工廠模式簡單的說就是對象通過工廠來生產出來,而不需要自己去new,只需要知道要創建的是什麼對象,之後去工廠提出來就可以了。

實現:有一個接口A,它有A1和A2兩個實現類,可以使用工廠根據名稱來獲取A的對應的對象。

/*******************************接口A****************************/
public interface A {
    void method();
}

/*******************************實現類A1****************************/
public class A1 implements A{
    @Override
    public void method() {
        System.out.println("this is A1");
    }
}
/*******************************實現類A2****************************/
public class A2 implements A {
    @Override
    public void method() {
        System.out.println("this is A2");
    }
}
/*******************************工廠****************************/
public class NormalFactory {
    public A bulid(String type) {
        if ("A1".equals(type)) {
            return new A1();
        } else if ("A2".equals(type)) {
            return new A2();
        }else {
            System.out.println("沒有對應的實現");
            return null;
        }
    }
}
/*****************************使用*******************************/
public static void main(String[] args) {
        //普通的工廠:
        NormalFactory normalFactory = new NormalFactory();
        A a1 = normalFactory.bulid("A1");
        a1.method();
        A a2 = normalFactory.bulid("A2");
        a2.method();
        A a3 = normalFactory.bulid("A3");
}

輸出如下:

在上面例子中可以看到,如果傳遞的參數錯誤的時候就不能正確的把對象創建出來了,如果使用多個工廠的模式(提供多個工廠方法),分別來創建對象就不用擔心了。

/****************************A A1 A2 同上***************************/

/****************************多個工廠類******************************/
public class MultiFactory {
    public A bulidA1() {
        return new A1();
    }

    public A bulidA2() {
        return new A2();
    }
}
/*******************************實現***********************************/
public static void main(String[] args) {
        MultiFactory multiFactory = new MultiFactory();
        A m1 = multiFactory.buildA1();
        m1.method();
        A m2 = multiFactory.buildA2();
        m2.method();
}

這樣直接創建指定的對象就沒有傳錯參數的煩惱了。

靜態工廠方法模式:將上述的工廠方法設爲靜態的,就不需要創建工廠的實例了。

抽象工廠模式:

在上述的工廠模式中,如果又添加了個實現類,就需要對工廠類進行修改,比較麻煩。所以可以把工廠給抽象出來,一個實現類對應一個工廠的實現類,再添加新的實現類時,只需要新寫一個工廠實現類即可。

實現:

/********************************A A1 A2 同上**********************************/

/*******************************工廠的抽象類***********************************/
public interface Provider {
    A bulid();
}
/******************************A1的工廠實現類***********************************/
public class A1Factory implements Provider {
    @Override
    public A bulid() {
        return new A1();
    }
}

/******************************A2的工廠實現類***********************************/
public class A2Factory implements Provider {
    @Override
    public A bulid() {
        return new A2();
    }
}
/*********************************使用******************************************/
    public static void main(String[] args) {
        Provider provider1 = new A1Factory();
        A p1 = provider1.bulid();
        p1.method();
        Provider provider2 = new A2Factory();
        A p2 = provider2.bulid();
        p2.method();
    }

3.建造者模式

當要創建一個過於複雜的對象時候,使用建造者模式可以把它分解成多個簡單的對象一步一步的構建起來。

比如一個對象它有ABC3個複雜的模塊,創建起來就比較複雜;或者對象可能只需要AC模塊,可能全部都需要;或者對象要先創造B模塊再創造A模塊...建造者模式就是將這3個模塊分離開,單獨創建,在根據具體的需求/順序來將這些模塊組合成完整的對象。

實現:

1).需要創建的複雜對象Model,裏面有3個模塊ABC

public class Model {
    private String A;

    private String B;

    private String C;

    public String getA() {
        return A;
    }

    public void setA(String a) {
        A = a;
    }

    public String getB() {
        return B;
    }

    public void setB(String b) {
        B = b;
    }

    public String getC() {
        return C;
    }

    public void setC(String c) {
        C = c;
    }

    @Override
    public String toString() {
        return "Model{" +
                "A='" + A + '\'' +
                ", B='" + B + '\'' +
                ", C='" + C + '\'' +
                '}';
    }
}

2).ABC3個模塊創建以及model組合的接口

public interface ModelBuilder {
    void buildA();

    void buildB();

    void buildC();

    Model buildModel();
}

3).實現接口裏ABC模塊的創建

public class BuildMethod implements ModelBuilder {
    private Model model;
    public BuildMethod() {
        model = new Model();
    }

    @Override
    public void buildA() {
        model.setA("A");
        System.out.println("buildA");
    }

    @Override
    public void buildB() {
        model.setB("B");
        System.out.println("buildB");
    }

    @Override
    public void buildC() {
        model.setC("C");
        System.out.println("buildC");
    }

    @Override
    public Model buildModel() {
        return model;
    }
}

4).根據需求定義對象創建組裝的方式

public class ModelConstruct {
    //只構建AC
    public Model buildAC(ModelBuilder builder) {
        builder.buildA();
        builder.buildC();
        return builder.buildModel();
    }

    //全部構建
    public Model buildABC(ModelBuilder builder) {
        builder.buildA();
        builder.buildB();
        builder.buildC();
        return builder.buildModel();
    }
}

5).使用

public static void main(String[] args) {
        ModelConstruct mc = new ModelConstruct();
        Model m1 = mc.buildAC(new BuildMethod());
        System.out.println(m1);
        Model m2 = mc.buildABC(new BuildMethod());
        System.out.println(m2);
    }

結果如下:


4.適配器模式

適配器模式簡單的說就是,類的接口不符合自己的需求,在不改變類的基礎上,增加一個適配器,轉成自己需要的。就像插頭是2孔,但是插座是3孔的,這時候就需要一個“適配器”,把2孔的插頭轉換成3孔的就能插進去了。

適配器模式分爲3類:類的適配器、對象的適配器、接口的適配器

類的適配器:

/********************實際需要method1、method2兩個方法********************/
public interface Target {
    void method1();
    void method2();
}
/**********************但原有的類只有method1***********************/
public class Source {
    public void method1() {
        System.out.println("this is method1");
    }
}
/*********************需要個適配器增加method2的方法********************/
public class ClassAdapater extends Source implements Target{
    @Override
    public void method2() {
        System.out.println("this is method2");
    }
}
/************這樣在ClassAdapater裏method1、method2都有了************/
/***************************使用******************************/
    public static void main(String[] args) {
        Target target = new ClassAdapater();
        target.method1();
        target.method2();
}

對象的適配器:

與類的適配器思路相同,只是不繼承Source類,而是Source類的實例來實現method1.

/*********************Target和Sourcetoo同上*****************************/
public class ObjectAdapter implements Target {
    private  Source source;

    public ObjectAdapter(Source source) {
        this.source = source;
    }

    @Override
    public void method1() {
        source.method1();
    }

    @Override
    public void method2() {
        System.out.println("this is method2");
    }
}

/*******************使用**************************/
    public static void main(String[] args) {
        Source source = new Source();
        Target target1 = new ObjectAdapter(source);
        target1.method1();
        target.method2();
}

接口的適配器:

有時候一個接口有很多的抽象方法,寫實現類時就要把他們全部實現,有時並不需要使用全部的接口。接口的適配器就是:創建一個抽象類來實現這些接口,當我們使用時,只需要繼承這個抽象類,然後重寫我們需要的方法就可以。


5.裝飾模式

顧名思義,就是給一個對象增加的方法增加一些新的功能做裝飾。比如要對A對象的F方法在調用時添加打印helloWord的功能,其他對象沒有。只需要裝飾者和A都同一個接口,裝飾者擁有A的實例並將該接口重寫即可。

實現:

/***************************被裝飾的類*************************/
public class Source implements Target{
    public void method() {
        System.out.println("method!!!");
    }
}
/*************************需要被裝飾的方法的接口********************/
public interface Target {
    void method();
}

/************************裝飾者************************/
public class Decorator implements Target {
    private Target source;

    public Decorator(Target source) {
        this.source = source;
    }

    @Override
    public void method() {
        System.out.println("hello world!!!!");
        source.method();
    }
}
/****************使用時,把需要裝飾的對象傳給裝飾者裝飾***************/
  public static void main(String[] args) {
        Target source = new Source();
        Target target = new Decorator(source);
        target.method();
    }

結果:


6.策略模式

簡單來說,就是多個方法都去實現一個統一的方法。策略模式提供了這個方法的不同實現,在使用的時候只需要決定要那個實現即可。

/***********************統一的計算方法******************/
public interface Strategy {
    int cal(int x,int y);
}

/*********************加法的實現*******************/
public class Add implements Strategy {
    @Override
    public int cal(int x, int y) {
        return x + y;
    }
}
/*********************減法的實現*******************/
public class Sub implements Strategy {
    @Override
    public int cal(int x, int y) {
        return x - y;
    }
}
/********************應用的場景-計算器*****************/
public class Calculator {
    public int doCalc(int x, int y, Strategy strategy) {
        return strategy.cal(x, y);
    }
}
/***********************使用**************************/
    public static void main(String[] args) {
        Strategy add = new Add();
        Strategy sub = new Sub();
        int x = 9;
        int y = 3;
        Calculator calc = new Calculator();
        int addRes = calc.doCalc(x,y,add);
        System.out.println(x + "+" + y + "=" + addRes);
        int subRes = calc.doCalc(x,y,sub);
        System.out.println(x + "-" + y + "=" + subRes);
    }

7.觀察者模式

觀察者模式就是當一個對象有變化時,其他和它有關聯的對象都會收到消息,然後發生相應的變化。

/**************************觀察者**************************/
public interface Observer {
    void update();
}
/*************************發生變化的主體*****************/
public class Subject {
    //依賴的觀察者
    private List<Observer> observers = new ArrayList<>();

    public void change() {
        System.out.println("subject has been change!");
        //通知給觀察者
        notifyObserver();
    }

    
    public void addObserver(Observer observer){
        //添加觀察者
        observers.add(observer);
    }

    private void notifyObserver() {
        for (Observer o :observers) {
            //觀察者執行相應的方法
            o.update();
        }
    }
}
/************************具體的觀察者A****************************/
public class ObserverA implements Observer {
    @Override
    public void update() {
        System.out.println("ObserverA notify!");
    }
}
/************************具體的觀察者B****************************/
public class ObserverB implements Observer {
    @Override
    public void update() {
        System.out.println("ObserverB notify!");
    }
}
/***********************使用****************************/
 public static void main(String[] args) {
        Subject subject = new Subject();
        Observer observerA = new ObserverA();
        Observer observerB = new ObserverB();
        subject.addObserver(observerA);
        subject.addObserver(observerB);
        subject.change();
    }

8.代理模式

代理模式就是不直接使用實現這個功能的對象,或者訪問這個對象有困難,訪問的時候使用另一個對象,而不是直接實現功能的對象。就像不想去太遠的火車站買車票,而選擇去火車站代售點買車票。

靜態代理實現:

/**************************要實現的功能****************/
public interface Book {
    void read();

    void write();
}
/************************真正實現功能的類***************/
public class RealBook implements Book{

    @Override
    public void read() {
        System.out.println("read book!");
    }

    @Override
    public void write() {
        System.out.println("write book!");
    }
}
/*********************代理類**********************/
public class ProxyBook implements Book {
    private RealBook realBook;
    @Override
    public void read() {
        if (realBook == null) {
            realBook = new RealBook();
        }
        realBook.read();
    }

    @Override
    public void write() {
        if (realBook == null) {
            realBook = new RealBook();
        }
        realBook.write();
    }
}
/*********************使用*********************/
    public static void main(String[] args) {
        Book book = new ProxyBook();
        book.read();
        book.write();
}

可以看到調用book的read和write方法的是ProxyBook,但ProxyBook其實是用realBOOK來實現。

動態代理:

接口的方法有很多的話,就要編寫很多的實現方法,而且沒當新增一個方法,實現類也要跟着添加,就比較麻煩,而使用動態代理就方便很多。動態代理的實現方式,是通過反射來實現的,藉助Java自帶的java.lang.reflect.Proxy,通過固定的規則生成。

實現:

/*******************book和realbook同上*********************/
/**********************動態代理類*******************/
public class DynamicProxy implements InvocationHandler {
    private Object object;

    public DynamicProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(object,args);
    }
}
/*****************************使用*****************************/
    public static void main(String[] args) {
        Book realBook = new RealBook();
        DynamicProxy proxy = new DynamicProxy(realBook);
        ClassLoader classLoader = realBook.getClass().getClassLoader();
        Book proxyBook = (Book) Proxy.newProxyInstance(classLoader, new Class[]{Book.class}, proxy);
        proxyBook.write();
        proxyBook.read();
}

對於常用的設計模式,我理解的就是這些啦。學習了之後,發現設計模式在平時還挺多的,像spring裏就用了很多,比如是單例、工廠、代理等等,理解設計模式之後看源碼也容易多了。


上述例子全部代碼:https://github.com/fi00wind/design-pattern

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