代理模式

11次阅读

共计 800 个字符,预计需要花费 2 分钟才能阅读完成。

简介

代理模式提供了一个代理,达到控制访问对象的目的。代理模式分为远程代理,保护代理,虚拟代理,缓冲代理和智能引用代理等。

UML 类图

示例

有句广告词,没有中间商赚差价,这个中间商很多时候就是代理的角色。中间商越多就是层层代理。很多商品都有一级代理商二级代理商等。在这里我们举一个火车票代售点的故事。
代理实现,proxy.h

#ifndef PROXY_H
#define PROXY_H
#include <iostream>
using namespace std;

#define SAFE_DELETE(p) if(p){delete (p); (p) = NULL;}
class CTicketSale
{
public:
    virtual void GetTicket() = 0;};

class CStationSale:public CTicketSale
{
public:
    void GetTicket()
    {cout<<"Get Ticket."<<endl;}
};

class CProxySale:public CTicketSale
{
public:
    CProxySale()
    {m_pTicketSale = new CStationSale;}
    ~CProxySale()
    {SAFE_DELETE(m_pTicketSale);
    }
    void GetTicket()
    {
        cout<<"Enter proxy:"<<endl;
        m_pTicketSale->GetTicket();}
private:
    CTicketSale* m_pTicketSale;
};

#endif

客户端调用,main.cpp

#include "proxy.h"

int main(int argc, char* argv[])
{
    CTicketSale* pProxySale = new CProxySale;
    pProxySale->GetTicket();
    SAFE_DELETE(pProxySale)
    return 0;
}

正文完
 0

代理模式

11次阅读

共计 595 个字符,预计需要花费 2 分钟才能阅读完成。

⭐️ 更多前端技术和知识点,搜索订阅号 JS 菌 订阅

Proxy 方法创建代理模式

使用 Proxy API 可以方便的创建一个 get 和 set 拦截器

let obj = {
  name: "oli",
  canSave: false,
  blackbox: true
};

let proxyObj = new Proxy(obj, {set(obj, prop, val) {if (obj.canSave && prop === "name") {obj.name = val;} else return;
  },
  get(obj, prop) {if (obj.blackbox) {return null} else return obj[prop]
  }
});

Object.defineProperty 方法创建代理模式

在 ES6 之前,通常使用 Object.defineProperty 这个方法:

let obj = {
  _name: "oli", // 实际的 name
  canSave: false,
  blackbox: true
};

let proxyObj = Object.defineProperty(obj, "name", {set(val) {if (obj.canSave) {obj.name = val;} else return;
  },
  get() {if (obj.blackbox) {return null} else return obj._name
  }
});

请关注我的订阅号,不定期推送有关 JS 的技术文章,只谈技术不谈八卦 ????

正文完
 0

代理模式

11次阅读

共计 1442 个字符,预计需要花费 4 分钟才能阅读完成。

代理简单介绍

生活中的代理:黄牛【三个角色。我(真实对象)黄牛(代理对象)火车票(目的对象)】

代理模式出现的原因
  • 真实对象无法直接访问目的对象
  • 真实对象不想直接访问目的对象
  • 真实对象访问目的对象存在困难

解决的问题是:在直接访问对象时带来的问题,原先直接访问的两个类通过代理对象不在直接访问,可以实现解耦。

代理模式对应的 UML 图

图片描述

代理模式的分类

按照代理对象的创建时机划分为两种:
1、在编译前已经创建好对象,编译之后运行前代理类的.Class 文件就已经存在,这种的称为静态代理
2、程序在运行的时候动态的创建代理对象,这种的称为动态代理

静态代理

在代码中手动的创建代理类。模拟的代码如 IDEA 展示

/**
 * 定义一个接口
 * 真实对象要干什么,代理对象通过接口可以知道
 */
public interface IUserDo {void takeTrain(Ticket ticket);
}

/**
 * @author pangjianfei
 * @Date 2019/5/22
 * @desc 真实对象
 */
public class RealUser implements IUserDo {

    public final String NAME = "真实回家人";

    @Override
    public void takeTrain(Ticket ticket) {System.out.println(NAME + ": 抢到票了, 我要回家了,花了"+ ticket.proxyPrice.intValue() + "元,值了!");
    }
}

/**
 * @author pangjianfei
 * @Date 2019/5/22
 * @desc 代理角色,黄牛
 */
public class ProxyUser implements IUserDo{

