《設計模式之禪》學習及源碼示例---裝飾模式\適配器模式\代理模式 一、裝飾模式 二、適配器模式 三、代理模式

一、裝飾模式

定義:

動態的給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更加靈活

組成角色:
Component:抽象被裝飾者

一個接口或者是抽象類,通常是我們需要修飾的類的接口或者是繼承的抽象類

ConcreteComponent:具體被裝飾者

我們需要裝飾的對象就是這個類,這個類一般會實現或者繼承Component

Decorator:裝飾抽象角色

這個接口是一個抽象類實現或繼承Component,作爲具體裝飾角色和被裝飾角色的中轉對象,非常重要

ConcreteDecorator:具體的裝飾角色

ConcreteDecoratorA\ConcreteDecoratorB\ConcreteDecoratorC.....作爲具體的裝飾實現,繼承Decorator現象具體對ConcreteComponent的裝飾

需求:現在我們要給一個毛坯房裝修

代碼演示:

public interface Component {
    void operate();
}
public class ConcreteComponent implements Component {
    @Override
    public void operate() {
        System.out.println("這是一間毛坯房");
    }
}
public abstract class Decorator implements Component{
    private Component component=null;
    //用於傳遞被裝飾者
    public Decorator(Component component) {
        this.component = component;
    }

    //給具體裝飾者執行,並在其之上進行添加前後置功能
    @Override
    public void operate() {
        this.component.operate();
    }
}
public class ConcreteDecoratorDoor extends Decorator {
    public ConcreteDecoratorDoor(Component component) {
        super(component);
    }
    private void method1(){
        System.out.println("裝個門");
    }
    private void method2(){
        System.out.println("門上裝個眼睛");
    }

    @Override
    public void operate() {
        super.operate();
        method1();
        method2();
    }
}
public class ConcreteDecoratorWindow extends Decorator {
    public ConcreteDecoratorWindow(Component component) {
        super(component);
    }

    private void method1(){
        System.out.println("裝個窗戶");
    }
    private void method2(){
        System.out.println("窗戶換上防彈玻璃");
    }

    @Override
    public void operate() {
        super.operate();
        method1();
        method2();
    }
}

測試

public class Test {
    public static void main(String[] args) {
        Component mopifang=new ConcreteComponent();
        mopifang=new ConcreteDecoratorDoor(mopifang);
        mopifang=new ConcreteDecoratorWindow(mopifang);
        mopifang.operate();
    }
}

源碼使用示例:JDK的IO使用了裝飾模式


InputStream作爲Component,FilterInputStream是Decorator,BufferedInputStream通過繼承FilterInputStream,給FileInputStream裝飾拓展了功能

public class FilterInputStream extends InputStream{
    protected  InputStream in;
    
    protected  FilterInputStream(InputStream in){
        this.in = in;
    }   
    ...
}
public class BufferedInputStream extends FilterInputStream {
..
}

通過傳入參數實現相關的裝飾

