幾種設計模式的作用及實現

工廠

工廠模式主要是爲了解耦,A類想要調用B類,但是實例化B對象比較複雜,所以我們就把B對象的創建放到工廠裏面統一管理。由於B對象的創建已經統一了起來,所以即使業務邏輯發生變化,我們也只需要修改工廠類,而不需要尋找每個創建B對象的地方去修改。

例如,我們項目可能會使用多個數據庫源,但是他們的JDBC配置是不同的,所以我們就可以使用工廠模式來統一,也減少了使用者出錯的概率。

使用場景:

精確數據計算,獲取Jdbc連接,或者類似的通訊連接

實現:

簡單工廠:

首先創建一個接口

public interface Operation {
 
    public double getResult(double numberA,double numberB) throws Exception;
 
}

然後是加減乘除的實現類

public class Add implements Operation{
 
    // 加法計算
    public double getResult(double numberA, double numberB) {
 
        return numberA + numberB;
    }
}

// 減  Sub 
// 乘  Mul 
// 除  Div 

然後是簡單工廠

public class EasyFactory {
 
    // 簡單工廠,根據字符串創建相應的對象
    public static Operation createOperation(String name) {
        Operation operationObj = null;
        switch (name) {
            case "+":
                operationObj = new Add();
                break;
            case "-":
                operationObj = new Sub();
                break;
            case "*":
                operationObj = new Mul();
                break;
            case "/":
                operationObj = new Div();
                break;
        }
        return operationObj;
    }
}

調用:

public class Client {
 
    public static void main(String[] args) throws Exception {
 
        Operation add = EasyFactory.createOperation("+");
        Operation sub = EasyFactory.createOperation("-");
        Operation mul = EasyFactory.createOperation("*");
        Operation div = EasyFactory.createOperation("/");
 
        System.out.println(add.getResult(1, 1));
        System.out.println(sub.getResult(1, 1));
        System.out.println(mul.getResult(1, 1));
        System.out.println(div.getResult(1, 1));
    }
}

工廠模式

工廠方法模式是對簡單工廠模式進一步的解耦,因爲在工廠方法模式中是一個子類對應一個工廠類,而這些工廠類都實現於一個抽象接口。這相當於是把原本會因爲業務代碼而龐大的簡單工廠類,拆分成了一個個的工廠類,這樣代碼就不會都耦合在同一個類裏了。

實現:

首先定義工廠接口:

import com.ljy.Mode.Operation;
 
public interface Factory {
 
    public Operation createOperation() ;
 
}

然後是工廠實現類

// 加法類工廠
public class AddFactory implements Factory{
 
    public Operation createOperation() {
        System.out.println("加法運算");
        return new Add();
    }
}
 
// 減法類工廠
public class SubFactory implements Factory{
 
    public Operation createOperation() {
        System.out.println("減法運算");
        return new Sub();
    }
}
........

調用:

public class Client {
 
    public static void main(String[] args) throws Exception {
 
        // 使用反射機制實例化工廠對象,因爲字符串是可以通過變量改變的
        Factory addFactory = (Factory) Class.forName("com.ljy.mode.factory.AddFactory").newInstance();
        Factory subFactory=(Factory) Class.forName("com.ljy.mode.factory.SubFactory").newInstance();
 
        // 通過工廠對象創建相應的實例對象
        Operation add = addFactory.createOperation();
        Operation sub = subFactory.createOperation();
 
        System.out.println(add.getResult(1, 1));
        System.out.println(sub.getResult(1, 1));
    }
}

抽象工廠模式

抽象工廠與工廠方法模式的區別在於:抽象工廠是可以生產多個產品的,它圍繞一個超級工廠創建其他工廠。該超級工廠又稱爲其他工廠的工廠。

說白了,就是創建一個抽象工廠,然後建立其他工廠類實現。

實現就不寫了 有點麻煩,以後補充。

單例

