设计模式-代理和观察者详解

导读:本篇文章讲解 设计模式-代理和观察者详解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1 代理模式(Proxy)

1.1 概念:

  为其他对象提供一种代理以控制对这个对象的访问(对其他对象功能的增强)。在某些情况下,一个对象不适合或者不能直接引用另一个对象(委托类),而代理对象(代理类)可以在客户端和目标对象之间起到中介的作用。

1.2 特点:

1) 其特征是代理类与委托类有同样的接口(父类),代理类主要负责为委托类预处理信息(前置通知)、过滤信息(前置通知)、把消息转发给委托类(环绕通知),以及事后处理信息(后置,最终通知)等。
2) 代理类对象本身并不真正实现服务(真正做的业务,委托类需要干什么事),而是通过调用委托类的对象的相关方法,来提供特定的服务(对原服务的增强)。
现实中的实例: 1,明星(委托类),经纪人(代理类) 明星演出,演出是真正的业务, 围绕着演出(执行真正业务),经纪人要做一堆的事情,演出之前,预约,谈演出费,收预付款,确定演出时间,演出服装,演出具体内容,根据上面这些东西,再预定机票,酒店,准备道具,明星去演出(真正业务)经纪人要陪伴,防止突发事件,演出后收尾款,交税等相关事宜。。。。
2,普通客户(委托类)和银行职员(代理类) 超过6W取款,事先预约 ,事后干什么 3,房东/客户 (委托类) 房屋中介(代理类)
等等。。。

1.3 实现:

1)静态代理:

银行雇员代理客户执行相关银行服务

 package com.aaa.dp.proxy.static1;

/**
 * @ fileName:Account
 * @ description: 委托类和代理类拥有的共同接口
 * @ author:zhz
 * @ createTime:2021/12/3 9:56
 * @ version:1.0.0
 */
public interface Account {

    /**
     * 查询余额
     */
    void  query();

    /**
     * 存取钱
     * @param money
     */
    void  updateMoney(double money);
}
package com.aaa.dp.proxy.static1;
/**
 * @ fileName:Client
 * @ description: 委托类(银行客户)
 * @ author:zhz
 * @ createTime:2021/12/3 9:58
 * @ version:1.0.0
 */
public class Client implements Account {
    @Override
    public void query() {
        System.out.println("。。。。。。。。。。XXX查询了余额......顿时不热了.....");
    }
    @Override
    public void updateMoney(double money) {
        System.out.println("。。。。。。。。。。XXX存取了"+money+"钱");
    }
}
package com.aaa.dp.proxy.static1;
/**
 * @ fileName:Employee
 * @ description:代理类(银行雇员)
 * @ author:zhz
 * @ createTime:2021/12/3 9:59
 * @ version:1.0.0
 */
public class Employee implements Account{
    //委托类
    private  Client client;
    /**
     * 通过构造 给委托类赋值 (也可以通过set方法)
     * @param client
     */
    public Employee(Client client) {
        this.client = client;
    }
    @Override
    public void query() {
        System.out.println("查询余额前做什么准备。。。。。。");
        //委托类执行真正的业务
        client.query();
        System.out.println("查询余额后做什么工作。。。。。。");
    }
    @Override
    public void updateMoney(double money) {
        System.out.println("存取前做什么准备。。。。。。");
        //委托类执行真正的业务
        client.updateMoney(money);
        System.out.println("存取后做什么工作。。。。。。");
    }
}
package com.aaa.dp.proxy.static1;
/**
 * @ fileName:Test
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 10:05
 * @ version:1.0.0
 */
public class Test {
    public static void main(String[] args) {
        Client client =new Client();
        Employee employee =new Employee(client);
        employee.query();
        employee.updateMoney(100000);
    }
}

2)JDK动态代理:

java.lang.reflect包下提供了一个Proxy类和一个Invocationhandler接口,用来完成动态代理。

Invocationhandler接口
当我们通过代理对象调用一个方法时,这个方法的调用就会被转发为由Invocationhandler这个接口的invoke方法进行调用。
Object invoke(Object proxy,Method method ,Object[] args)
参数:Object proxy:指被代理的对象
Method method: 要调用的方法
Object[] args: 方法调用时所需的参数

Proxy类
该类提供了一个创建动态代理对象的方法。
static Object newProxyInstance(ClassLoader loader ,Class<?>[] interfaces,InvocationHandler h);
参数:
ClassLoader loader:定义代理类的类加载器。
Class<?>[] interfaces: 代理类要实现的接口列表
InvocationHandler h: 指派方法调用的调用处理程序 表示代理对象要关联的InvocationHandler对象。

package com.aaa.dp.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * @ fileName:JDKProxy
 * @ description:jdk代理类  可以代理任意委托类
 * @ author:zhz
 * @ createTime:2021/12/3 10:42
 * @ version:1.0.0
 */