    public final String NAME = "黄牛";

    /** 代理的是哪一位 */
    RealUser realUser;

    public ProxyUser(RealUser realUser) {this.realUser = realUser;}

    @Override
    public void takeTrain(Ticket ticket) {Ticket realTicket = buyTicket(ticket);
        realUser.takeTrain(realTicket);
        System.out.println(NAME + ": 我挣了" +(realTicket.proxyPrice.intValue()-realTicket.realPrice.intValue()) + "元");
    }

    public Ticket buyTicket(Ticket ticket) {System.out.println(NAME + ": 去买从"+ticket.from+"到"+ticket.to+"的火车票了");
        Ticket backHomeTicket = new Ticket();
        backHomeTicket.setFrom(ticket.from);
        backHomeTicket.setTo(ticket.to);
        backHomeTicket.setTakeOffDate(ticket.takeOffDate);
        backHomeTicket.setRealPrice(new BigDecimal(100));
        backHomeTicket.setProxyPrice(new BigDecimal(120));
        System.out.println(NAME + ": 购买成功");
        return backHomeTicket;
    }
}

正文完
 0

代理模式

11次阅读

共计 7017 个字符,预计需要花费 18 分钟才能阅读完成。

栗子
定义一个游戏者接口
public interface IGamePlayer{
// 登录游戏
public void login(String user, String password);
// 杀怪
public void killBoss();
// 升级
public void upgrade();
}
定义游戏着类,实现游戏者接口
public class GamePlay implements IGamePlayer{
private String name = “”;
// 构造函数注入
public GamePlayer(String _name){
this.name = _name;
}
// 打怪
public void killBoss(){
System.out.println(this.name + “ 在打怪 ”);
}
// 登录
public void login(String user, String password){
System.out.println(“ 登录 ”);
}
// 升级
public void upgrade(){
System.out.println(this.name + “ 升级 ”);
}
}
场景类
public class Client{
public static void main(String[] args){
// 定义一个玩家
IGamePlayer player = new GamePlayer(“ 张三 ”);
// 开始游戏
System.out.println(“ 开始游戏 ”);
// 登录
player.login(“zangshan”, “password”);
// 杀怪
player.killBoss();
// 升级
player.upgrade();
// 记录结束时间
}
}
改进
增加一个代练
public class GamePlayerProxy implements IGamePlayer{}
private IGamePlayer gamePlayer = null;
// 构造函数注入
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
// 代练
public void killBoss(){
this.gamePlayer.killBoss();
}
// 代练登录
public void login(String user, String password){
this.gamePlayer.login(user, password);
}
// 代练升级
public void upgrade(){
this.gamePlayer.upgrade();
}
}
场景类如下
public class Client {
public static void main(String[] args){
// 定义一个玩家
IGamePlayer player = new GamePlayer(“ 张三 ”);
// 定义代练者
IGamePlayer proxy = new GamePlayerProxy(player);
// 开始游戏
proxy.login(“zangshan”, “password”);
proxy.killBoss();
// 升级
proxy.upgrade();
}
}
扩展
代理分为普通代理和透明代理
普通代理
普通代理要求客户端只能访问代理角色,不能访问真实角色。
// 普通代理的游戏者
public class GamePlayer implemnts IGamePlayer{
private String name = “”;
// 构造函数传递姓名
public GamePlayer(IGamePlayer _gamePlayer, String _name)throws Exception{
if(_gamePlayer == null){
throw new Exception(“ 不能创建真实角色 ”);
}else{
this.name = _name;
}
}
// 打怪
public void killBoss(){
System.out.println(this.name + “ 在打怪!”);
}
// 登录
public void login(String user. String password){

}
// 升级
public void upgrade(){

}

}
// 普通代理的代理者
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer = null;
// 构造函数注入
public GamePlayerProxy(String name){
try{
gamePlayer = new GamePlayer(this, name);
}catch(Exception e){

}
}
// 代练
public void killBoss(){
this.gamePlayer.killBoss();
}
// 登录
public void login(String user, String password){
this.gamePlayer.login(user, password);
}
// 升级
public void upgrade(){
this.gamePlayer.upgrade();
}
}
最后场景类
public class Client{
public static void main(String[] args){
// 定义代练者
IGamePlayer proxy = new GamePlayerProxy(“”);
// 开始
proxy.login(“”, “password”);
// 升级
proxy.upgrade();
// 记录结束时间
}
}
强制代理
public interface IGamePlayer{
// 登录
public void login(String user, String password);
// 杀怪
public void killBoss();
// 升级
public void upgrade();
// 找代理
public IGamePlayer getProxy();
}
// 强制代理的真实角色
public class GamePlayer implements IGamePlayer{
private String name = “”;
// 找代理
private IGamePlayer proxy = null;
public GamePlayer(String _name){
this.name = _name;
}
// 找到代理
public IGamePlayer getProxy(){
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
// 打怪
public void killBoss(){
if(this.isProxy()){

}else{

}
}
// 登录
public void login(String user, String password){
if(this.isProxy()){

}else{

}
}
// 升级
public void upgrade(){
if(this.isProxy()){

}else{

}
}
// 是否代理
private boolean isProxy(){
if(this.proxy == null){
return false;
}else{
return true;
}
}

}
// 强制代理代理类
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer = null;
// 构造函数传递用户名
public GamePlayerProxy(IGamePlayer _gaemPlayer){
this.gamePlayer = _gamePlayer;
}
// 代练
public void killBoss(){
this.gaemPlayer.killBoss();
}
// 登录
public void login(String user, String password){
this.gamePlayer.login(user, password);
}
// 升级
public void upgrade(){
this.gamePlayer.upgrade();
}
// 如果代理没有,为自身
public IGamePlayer getProxy(){
return this;
}
}
最后书写场景类
public class Client{
public static void main(String[] args){
// 定义游戏角色
IGamePlayer player = new GamePlayer(“ 张三 ”);
// 开始游戏
player.login(“zangshan”, “password”);
// 杀怪
player.killBoss();
// 升级
player.upgrade();
}
}
强制代理场景类
public class Client{
public static void main(String[] args){
// 定义游戏角色
IGamePlayer player = new GamePlayer(“ 张三 ”);
// 获得指定代理
IGamePlayer proxy = player.getProxy();
// 开始打游戏
proxy.login(“zangshan”, “password”);
// 开始杀怪
proxy.killBoss();
// 升级
proxy.upgrade();
}
}
代理需要拥有个性
即一个类可以实现多个接口,完成不同任务的整合。
// 定义代理类的接口
public interface IProxy{
// 计算费用
public void count();
}
// 定义代理类
public class GamePlayerProxy implements IGamePlayer, IProxy{
private IGamePlayer gamePlayer = null;
// 通过构造函数传递对谁代练
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
// 代练
public voidkillBoss(){
this.gamePlayer.killBoss();
}
// 代练登录
public void login(String user, String password){
this.gamePlayer.login(user, password);
}
// 代练升级
public void upgrade(){
this.gamePlayer.upgrade();
this.count();
}
// 计算费用
public void count(){

}
}
在上方中先实现了 IProxy 接口,然后再调用该接口中的方法,完成结算.
动态代理
动态代理在实现阶段不用关系代理谁。在运行阶段指定代理那个对象。即,在实现的时候不用关心代理谁,只需要在运行的时候指定代理那个对象
// 动态代理类
public class GamePlayIH implements invocationHandler{
// 被代理着
Class cls = null;
// 被代理的实例
Object obj = null;
// 我要代理谁
public GamePlayerIH(Object _obj){
this.obj = _obj;
}
// 调用被代理的方法
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
Object result = method.invoke(this.obj, args);
return result;
}
}
// 动态代理场景类
public class Client{
public static void main(String[] args)throws Throwable{
// 定义一个痴迷的玩家
IGamePlayer player = new GamePlayer(“ 张三 ”);
// 定义一个 handler
invocationHandler handler = new GamePlayIH(player);
// 开始游戏,记录时间戳
System.out.println(“ 开始时间 ”);
// 获得类的父类加载器
ClassLoader cl = player.getClass().getClassLoader();
// 动态产生代理者
IGamePlayer proxy = (IGamePlayer)proxy.newProxyinstance(cl, new Class[]{IGamePlayer.class}, handler);
// 登录
proxy.login(“zhangsan”, “password”);
// 开始
proxy.killBoss();
// 升级
proxy.upgrade();
// 记录结束时间
}
}
ps 动态代理直接在需要代理的时候直接动态生成代理
此时,如果需要登录的时候发送消息,此时修改如下
public class GamePlayIH implements invocationHandler {
// 被代理着
Class cls = null;
// 被代理的实例
Object obj = null;
// 我要代理谁
public GamePlayIH(Object _obj){
this.obj = _obj;
}
// 调用代理的方法
public Object invoke(Object proxy, Method method, Object[] args){
Objetc result = method.invoke(this.obj, args);
// 如股票是登录方法,发送消息
if(method.getName().equalslgnoreCase(“login”)){

}
return result;
}
}
这样就完成的消息的发送
最后
// 抽象主题
public interface Subject{
// 业务操作
public void doSomething(String str);
}
// 真实主题
public class RealSubject implements Subject{
// 业务操作
public void doSomething(String str){
/// 业务操作
}
}
下面是动态代理类该动态代理类通过构造函数将将对象传入。由于实现了 invoke 方法,此时会被调度到该方法
public class MyinvocationHandler implements invocationHandler{
// 被代理的对象
private Object target = null;
// 构造函数传递
public MyInvocationHandler(Object _obj){
this.target = _obj;
}
// 代理方法
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
// 执行被代理的方法
return method.invoke(this.target, args);
}
}
下面是动态代理类
public class DynamiProxy{
public static T newProxyinstance(ClassLoader loader, Class<?>[] interfaces, invocationHandler h){
// 寻找 JoinPoint 连接点
// 寻找连接点,即方法执行前后的连接点
if(true){
// 执行一个前置通知
new BeforeAdvice().exec();
}
// 执行切面
// 并返回结果
return (T)Proxy.newProxyinstance(loader.interfaces, h);
}
}
下面实现通知
public interface IAdvice{
public void exec();
}
public class BeforeAdvice implements IAdvice{
public void exec(){
System.out.println(“ 我是通知 ”);
}
}
最后书写场景类
public class Client{
public static void main(String[] args){
// 定义一个主题
Subject subject = new RealSUbject();
// 定义一个前置通知
invocationHandler handler = new MyinvocationHandler(subject);
// 定义主题代理
Subject proxy = DynamicProxy.newProxyinstance(subject.getClass().getClassLoader(), subject.getClass().getinterfaces(),handler);
// 最后执行这个代理的接口
proxy.doSomething(“Finish”);
}
}
总结 程序的执行的过程,在 DynamicProxy 类中,使用 newProxyinstance 方法重写生成一个对象,这个对象是其代理对象,一个类的动态代理是这样的过程先场景类需要调用 doSomething 方法的时候,进入动态代理类,当动态代理类调用 Proxy.newProxyinstance 的时候,会调度到代理方法,由代理方法再执行 method.invoke(this.target, args); 完成对代理的调用。
https://www.iming.info/dai-li…