單例設計模式,就是有一個準則,那就是只有一個對象被創建!!!,而且單例也是最簡單的設計模式了

單例模式的作用很明顯,首先,節省內存,因爲我只有一個對象,其次,我能保證數據的唯一性。

爲了保證對象唯一性,所以我們首先要確定構造方法的私有性,這樣的話就不能通過new的方式創建對象了,然後我們可以在內部創建自身對象,但是對象必須是私有的,然後,我們需要一個靜態的公開的方法,來讓外部類獲取對象。

餓漢模式

餓漢,一直在叫着“我好餓啊”,啥都想吃,所以,這種設計模式,是在一開始就已經把對象創建好了,但是,會造成內存浪費啊,就算沒用到他,也會在哪裏佔着浪費內存。

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
        return instance;  
    }  
}

懶漢模式

懶漢啥樣的,那當然是懶啊,所以懶漢模式,就意味着,只有等到需要這個對象的時候,纔回去創建對象

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}

但是這種設計模式,有個很大的缺點,那就是線程不安全!!!一旦多個線程同時進來,那就會創建多個對象,違反我們的原則了。

那要是線程不安全咋辦呢,簡單啊,那就加鎖唄。

餓漢模式(同步方法)

public class Singleton {  
    private static Singleton singleton;  
    private Singleton (){}  
    public static synchronized Singleton getSingleton() {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        return singleton;  
    }  
}

但是加鎖一時爽,效率太差了啊,那麼多線程都在方法外面等着呢啊

餓漢模式(雙重檢查)

既然加鎖方法會造成效率低下,那咱們就把加鎖的位置改一下吧,放在方法裏面

public class Singleton {  
    private static Singleton singleton;  
    private Singleton (){}  
    public static synchronized Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
    }  
}

但是,一想想,不對啊  這樣還是會造成線程不安全啊,導致出現兩個對象啊,不行不行,那咋辦呢  要不再加個鎖?可是鎖加在哪裏呢?

餓漢模式(雙重鎖)

於是,我們就加了個鎖,將這個鎖加載了聲明對象的時候,使用了volatile,這個版本也是非常完美的版本的,面試推薦哦

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
    }  
}

那麼我們總想着懶漢餓漢,是不是還有其他方式呢(注意,上面任意一種方式,都是無法阻止反射的!!!或者通過序列化 ,我們也是可以獲得兩個對象),其實是有的,那就是枚舉,枚舉是天生的單例,而且可以阻止反射哦

枚舉

public enum  EnumSingleton {
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}

看,是不是,非常簡單,代碼量也很少

裝飾器

裝飾器模式的定義是允許向一個現有的對象添加新的功能,同時又不改變其結構。就像是,我下個單,中間可能會有打折,可能會有運費,價格就會變動,所以我們就可以通過裝飾器模式來實現。

至於實現的話,其實也是主要靠繼承子類重寫父類方法的原理來實現

首先一個接口是肯定的了:

public interface Component {
	public void biu();
}

然後接口實現類:

public class ConcretComponent implements Component {

	public void biu() {
        
        System.out.println("biubiubiu");
    }
}

接下來是裝飾器的接口:

//裝飾類
public class Decorator implements Component {

  public Component component;
  
  public Decorator(Component component) {
      
      this.component = component;
  }
  
  public void biu() {
      
      this.component.biu();
  }
}

最後就是裝飾器接口實現類:

//具體裝飾類
public class ConcreteDecorator extends Decorator {

  public ConcreteDecorator(Component component) {
      super(component);
  }
  public void biu() {
      System.out.println("fire in the hole");
      this.component.biu();
  }
}

說白了是不是感覺很簡單的,不就是多態麼。

具體調用是這樣的:

public class Client {
	public static void main(String[] args) {
		//使用裝飾器
		Component component = new ConcreteDecorator(new ConcretComponent());
		component.biu();
	}
}

代理

