《設(shè)計模式之禪》學(xué)習(xí)及源碼示例---裝飾模式\適配器模式\代理模式

一、裝飾模式

定義:

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

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

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

ConcreteComponent:具體被裝飾者

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

Decorator:裝飾抽象角色

這個接口是一個抽象類實現(xiàn)或繼承Component,作為具體裝飾角色和被裝飾角色的中轉(zhuǎn)對象,非常重要

ConcreteDecorator:具體的裝飾角色

ConcreteDecoratorA\ConcreteDecoratorB\ConcreteDecoratorC.....作為具體的裝飾實現(xiàn),繼承Decorator現(xiàn)象具體對ConcreteComponent的裝飾

需求:現(xiàn)在我們要給一個毛坯房裝修

代碼演示:

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;
    }

    //給具體裝飾者執(zhí)行,并在其之上進行添加前后置功能
    @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,F(xiàn)ilterInputStream是Decorator,BufferedInputStream通過繼承FilterInputStream,給FileInputStream裝飾拓展了功能

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

通過傳入?yún)?shù)實現(xiàn)相關(guān)的裝飾

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目標(biāo)接口

該接口就是我們的期待接口,其他接口向這個接口轉(zhuǎn)換

Adaptee源角色

希望被轉(zhuǎn)換的接口

Adapter適配器角色

適配器角色職責(zé)是把源角色轉(zhuǎn)為目標(biāo)接口

需求:現(xiàn)在我們需要將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("轉(zhuǎn)化后變成"+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接口,創(chuàng)建這兩個對象時要傳入InputStream和OnputStream類型的實例對象,將字節(jié)流轉(zhuǎn)換為字符流。InputStreamReader和OutputStreamWriter就充當(dāng)了Adapter適配器角色,InputStream和OnputStream類型的實例對象就是Adaptee源角色,而Read和Writer接口就是Target目標(biāo)接口

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

三、代理模式

為其他對象提供一個代理以控制對某個對象的訪問。代理類主要負責(zé)為被代理類預(yù)處理消息、過濾消息、傳遞消息給委托類,代理類不現(xiàn)實具體服務(wù),而是利用委托類來完成服務(wù),而完成這些流程不會對被代理類有代碼侵入,代理類起到了中介的作用。
其實就是代理類為被代理類預(yù)處理消息、過濾消息并在此之后將消息轉(zhuǎn)發(fā)給被代理類,之后還能進行消息的后置處理。代理類,代理類本身不實現(xiàn)服務(wù),而是通過調(diào)用被代理類中的方法來提供服務(wù)。

靜態(tài)代理

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動態(tài)代理

實例1:簡單實現(xiàn)

代理工廠

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);
}

需要被加強的接口實現(xiàn)類

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

代理類實現(xiàn):

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被代理對象需要增強的方法,被代理對象執(zhí)行方法的參數(shù)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //增強方法的參數(shù),0代表第一個參數(shù)
        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的動態(tài)代理需要實現(xiàn)接口,CGLIB不需要實現(xiàn)接口
引入依賴

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

被代理對象

public class CustomerServiceImpl {
    public int insertData(Integer i) {
        System.out.println("執(zhí)行插入業(yè)務(wù)");
        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動態(tài)代理的區(qū)別?
  • 1、Jdk動態(tài)代理:利用攔截器(必須實現(xiàn)InvocationHandler)加上反射機制生成一個代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理
  • 2、Cglib動態(tài)代理:利用ASM框架,對代理對象類生成的class文件加載進來,通過修改其字節(jié)碼生成子類來處理
什么時候用cglib什么時候用jdk動態(tài)代理?
  • 1、目標(biāo)對象生成了接口 默認用JDK動態(tài)代理
  • 2、如果目標(biāo)對象使用了接口,可以強制使用cglib
  • 3、如果目標(biāo)對象沒有實現(xiàn)接口,必須采用cglib庫,Spring會自動在JDK動態(tài)代理和cglib之間轉(zhuǎn)換
JDK動態(tài)代理和cglib字節(jié)碼生成的區(qū)別?
  • 1、JDK動態(tài)代理只能對實現(xiàn)了接口的類生成代理,而不能針對類
  • 2、Cglib是針對類實現(xiàn)代理,主要是對指定的類生成一個子類,覆蓋其中的方法,并覆蓋其中方法的增強,但是因為采用的是繼承,所以該類或方法最好不要生成final,對于final類或方法,是無法繼承的
Cglib比JDK快?
  • 1、cglib底層是ASM字節(jié)碼生成框架,但是字節(jié)碼技術(shù)生成代理類,在JDL1.6之前比使用java反射的效率要高
  • 2、在jdk6之后逐步對JDK動態(tài)代理進行了優(yōu)化,在調(diào)用次數(shù)比較少時效率高于cglib代理效率
  • 3、只有在大量調(diào)用的時候cglib的效率高,但是在1.8的時候JDK的效率已高于cglib
  • 4、Cglib不能對聲明final的方法進行代理,因為cglib是動態(tài)生成代理對象,final關(guān)鍵字修飾的類不可變只能被引用不能被修改
Spring如何選擇是用JDK還是cglib?(Mybatis使用的是jdk的動態(tài)代理)
  • 1、當(dāng)bean實現(xiàn)接口時,會用JDK代理模式
  • 2、當(dāng)bean沒有實現(xiàn)接口,用cglib實現(xiàn)
  • 3、可以強制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
SpringAOP日志管理實戰(zhàn)

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

裝飾器和代理模式的區(qū)別

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

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

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

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容