1. 程式人生 > >裝飾者模式與動態代理

裝飾者模式與動態代理

類的方法的增強的方式有很多,最初使用的繼承,但繼承的缺點是後期專案會產生很多的類,增加了專案的複雜度,於是,人們提出了組合,這點在Go語言設計最突出,Go語言甚至去掉了類的繼承,在java的設計模式的,類的增強的設計模式常見有裝飾者模式和動態代理,本文闡述裝飾者模式和動態代理模式的設計技巧.

裝飾者模式

裝飾者模式可以實現對一個類的方法的前後增強,為了更好的闡述該模式的原理,我從一個Service層的新增使用者的方法addUser()展開

首先寫一個介面UserService,中有一個addUser()方法

public interface UserService {

   void
addUser(); }

實現UserService

public class UserServiceImp implements UserService {

    @Override
    public void addUser() {
        System.out.println("add user ........");
    }
}

寫一個裝飾類,前置增強addUser() 方法,指定其構造方法,使得在建立類的時候,傳入UserService,並儲存在屬性中,在重寫的方法中呼叫


public  class UserServiceBeforeDecorate
implements UserService {
private UserService userService; public UserServiceBeforeDecorate(UserService userService){ this.userService=userService; } @Override public void addUser() { //前置增強 System.out.println("before decorate.........."); userService.addUser(); } }

寫一個UserService的後置增強方法,與上面類似

public class UserServiceAfterDecorate implements UserService {

    private UserService userService;

    public UserServiceAfterDecorate(UserService userService){
        this.userService=userService;
    }

    @Override
    public void addUser() {

        userService.addUser();
        //後置增強
        System.out.println("after decorate ..........");

    }
}

寫一個測試類

    @Test
    public void test3(){
        UserServiceBeforeDecorate decorate = new UserServiceBeforeDecorate(new UserServiceAfterDecorate(new UserServiceImp()));
        decorate.addUser();
    }

這種寫法是不是很熟悉,在java的IO中,就是這樣設計的

        // IO的使用
        try {
            BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(new File("lhc.txt")));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

但也可以看到,在java的IO的API中,出現了眾多的小類,增加了IO的複雜度,這也是裝飾者模式中不可避免的缺點

動態代理

增強類的方法的另一種方式是使用動態代理,Spring框架的AOP也是基於此,我同樣從一個Service層的新增使用者的方法addUser()展開,看如何前後置增強
使用動態代理,需要寫一個類實現InvocationHandler ,同樣,指定其構造方法,在建立類的時候注入UserService ,並儲存到屬性中,值得關注的是重寫的invoke的方法引數含義
其中method指的是被代理的物件的方法,args指的是被代理的物件的方法的引數,故使用反射執行該方法,並新增前後置增強


public class UserServiceInvocationHandler implements InvocationHandler {

    private UserService userService;

    public UserServiceInvocationHandler(UserService userService){
        this.userService=userService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //對UserService的每一方法的前後置增強

        System.out.println("addUser before.........");
        Object invoke = method.invoke(userService, args);
        System.out.println("addUser after.........");
        return invoke;
    }
}

寫一個測試,使用Prox的newProxyInstance方法,建立代理物件,需要傳入三個引數
第一個:實現類的類載入器
第二個: 實現類所實現的所有介面
第三個就是上面寫的實現InvocationHandler 的類

    @Test
    public void test6(){
        UserService userServiceImp = new UserServiceImp();

        UserService userService = (UserService)Proxy.newProxyInstance(userServiceImp.getClass().getClassLoader(), userServiceImp.getClass().getInterfaces(), new UserServiceInvocationHandler(userServiceImp));
        userService.addUser();
    }

呼叫器addUser方法,可看到控制檯輸出方法的前後置增強

addUser before.........
add user ........
addUser after.........