代理,一說起代理我們就可以聯想到代理商,代理商幹嘛的,那不就是幫助別人做事的麼,例如什麼獵頭啊,中介啊,類似的,都是代理的體現。而且我們還可以藉助代理類再次增加一些功能,而不需要修改原有代碼。符合開閉原則

至於到項目開發中,我們總會遇見一些事情,是我們不想讓我們的實現類實現的,例如,日誌啊,緩存啊,我們想在這個實現類執行的前後執行另外的一些操作,這就用到了我們的代理模式了。

同時代理模式和裝飾器模式是不一樣的!!!。

裝飾器模式和代理模式的區別:

裝飾器模式關注於在一個對象上動態的添加方法,然而代理模式關注於控制對對象的訪問。換句話 說,用代理模式,代理類(proxy class)可以對它的客戶隱藏一個對象的具體信息。因此,當使用代理模式的時候,我們常常在一個代理類中創建一個對象的實例。並且,當我們使用裝飾器模 式的時候,我們通常的做法是將原始對象作爲一個參數傳給裝飾者的構造器。

代理分爲靜態代理動態代理。

靜態代理

靜態代理是在運行前,代理類的class文件就已經創建好了。但是呢,靜態代理也有缺點,我們得爲每一個服務都得創建代理類,工作量太大,不易管理。同時接口一旦發生改變,代理類也得相應修改。 

首先是接口:

package com.ljy.util.designMode.proxy;

public interface BuyHouse {
    void buyHosue();
}

然後是實現類:

package com.ljy.util.designMode.proxy;

public class BuyHouseImpl implements BuyHouse {

    @Override
    public void buyHosue() {
        System.out.println("我要買房");
    }
}

然後就是靜態代理類:

package com.ljy.util.designMode.proxy;

public class BuyHouseProxy implements BuyHouse {

    private BuyHouse buyHouse;

    public BuyHouseProxy(final BuyHouse buyHouse) {
        this.buyHouse = buyHouse;
    }

    @Override
    public void buyHosue() {
        System.out.println("買房前準備");
        buyHouse.buyHosue();
        System.out.println("買房後裝修");

    }
}

最後是測試類:

package com.ljy.util.designMode.proxy;

public class ProxyTest {
    public static void main(String[] args) {
        BuyHouse buyHouse = new BuyHouseImpl();
        buyHouse.buyHosue();
        BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
        buyHouseProxy.buyHosue();
    }
}

動態代理

動態代理是在程序運行時通過反射機制動態創建的。

在動態代理中我們不再需要再手動的創建代理類,我們只需要編寫一個動態處理器就可以了。真正的代理對象由JDK再運行時爲我們動態的來創建。

首先讓我們創建動態代理類:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDKDynamicProxy
 * jdkd動態代理
 *
 * @author
 * @create 2018-03-29 16:17
 **/
public class JDKDynamicProxy implements InvocationHandler { // 一定要實現這個類哦

    private Object target;

    public JDKDynamicProxy(Object target) {
        this.target = target;
    }

    /**
     * 獲取被代理接口實例對象
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("買房前準備");
        Object result = method.invoke(target, args);
        System.out.println("買房後準備");
        return result;
    }
}

然後就是測試類:

package com.ljy.util.designMode.proxy.dynamic;

import com.ljy.util.designMode.proxy.BuyHouse;
import com.ljy.util.designMode.proxy.BuyHouseImpl;

/**
 * Client
 * client測試代碼
 **/
public class Client {
    public static void main(String[] args) {
        // 將JDK動態代理生成的class文件保存到本地
//        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); // 或者是這個

        // jdk動態代理測試
    	BuyHouse buyHouse = new JDKDynamicProxy(new BuyHouseImpl()).getProxy();
    	buyHouse.buyHosue();
    }
}

是不是,只需要一個動態代理類就可以實現了,還不需要針對每個實現類開發。

 


未完待續。。。

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