二十三種設計模式詳解

1 動態代理

代理是什麼?

代理其實就是代爲處理的意思,個人理解就是產生一個處理類對需要代理的對象進行處理,並且返回該代理對象

靜態代理

首先實現一個Moveable接口,表示代理與被代理的對象都屬於同一個類別

public interface Moveable {
    void move();

    void stop();
}

接下來實現一個被代理對象坦克類,有着坦克開始與結束的方法

public class Tank implements Moveable{
    @Override
    public void move() {
        System.out.println("Tank moving 。。");
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void stop() {
        System.out.println("Tank stop");
    }
}

接下來如果想要知道Tank的move方法允許了多久的話,最直接的就是使用繼承方法,但是如果使用繼承的話,想要再知道運行的日誌的話就需要再繼承擴展一個類,因此使用聚合來解耦合。

實現時間代理

public class TankTimeProxy implements Moveable{
    Moveable t;

    public TankTimeProxy(Moveable t) {
        super();
        this.t = t;
    }

    @Override
    public void move() {
        Long start = System.currentTimeMillis();
        t.move();
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }

    @Override
    public void stop() {
        Long start = System.currentTimeMillis();
        t.stop();
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
}

實現日誌代理

public class TankLogProxy implements Moveable{
    Moveable t;

    public TankLogProxy(Moveable t) {
        super();
        this.t = t;
    }

    @Override
    public void move() {
        System.out.println("Tank start");
        t.move(); //這裏調用的其實是Tank的方法
        long end = System.currentTimeMillis();
        System.out.println("Tank end");
    }

    @Override
    public void stop() {
    }
}

調用地方的代碼如下

 public static void main(String[] args) throws NoSuchMethodException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
        Tank t =new Tank();
        TankTimeProxy ttp =new TankTimeProxy(t);
        TankLogProxy tlp = new TankLogProxy(ttp);  //在時間代理上再加上日誌代理
//        Moveable m = tlp;
        Moveable m = tlp;
        m.move();
}

打印數據的數據如下

Tank start
Tank moving 。。
861
Tank end

動態代理

(這裏借用網上的例子,代碼已經測過無誤)
在完成了靜態代理之後,我們需要考慮另外的問題,如果在我們需要計算的是除了move之外的其他方法的運行時間,那麼豈不是又要生成一個代理類了?多個方法的話就是多個代理類,會導致類無限制擴展。因此就用到了動態代理

使用動態代理需要用到反射,反射是用於獲取已創建實例的方法或者屬性,並對其進行調用或者賦值。在這裏我們可以動態一個代理對象,並且動態編譯。然後,再通過反射創建對象並加載到內存中,就實現了對任意對象進行代理
在這裏插入圖片描述
第一步:定義自己的一個代理的邏輯處理接口,用來實現處理自定義邏輯

public interface InvocationHandler {
     void invoke(Object o, Method m) throws InvocationTargetException, IllegalAccessException;
}

第二步:定義一個運行時間處理器,用來處理代理對象,

public class TimeHandler implements InvocationHandler {
    //被代理的對象
    private Object target;

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

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }
    // o 是代理對象
    @Override
    public void invoke(Object o, Method m) throws InvocationTargetException, IllegalAccessException {
        long start = System.currentTimeMillis();
        System.out.println("starttiem: "+ start);
        m.invoke(target); //調用被代理對象原來的方法
        long end = System.currentTimeMillis();
        System.out.println("time: " + (end -start));
    }
}

第三步:編寫動態代理的代碼:

public class Proxy {

    /**
     * 動態代理
     * 產生新的代理類
     * infce 產生對應接口的代理   該接口對應的處理方法
     * @return
     */
    public static Object newProxyInstance(Class infce, InvocationHandler h) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        String methodStr = "";
        Method[] methods = infce.getMethods();
        String rt = "\r\n";
        for(Method m : methods) { //動態代碼 編譯後動態生成一個對象,該對象包含着InvocationHandler,
            methodStr += "@Override" + rt +
                    "public void " + m.getName() + "() {" + rt +
                    "    try {" + rt +
                    "    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
                    "    h.invoke(this, md);" + rt +  //傳入的是this
                    "    }catch(Exception e) {e.printStackTrace();}" + rt +

                    "}";
        }