public class JDKProxy implements InvocationHandler {
    //委托类
    private  Object  obj;
    /**
     * 给委托类赋值,返回代理类对象
     * @param obj
     * @return
     */
    public  Object bindObj(Object obj){
        this.obj=obj;
        //通过反射获取委托类的所有接口
        Class<?>[] interfaces = this.obj.getClass().getInterfaces();
        //参数1  类加载器  参数2
        return Proxy.newProxyInstance(
                JDKProxy.class.getClassLoader(),
                interfaces,
                this);
    }
    /**
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        String methodName = method.getName();
        System.out.println("方法名称为:"+methodName);
       /* if("query".equals(methodName)) {
            System.out.println("查询余额前做什么准备。。。。。。");
            result = method.invoke(this.obj, args);
            System.out.println("查询余额后做什么工作。。。。。。");
        }else if("updateMoney".equals(methodName)){
            System.out.println("存取前做什么准备。。。。。。");
            result = method.invoke(this.obj, args);
            System.out.println("存取后做什么工作。。。。。。");
        }*/
        System.out.println("事前做什么准备。。。。。。");
        result = method.invoke(this.obj, args);
        System.out.println("事后做什么工作。。。。。。");
        return result;
    }
}

package com.aaa.dp.proxy.jdk;
import java.util.List;
/**
 * @ fileName:DeptService
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:03
 * @ version:1.0.0
 */
public interface DeptService {
    /**
     * 修改方法
     * @param o
     * @return
     */
    int update(Object o);
    /**
     * 添加方法
     * @param o
     * @return
     */
    int add(Object o);
    /**
     * 删除方法
     * @param id
     * @return
     */
    int deleteById(int  id);
    /**
     * 分页带参查询
     * @param str
     * @return
     */
    List<Object> queryAll(String ...str);
}
package com.aaa.dp.proxy.jdk;
import java.util.List;
/**
 * @ fileName:DeptServiceImpl
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:06
 * @ version:1.0.0
 */
public class DeptServiceImpl implements DeptService {
    @Override
    public int update(Object o) {
        System.out.println("................模拟更新。。。");
        return 0;
    }
    @Override
    public int add(Object o) {
        System.out.println("................模拟添加。。。");
        return 0;
    }
    @Override
    public int deleteById(int id) {
        System.out.println("................模拟删除。。。");
        return 0;
    }
    @Override
    public List<Object> queryAll(String... str) {
        System.out.println("................模拟查询。。。");
        return null;
    }
}

测试:



package com.aaa.dp.proxy.jdk;
import com.aaa.dp.proxy.static1.Account;
/**
 * @ fileName:TestA
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:07
 * @ version:1.0.0
 */
public class TestA {
    public static void main(String[] args) {
        //委托类
        DeptService deptService =new DeptServiceImpl();
        //实例化代理类
        JDKProxy jdkProxy = new JDKProxy();
        //返回当前委托类接口的子类 (代理类)  多态
        DeptService deptServiceResult = (DeptService)jdkProxy.bindObj(deptService);
        deptServiceResult.add("1");
        deptServiceResult.update("1");
        deptServiceResult.deleteById(1);
        deptServiceResult.queryAll();
    }
}

3)CGLIB(Code Generation Library)动态代理:

        maven依赖:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>
主要应用于没有接口的动态代理生成(主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。), spring AOP框架,Hibernate都使用了CGLLIB。

net.sf.cglib.proxy.Enhancer类:允许为非接口类型创建一个Java代理。Enhancer动态创建了给定类型的子类,并拦截了所有的方法。
(setSuperclass,setCallback,create)

net.sf.cglib.proxy.Callback接口:在CGLIB包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。

举例说明回调:
net.sf.cglib.proxy.MethodInterceptor接口:是最通用的回调(callback)类型,它经常被AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法。

public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) 

proxy:代理的对象;method:委托类执行的方法;params:方法中的参数; methodProxy:代理的方法

由于性能的原因,对原始方法的调用我们使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用java.lang.reflect.Method对象

实现代码:

package com.aaa.dp.proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
 * @ fileName:CGLibProxy
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:21
 * @ version:1.0.0
 */
public class CGLibProxy implements MethodInterceptor {
    //委托类
    private Object obj;
    /**
     * 给委托类赋值,并生成委托类的代理类
     * @param obj
     * @return
     */
    public Object bindObj(Object obj){
        this.obj =obj;
        Enhancer enhancer =new Enhancer();
        //设置生成代理类的父类
        enhancer.setSuperclass(this.obj.getClass());
        //生成委托类的子类后,用Callback调用子类的所有方法
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        System.out.println("事前做什么准备。。。。。。");
        result = methodProxy.invoke(this.obj,objects);
        System.out.println("事后做什么工作。。。。。。");
        return result;
    }
}


package com.aaa.dp.proxy.cglib;
import com.aaa.dp.proxy.jdk.DeptService;
import java.util.List;
/**
 * @ fileName:DeptServiceImpl
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:06
 * @ version:1.0.0
 */