public class Test {
    public static void main(String[] args) {
        try {
            InputStream in = new FileInputStream(new File("c:\\xxx\\xxxx"));
            BufferedInputStream in1 = new BufferedInputStream(in);
            LineNumberInputStream in2 = new LineNumberInputStream(in1);
                         ....
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

二、適配器模式

定義:

將一個類的接口變成客戶端所期待的另一種接口,從而使原本因爲接口不匹配而無法在一起工作的兩個類能夠在一起工作

組成角色:
Target目標接口

該接口就是我們的期待接口,其他接口向這個接口轉換

Adaptee源角色

希望被轉換的接口

Adapter適配器角色

適配器角色職責是把源角色轉爲目標接口

需求:現在我們需要將220V的電壓適配爲5V

代碼演示:

public interface Target {
    Integer output5v();
}
public class Adaptee {
    private final static Integer output220V=220;
    public Integer output220V(){
        System.out.println("這是"+output220V+"V的電壓");
        return output220V;
    }
}

適配後

public class Adapter extends Adaptee implements Target {
    @Override
    public Integer output5v() {
        Integer output220V = super.output220V()/44;
        System.out.println("轉化後變成"+output220V+"V");
        return output220V;
    }
}
public class Test {
    public static void main(String[] args) {
        Target target=new Adapter();
        target.output5v();
    }
}

源碼使用示例:JDK的IO使用了裝飾模式

InputStreamReader和OutputStreamWriter分別繼承了Read和Writer接口,創建這兩個對象時要傳入InputStream和OnputStream類型的實例對象,將字節流轉換爲字符流。InputStreamReader和OutputStreamWriter就充當了Adapter適配器角色,InputStream和OnputStream類型的實例對象就是Adaptee源角色,而Read和Writer接口就是Target目標接口

public OutputStreamWriter(OutputStream out) {
        super(out);
        try {
//適配過程是將byte轉碼爲char的編碼
            se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    }
//將字節流轉化爲字符流
public static void main(String[] args) throws FileNotFoundException {
        OutputStream outputStream=new FileOutputStream("c:\\xx\\xxx");
        OutputStreamWriter outputStreamWriter=new OutputStreamWriter(outputStream);
}

三、代理模式

爲其他對象提供一個代理以控制對某個對象的訪問。代理類主要負責爲被代理類預處理消息、過濾消息、傳遞消息給委託類,代理類不現實具體服務,而是利用委託類來完成服務,而完成這些流程不會對被代理類有代碼侵入,代理類起到了中介的作用。
其實就是代理類爲被代理類預處理消息、過濾消息並在此之後將消息轉發給被代理類,之後還能進行消息的後置處理。代理類,代理類本身不實現服務,而是通過調用被代理類中的方法來提供服務。

靜態代理

public interface IHello {
void sayHello(String str);
}
public class Hello implements IHello{
  @Override
   public void sayHello(String str) {
         System.out.println("hello "+str);
     }
}
public class ProxyHello implements IHello{    
    private IHello hello;    
    public ProxyHello(IHello hello) {
        super();
        this.hello = hello;
    }
//加強的方法
    @Override
    public void sayHello(String str) {
        Logger.start();
        hello.sayHello(str);
        Logger.end();
    }
}
public class Logger {
     public static void start(){
         System.out.println(new Date()+ " say hello start...");
     }
     public static void end(){
         System.out.println(new Date()+ " say hello end");
     }
 }
 public class Test {
     public static void main(String[] args) {
         IHello hello = new ProxyHello(new Hello());
         hello.sayHello("world");    
     }
 }

JDK動態代理

實例1:簡單實現

代理工廠

public class ProxyFatory {
    //obj是被代理的對象
    public static Object getProxyInstance(Object obj){
        //對人
        MyProxy myProxy=new MyProxy();
        myProxy.bind(obj);
        //返回的是代理人myProxy的invoke方法所返回的對象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),myProxy);
    }
}

代理人必須是InvocationHandler 方法的子類

public class MyProxy implements InvocationHandler {
    private Object obj;

    public void bind(Object obj){
        this.obj=obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        proxy=obj;
        return method.invoke(proxy,args);
    }
}

被代理的對象

public class IPhone implements Apple {
    private int id;
    private String name;
    public IPhone() {
    }
    @Override
    public String getIdAndName(int id, String name) {
        return name+id;
    }
}

測試

public class Test {
    @org.junit.Test
    public void test1(){
        Apple proxyInstance = (Apple) ProxyFatory.getProxyInstance(new IPhone());
        String iPhone11 = proxyInstance.getIdAndName(1, "IPhone1");
        System.out.println(iPhone11);
    }
}

實例2:方法加強

需要被加強的接口類

public interface OrderService {
    int insertData(Integer i);
}

需要被加強的接口實現類

public class OrderServiceImpl implements OrderService {
    @Override
    public int insertData(Integer i) {
        System.out.println("執行插入業務");
        return i;
    }
}

代理類實現:

public class OrderProxy implements InvocationHandler {
    private Object target;
    public OrderProxy(Object target) {
        this.target = target;
    }
    //前置增強
    private void before(Object o){
        if (o instanceof Integer) {
            System.out.println("OrderService的前置增強....");
        }
    }
    //後置增強
    private void after(Object o){
        if (o instanceof Integer) {
            System.out.println("OrderService後置增強......");
        }
    }
    public Object bind(){
        Class aClass = target.getClass();
        return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
    }
//proxy代理類(這個很少用的),method被代理對象需要增強的方法,被代理對象執行方法的參數
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //增強方法的參數,0代表第一個參數
        Object arg = args[0];
        before(arg);
        //需要增強的方法
        Object invoke = method.invoke(target, args);
        after(arg);
        return invoke;
    }
}

測試

 @org.junit.Test
    public void test12() {
        OrderService orderService = (OrderService) new OrderProxy(new OrderServiceImpl()).bind();
        orderService.insertData(1);
    }

CGLIB

jdk的動態代理需要實現接口,CGLIB不需要實現接口
引入依賴

  <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

被代理對象

public class CustomerServiceImpl {
    public int insertData(Integer i) {
        System.out.println("執行插入業務");
        return i;
    }
}

代理類

public class CustomerProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Customer的前置增強....");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("Customer後置增強......");
        return o1;
    }
}

測試

@org.junit.Test
    public void test112() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CustomerServiceImpl.class);
        enhancer.setCallback(new CustomerProxy());
        CustomerServiceImpl customerService = (CustomerServiceImpl) enhancer.create();
        customerService.insertData(1);
    }