        String src =
                "package com.bjsxt.proxy;" +  rt +
                        "import java.lang.reflect.Method;" + rt +
                        "public class $Proxy1 implements " + infce.getName() + "{" + rt +
                        "    public $Proxy1(InvocationHandler h) {" + rt +
                        "        this.h = h;" + rt +
                        "    }" + rt +


                        "    com.bjsxt.proxy.InvocationHandler h;" + rt +

                        methodStr +
                        "}";
        String fileName =
                "d:/src/com/bjsxt/proxy/$Proxy1.java";
        File f = new File(fileName);
        FileWriter fw = new FileWriter(f);
        fw.write(src);//將要編譯的代碼寫到文件中去
        fw.flush();
        fw.close();

        //編譯TimeProxy源碼
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileName);
        JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();

        //加載到內存中並創建對象
        URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
        System.out.println(c);
        //傳過來那個處理器就按照那個處理器去實現
        Constructor ctr = c.getConstructor(InvocationHandler.class);
        Object m = ctr.newInstance(h);
        //m.move();
        return m;
    }
}

第四步:調用:

public static void main(String[] args) throws Exception {
		Tank t = new Tank();
		InvocationHandler h = new TimeHandler(t);
		
		Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);
		
		m.move();
	}

在這裏Proxy.newProxyInstance(Moveable.class, h)生成的Proxy1代理類爲:

class Prox1 implements Moveable {
   private InvocationHandler handler;

  public TimeProxy(InvocationHandler handler) {
     this.handler = handler;
  }

  @Override
  public void move() {
     try {
        Method method = com.youngfeng.proxy.Flyable.class.getMethod("move");
        this.handler.invoke(this, method, null);
    } catch(Exception e) {
        e.printStackTrace();
    }
  }
}

方法調用鏈:Proxy1 . move() -> TimeHandler.invoke(Object o, Method m) -> Tank.move();

主要邏輯
  • Proxy->newProxyInstance(infs, handler) 用於生成代理對象
  • InvocationHandler:這個接口主要用於自定義代理邏輯處理
  • 爲了完成對被代理對象的方法攔截,我們需要在InvocationHandler對象中傳入被代理對象實例。

因此對其他對象進行代理就不再需要修改newProxyInstance方法中的代碼

2 策略模式

策略模式主要用於不同的類根據不同的方法去解決,比如一隻貓的屬性有體重跟身高,實現按身高進行排序與按體重進行排序,即可使用策略模式

  • Comparable表明實現該接口的類必須實現compare方法,
  • Comparator則是比較器的具體實現
    第一步:定義一個實現了Comparable的Cat類
public class Cat implements Comparable{
    int Height;
    int weight;
    Comparator comparator;
    public int getHeight() {
        return Height;
    }

    public void setHeight(int height) {
        Height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public Comparator getComparator() {
        return comparator;
    }

    public void setComparator(Comparator comparator) {
        this.comparator = comparator;
    }

    @Override
    public int compareTo(Object o) {
        return comparator.compare(this, o);
    }
}

在這個類裏面實現了一個繼承器,傳入自己實現的Comparator可以實現對身高和體重進行比較。
第二步:實現一個接口類使它的實現類確定實現了compareTo方法

public interface Comparable {
    int compareTo(Object o);
}

第三步:實現一個比較器的接口:

public interface Comparator {
    int compare(Object o1, Object o2);
}

第四步:繼承Comparator接口實現身高比較器

public class CatHeightComparator implements Comparator{
    @Override
    public int compare(Object o1, Object o2) {
        Cat c1 = (Cat)o1;
        Cat c2 = (Cat)o2;
        if (c1.getHeight() > c2.getHeight()) return 1;
        else if (c1.getHeight() > c2.getHeight()) return -1;
        return 0;
    }
}

策略模式其實就是需要變化的類其中聚合一個策略,根據需要傳入不同的實現類實現不同的需要

3 狀態模式

狀態模式使用對象的形式來記錄某種狀態。使用狀態模式可以省去多個if-else或者是switch的判斷。可以直接使用對象方法的形式來處理邏輯。
例如在這裏實現兩個類Boy和MM類,其中MM中包含了抽象類State。代表了MM此時的心情

public class Boy {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
public class MM {
    private MMState state = new MMHappyState();

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void smile(){
        state.smile();
    }

    public void cry(){
        state.cry();
    }
    public void say(){
        state.say();
    }
}

抽象類MyState

public abstract class MMState {
    public abstract void smile();