public class DeptServiceImpl implements DeptService {
    public final int update(Object o) {
        System.out.println("................模拟更新。。。");
        return 0;
    }
    public int add(Object o) {
        System.out.println("................模拟添加。。。");
        return 0;
    }
    public int deleteById(int id) {
        System.out.println("................模拟删除。。。");
        return 0;
    }
    public List<Object> queryAll(String... str) {
        System.out.println("................模拟查询。。。");
        return null;
    }
}
package com.aaa.dp.proxy.cglib;
/**
 * @ fileName:Test
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:28
 * @ version:1.0.0
 */
public class Test {
    public static void main(String[] args) {
        DeptServiceImpl deptService =new DeptServiceImpl();
        CGLibProxy cgLibProxy =new CGLibProxy();
        DeptServiceImpl deptServiceResult = (DeptServiceImpl)cgLibProxy.bindObj(deptService);
        deptServiceResult.add("1");
        deptServiceResult.update("1");
        deptServiceResult.deleteById(1);
        deptServiceResult.queryAll();
    }
}

如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)。
CGLib动态代理创建代理实例速度慢,但是运行速度快;JDK动态代理创建实例速度快,但是运行速度慢。如果实例是单例的,推荐使用CGLib方式动态代理,反之则使用JDK方式进行动态代理。Spring的实例默认是单例,所以这时候使用CGLib性能高。

2 观察者模式(Observer Pattern)

2.1 定义

   观察者模式 是软件设计模式的一种。 它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。在观察者模式中,监视另一个对象状态的对象称为Observer,正在被监视的对象称为Subject。

2.2 现实例子

  例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。

2.3 角色

1)、抽象主题(Subject):
它把所有观察者对象的引用保存到一个集合里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
2)、具体主题(Concrete Subject):
将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
3)、抽象观察者(Observer):
为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
4)、具体观察者(Concrete Observer):
实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

2.4 实现

被观察者接口:

package com.aaa.dp.observer;
/**
 * @ fileName:Subject
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:43
 * @ version:1.0.0
 */
public interface Subject {
    /**
     * 注册观察者
     */
    void registerObserver(Observer observer);
    /**
     * 移除观察者
     * @param observer
     */
    void removeObserver(Observer observer);
    /**
     * 通知所有观察者
     * @param message
     */
    void  notifyObserver(String message);
}

被观察者实现:

 
package com.aaa.dp.observer;
import java.util.ArrayList;
import java.util.List;
/**
 * @ fileName:ConcreteSubject
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:45
 * @ version:1.0.0
 */
public class ConcreteSubject implements Subject {
    //所有观察者集合
    private List<Observer> observerList = new ArrayList<>();
    @Override
    public void registerObserver(Observer observer) {
        observerList.add(observer);
    }
    @Override
    public void removeObserver(Observer observer) {
        observerList.remove(observer);
    }
    @Override
    public void notifyObserver(String message) {
        if(observerList!=null&&observerList.size()>0){
            for (Observer observer : observerList) {
                observer.update(message);
            }
        }
    }
}

观察者接口:

package com.aaa.dp.observer;
/**
 * @ fileName:Observer
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:44
 * @ version:1.0.0
 */
public interface Observer {
    /**
     * 根据主题变化,观察者也发生变化
     * @param message
     */
    void update(String message);
}

观察者实现:

package com.aaa.dp.observer;
/**
 * @ fileName:ConcreteObserver
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:49
 * @ version:1.0.0
 */
public class ConcreteObserver implements Observer {
    //观察者名称
    private String obName;
    public ConcreteObserver(String obName) {
        this.obName = obName;
    }
    @Override
    public void update(String message) {
        System.out.println("观察者"+obName+"根据"+message+"变化,自己做了什么什么调整。。。。");
    }
}

测试:

package com.aaa.dp.observer;
/**
 * @ fileName:Test
 * @ description:
 * @ author:zhz
 * @ createTime:2021/12/3 11:50
 * @ version:1.0.0
 */
public class Test {
    public static void main(String[] args) {
        //实例化OB
        Observer observer1 =new ConcreteObserver("ob1");
        Observer observer2 =new ConcreteObserver("ob2");
        Observer observer3 =new ConcreteObserver("ob3");
        Observer observer4 =new ConcreteObserver("ob4");
        //实例化Subject
        Subject subject =new ConcreteSubject();
        subject.registerObserver(observer1);
        subject.registerObserver(observer2);
        subject.registerObserver(observer3);
        subject.registerObserver(observer4);
        //主题变化
        subject.notifyObserver("变黄了");
        System.out.println("----------------------------------");
        //移除OB
        subject.removeObserver(observer1);
        subject.notifyObserver("变红了");
    }
}

2.5 好处

   观察者模式将观察者和主题(被观察者)彻底解耦,主题只知道观察者实现了某一接口(也就是Observer接口)。并不需要观察者的具体类是谁、做了些什么或者其他任何细节。任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现了Observer接口的对象列表。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/75496.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!