面試題:

Cglib和jdk動態代理的區別?
  • 1、Jdk動態代理:利用攔截器(必須實現InvocationHandler)加上反射機制生成一個代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理
  • 2、Cglib動態代理:利用ASM框架,對代理對象類生成的class文件加載進來,通過修改其字節碼生成子類來處理
什麼時候用cglib什麼時候用jdk動態代理?
  • 1、目標對象生成了接口 默認用JDK動態代理
  • 2、如果目標對象使用了接口,可以強制使用cglib
  • 3、如果目標對象沒有實現接口,必須採用cglib庫,Spring會自動在JDK動態代理和cglib之間轉換
JDK動態代理和cglib字節碼生成的區別?
  • 1、JDK動態代理只能對實現了接口的類生成代理,而不能針對類
  • 2、Cglib是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,並覆蓋其中方法的增強,但是因爲採用的是繼承,所以該類或方法最好不要生成final,對於final類或方法,是無法繼承的
Cglib比JDK快?
  • 1、cglib底層是ASM字節碼生成框架,但是字節碼技術生成代理類,在JDL1.6之前比使用java反射的效率要高
  • 2、在jdk6之後逐步對JDK動態代理進行了優化,在調用次數比較少時效率高於cglib代理效率
  • 3、只有在大量調用的時候cglib的效率高,但是在1.8的時候JDK的效率已高於cglib
  • 4、Cglib不能對聲明final的方法進行代理,因爲cglib是動態生成代理對象,final關鍵字修飾的類不可變只能被引用不能被修改
Spring如何選擇是用JDK還是cglib?(Mybatis使用的是jdk的動態代理)
  • 1、當bean實現接口時,會用JDK代理模式
  • 2、當bean沒有實現接口,用cglib實現
  • 3、可以強制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
SpringAOP日誌管理實戰

https://www.cnblogs.com/jianjianyang/p/4910851.html

裝飾器和代理模式的區別

對裝飾器模式來說,裝飾者和被裝飾者都實現一個接口。對代理模式來說,代理類和真實處理的類都實現同一個接口。此外,不論我們使用哪一個模式,都可以很容易地在真實對象的方法前面或者後面加上自定義的方法。

在裝飾模式調用者只想要你把他給你的對象裝飾一下。而代理模式使用的是代理對象在自己的構造方法裏面new的一個被代理的對象,不是調用者傳入的。調用者不知道你找了其他人,他也不關心這些事,只要你把事情做對了即可。

  • 1、裝飾器模式強調的是增強自身,在被裝飾之後你能夠在被增強的類上使用增強後的功能。增強後你還是你,只不過能力更強了而已;代理模式強調要讓別人幫你去做一些本身與你業務沒有太多關係的職責(記錄日誌、設置緩存)。代理模式是爲了實現對象的控制,因爲被代理的對象往往難以直接獲得或者是其內部不想暴露出來。

  • 2、裝飾模式是以對客戶端透明的方式擴展對象的功能,是繼承方案的一個替代方案;代理模式則是給一個對象提供一個代理對象,並由代理對象來控制對原有對象的引用;

  • 3、裝飾模式是爲裝飾的對象增強功能;而代理模式對代理的對象施加控制,但不對對象本身的功能進行增強;

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