    public abstract void cry();
    public abstract void say();
}

代表MM好心情的MMHappyState

public class MMHappyState extends MMState{
    @Override
    public void smile() {

    }

    @Override
    public void cry() {

    }

    @Override
    public void say() {

    }
}

因此當MM狀態改變時,比如從開心的哭變成悲傷的哭只需要改變State即可,不需要修改其他代碼。

4 工廠模式

抽象工廠

當需要新的工廠時直接繼承VehicleFactory就可以
第一步:Moveable接口

public interface Moveable {
    void run();
}

所有的交通工具都實現了該接口
第二步:定義了汽車和飛機類

public class Plane implements Moveable{
    @Override
    public void run() {
        System.out.println("plane");
    }
}

public class Car implements Moveable{
    private static Car c = new Car();
    public void run(){
        System.out.println("run");
    }
    //靜態工廠方法 用來產生car產品
    public static Car getInstance(){
        return c;
    }
}

第三步:實現一個工廠類,負責實現創建所有實例的內部邏輯。

public abstract class VehicleFactory {
    abstract Moveable create();
}

第四步: 實現工廠接口

public class PlaneFactory extends VehicleFactory{
    @Override
    Moveable create() {
        return new Plane();
    }
}

public class CarFactory extends VehicleFactory{


    @Override
    Moveable create() {
        return new Car();
    }
}

在調用時需要那個類的工廠直接new它的工廠就行

public static void main(String[] args) {

       VehicleFactory factory = new PlaneFactory();
        Moveable m = factory.create();
        m.run();
    }

5 責任鏈模式

使多個對象都有處理請求的機會,從而避免了請求的發送者和接收者之間的耦合關係。將這些對象串成一條鏈,並沿着這條鏈一直傳遞該請求,直到有對象處理它爲止。

目標

用Filter模擬處理Request、Response

思路技巧

(1)Filter的doFilter方法改爲doFilter(Request,Resopnse,FilterChain),有FilterChain引用,爲利用FilterChain調用下一個Filter做準備
(2)FilterChain繼承Filter,這樣,FilterChain既是FilterChain又是Filter,那麼FilterChain就可以調用Filter的方法doFilter(Request,Resopnse,FilterChain)
(3)FilterChain的doFilter(Request,Resopnse,FilterChain)中,有index標記了執行到第幾個Filter,當所有Filter執行完後request處理後,就會return,以倒序繼續執行response處理

代碼如下:

Filter.java

public interface Filter {
  

   void doFilter (Request request, Response response,FilterChain filterChain);
}

HTMLFilter.java :替換HTML代碼

public class HTMLFilter implements Filter {

   @Override
   public void doFilter(Request request, Response response,
           FilterChain filterChain) {
       request.setRequestStr(request.getRequestStr().replace('<', '[').replace(">", "]")+"---HTMLFilter()");
       filterChain.doFilter(request, response, filterChain);
       response.setResponseStr(response.getResponseStr()+"---HTMLFilter()");
   }
}

3.SensitiveFilter.java 替換敏感詞彙

public class SensitiveFilter implements Filter {

    @Override
    public void doFilter(Request request, Response response,
            FilterChain filterChain) {
        request.setRequestStr(request.getRequestStr().replace("敏感", "幸福")+"---SensitiveFilter()");
        filterChain.doFilter(request, response, filterChain);
        response.setResponseStr(response.getResponseStr()+"---SensitiveFilter()");
    }

}

4.FilterChian.java 過濾器鏈

public class FilterChain implements Filter {

    private List<Filter> filters = new ArrayList<Filter>();
    int index = 0;    //標記執行到第幾個filter
    
    //把函數的返回值設爲FilterChain,返回this,就能方便鏈式編寫代碼
    public FilterChain addFilter(Filter filter) {
        filters.add(filter);
        return this;
    }

    public void doFilter(Request request, Response response, FilterChain fc) {
        if(index == filters.size()) return ;
        Filter f = filters.get(index);
        index++;
        f.doFilter(request, response, fc);
    }
}

5.Request.java 和 Response.java

public class Request {

    private String requestStr;

    public String getRequestStr() {
        return requestStr;
    }

    public void setRequestStr(String requestStr) {
        this.requestStr = requestStr;
    }
    
}

public class Response {

    private String responseStr;

    public String getResponseStr() {
        return responseStr;
    }