正文完
 0

代理模式

11次阅读

共计 4189 个字符,预计需要花费 11 分钟才能阅读完成。

代理模式
定义
为其他对象提供一种代理,以控制对这个对象的访问。(代理对象在客户端和目标对象之间起到中介的作用)
优点

代码模式能将代理对象与真实被调用的目标对象分离。
一定程度上降低了系统的耦合度,扩展性好。
保护目标对象。(客户端直接交互的是代理类而不是目标对象,这样就保护了目标对象)
增强目标对象

缺点

在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
增加了系统的复杂度。

分类
静态代理
在代码中显式的代理目标。
动态代理
jdk 中的动态代理只能对实现了接口的类进行动态代理。并不能针对一个具体的类进行动态代理。无法代理可以代理接口,在代理类的时候只有在调用到代理类是才由 jvm 动态的创建,jvm 根据传进来的对象以及代理方法名动态的创建了代理类的 class 文件,这个 class 文件被字节码引擎执行,然后通过该代理类的对象进行方法的调用。(比 CGLib 快大约百分之 10)
CGLib 代理
主要针对 jdk 无法代理具体的类的问题。实现原理是生成一个被代理类的子类,通过覆盖父类的方法来实现动态代理。也就是用过继承然后重写,所以被代理的不能是一个 final 的类或者是代理方法不能是一个 final 方法。
spring 中代理选择