    public void setResponseStr(String responseStr) {
        this.responseStr = responseStr;
    }
    
}

6Test.java

public class Test {

    @org.junit.Test
    public void testFilter(){
        
        String msg = "<html>敏感字眼</html>"; 
        
        Request request = new Request();
        request.setRequestStr(msg);
        Response response = new Response();
        response.setResponseStr("response------------");
        
        FilterChain fc = new FilterChain();
        fc.addFilter(new HTMLFilter()).addFilter(new SensitiveFilter());
        
        fc.doFilter(request, response, fc);
        System.out.println(request.getRequestStr());
        System.out.println(response.getResponseStr());
        
    }

}

6 命令模式

命令也是類,將命令作爲一個類來保存,當要使用的時候可以直接拿來使用
在這裏插入圖片描述
1.Client.java

public class Client {
 
     public void request(Server server){
         server.addCommand(new TextCommand());
         server.addCommand(new ImageCommand());
         server.doSomething();
     }
 }

2.Server.java

 public class Server {
 
     private List<Command> commands = new ArrayList<Command>();
 
     public void doSomething() {
         for(Command c : commands){
             c.execute();
         }
     }
 
     public void addCommand(Command command) {
         commands.add(command);
     }
 
 }

3.Command.java

public abstract class Command {
 
     public abstract void execute();
     public abstract void unDo();
 
 }

4.TextCommand.java

 public class TextCommand extends Command {
 
     @Override
     public void execute() {
         System.out.println("TextCommand...........");
     }
 
     @Override
     public void unDo() {
         // 涉及到操作的歷史記錄
     }
 
 }

5.ImageCommand.java

public class ImageCommand extends Command {
 
     @Override
     public void execute() {
         System.out.println("ImageCommand...........");
     }
 
     @Override
     public void unDo() {
         // 涉及到操作的歷史記錄
     }
 
 }

6.Test.java

public class Test {
 
     @org.junit.Test
     public void test(){
         Client c = new Client();
         c.request(new Server());
     }
 
 }

7 橋接模式

設想如果要繪製矩形、圓形、橢圓、正方形,我們至少需要4個形狀類,但是如果繪製的圖形需要具有不同的顏色,如紅色、綠色、藍色等,此時至少有如下兩種設計方案:
• 第一種設計方案是爲每一種形狀都提供一套各種顏色的版本。
在這裏插入圖片描述
• 第二種設計方案是根據實際需要對形狀和顏色進行組合。
在這裏插入圖片描述
模式定義
橋接模式(Bridge Pattern):將抽象部分與它的實現部分分離,使它們都可以獨立地變化。它是一種對象結構型模式,又稱爲柄體(Handle and Body)模式或接口(Interface)模式
在這裏插入圖片描述
應用情況:(1)兩個維度擴展(2)排列組合
在這裏插入圖片描述
1.Gift.java 禮物

public class Gift {
 GiftImpl impl;
}

2.GiftImpl.java 繼承該類是具體物品


public class GiftImpl {
}

3.WarmGift.java

public class WarmGift extends Gift {

    public WarmGift(GiftImpl giftImpl) {
        //調用父類的giftImpl
        this.giftImpl = giftImpl;
    }

    @Override
    public String toString() {
        return this.getClass().getName()+"-----"+giftImpl.getClass().getName();
    }
}

4.WildGift.java

public class WildGift extends Gift {

    public WildGift(GiftImpl giftImpl) {
        //調用父類的giftImpl
        this.giftImpl = giftImpl;
    }

    @Override
    public String toString() {
        return this.getClass().getName()+"-----"+giftImpl.getClass().getName();
    }
}

5.Flower.java

public class Folwer extends GiftImpl{
}

6.Ring.java

public class Ring extends GiftImpl{
}

7.MM.java

public class MM {
    
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
}

8.Boy.java
pursue(MM mm)是送給女孩子禮物的方法 想要送不同的禮物修改gift = new WildGift(new Flower())即可

public class Boy {

    private String name;
    
    public void pursue(MM mm){
        Gift gift = new WildGift(new Flower());
        give(gift, mm);
        System.out.println(gift);
    }

    public void give(Gift gift, MM mm) {
        
    }
}

9.Test.java

public class Test {

    @org.junit.Test
    public void test() {
        Boy b = new Boy();
        b.pursue(new MM());
    }

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