当 bean 有实现接口时,spring 就会使用 jdk 的动态代理。
当 bean 没有实现接口时,spring 使用 CGLib
可以强制使用 CGLib (具体配置自行谷歌吧)

下面我们实现一下静态。我们假设一个应用场景就是,要保存一个订单,订单就包括订单实体类,service。dao 等,我们要用代理的方式来实现数据库分库,不同的订单要放在不同的数据库中。下面开始看代码。
订单实体类。
public class Order {
private Object orderInfo;
private Integer userId;

public Object getOrderInfo() {
return orderInfo;
}

public void setOrderInfo(Object orderInfo) {
this.orderInfo = orderInfo;
}

public Integer getUserId() {
return userId;
}

public void setUserId(Integer userId) {
this.userId = userId;
}
}

订单操作的 dao 接口
public interface IOrderDao {
int insert(Order order);

}
dao 的实现类,我们这里只是模拟一下数据库操作。
public class OrderDaoImpl implements IOrderDao {
@Override
public int insert(Order order) {
System.out.println(“Dao 层添加 Order 成功 ”);
return 1;
}
}

service 接口
public interface IOrderService {
int saveOrder(Order order);
}
service 实现类,我们没有集成 spring 模拟操作
public class OrderServiceImpl implements IOrderService {
private IOrderDao iOrderDao;

@Override
public int saveOrder(Order order) {
//Spring 会自己注入,我们课程中就直接 new 了
iOrderDao = new OrderDaoImpl();
System.out.println(“Service 层调用 Dao 层添加 Order”);
return iOrderDao.insert(order);
}

}
要分库我们来用 ThreadLocal 实现一个上下文对象,模拟暂放在哪个数据的操作。
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<String>();

public static void setDBType(String dbType){
CONTEXT_HOLDER.set(dbType);
}
public static String getDBType(){
return (String)CONTEXT_HOLDER.get();
}
public static void clearDBType(){
CONTEXT_HOLDER.remove();
}

}

动态的使用数据库的实现。(模拟操作)
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDBType();
}
}
进入重点,代理类的实现。这里的核心就是在执行代理方式之前加入我们的增强操作。
public class OrderServiceStaticProxy {
private IOrderService iOrderService;

public int saveOrder(Order order){
beforeMethod(order);
iOrderService = new OrderServiceImpl();
int result = iOrderService.saveOrder(order);
afterMethod();
return result;
}

private void beforeMethod(Order order){
int userId = order.getUserId();
int dbRouter = userId % 2;
System.out.println(“ 静态代理分配到【db”+dbRouter+”】处理数据 ”);

//todo 设置 dataSource;
DataSourceContextHolder.setDBType(“db”+dbRouter);
System.out.println(“ 静态代理 before code”);
}
private void afterMethod(){
System.out.println(“ 静态代理 after code”);
}
}
最后就是测试类
public class StaticProxyTest {
public static void main(String[] args) {
Order order = new Order();
order.setUserId(2);
OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy();
orderServiceStaticProxy.saveOrder(order);
}
}

输出结果
静态代理分配到【db0】处理数据
静态代理 before code
Service 层调用 Dao 层添加 Order
Dao 层添加 Order 成功
静态代理 after code
静态代理每次代理都要写一个代理类,代理方法,容易产生类爆炸的情况。下面我们实现一下动态代理。实现动态的时我们需要实现 InvocationHandler 接口,实现 invoke 方法这个方法需要 Object proxy, Method method, Object[] objs args 三个参数,一个参数是动态生成的类的对象,一般我们在 invoke 方法中很少用到。method 顾名思义就是需要代理的方法,Object 数据就是参数集合。在使用的时候先要 Proxy.newProxyInstance 创建一个被代理的对象,这个方法有 ClassLoader loader, Class<?>[] interfaces, InvocationHandler h 三个参数,ClassLoader 对象我们通过 getClass 方法的 getClassLoad 方法很容易拿到,Class<?>[] interfaces 就是接口集合了,通过 getInterfaces 也可以很容易拿到,最后一个就是动态代理类的对象了。下面我们看一下代码。
public class OrderServiceDynamicProxy implements InvocationHandler {

private Object target;

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

public Object bind(){
Class cls = target.getClass();
return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object argObject = args[0];
beforeMethod(argObject);
Object object = method.invoke(target,args);
afterMethod();
return object;
}

private void beforeMethod(Object obj){
int userId = 0;
System.out.println(“ 动态代理 before code”);
if(obj instanceof Order){
Order order = (Order)obj;
userId = order.getUserId();
}
int dbRouter = userId % 2;
System.out.println(“ 动态代理分配到【db”+dbRouter+”】处理数据 ”);

//todo 设置 dataSource;
DataSourceContextHolder.setDBType(“db”+String.valueOf(dbRouter));
}

private void afterMethod(){
System.out.println(“ 动态代理 after code”);
}
}

代理模式就这些内容啦。

正文完
 0