中介者设计模式详解C/Java/JS/Go/Python/TS不同语言实现

Posted 刀法如飞-专注算法与设计模式

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了中介者设计模式详解C/Java/JS/Go/Python/TS不同语言实现相关的知识,希望对你有一定的参考价值。

* 中介者模式是一种行为型设计模式,它可以用来减少类之间的直接依赖关系, * 将对象之间的通信封装到一个中介者对象中,从而使得各个对象之间的关系更加松散。 * 在中介者模式中,对象之间不再直接相互交互,而是通过中介者来中转消息。

简介

中介者模式(Mediator Pattern)是一种行为型模式。它限制对象之间的直接交互,它用一个中介对象来封装一系列的动作,以让对象之间进行交流。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

当一些对象和其他对象紧密耦合以致难以对其进行修改时,或当组件因过于依赖其他组件而无法在不同应用中复用时,可使用中介者模式。

 

作用

  1. 用来降低多个对象和类之间的通信复杂性。
  2. 对象之间不再显式地相互引用,但可以独立地改变它们之间的交互,从而保持松耦合。

 

实现步骤

  1. 定义一个工具接口,供不同工具类来实现。建立多个具体工具类实现基础工具接口。
  2. 定义一个基础抽象类,里面聚合了工具接口,相当于桥接。
  3. 再重新定义一个新抽象类,继承自基础抽象类,并补充一些方法。
  4. 建立具体对象类继承新抽象类,调用桥接接口里的方法来实现功能。

 

UML

 

 

Java代码

 

中介者抽象接口

 

// RoomMediator.java 定义中介者接口或抽象类
public interface RoomMediator 
   public void register(AbstractUser user);
   public void sendTo(AbstractUser from, AbstractUser to, String message);
   public void send(AbstractUser from, String message);

 

 

通用中介者

 

// GeneralRoom.java 中介者的具体实现类
public class GeneralRoom implements RoomMediator 
   public List<AbstractUser> users = new ArrayList<AbstractUser>();

   @Override
   public void register(AbstractUser user) 
      // 用户注册登记,追加用户列表
      user.setRoomMediator(this);
      users.add(user);
   

   // 作为中介者通知给某个用户
   @Override
   public void sendTo(AbstractUser from, AbstractUser to, String message) 
      System.out.println(
            "GeneralRoom:sendTo() [from: " + from.getName() + " message: " + message + " to: " + to.getName() + " ]");
      // 根据用户名称定向发送
      to.recieve(from, message);
   

   // 作为中介者通知给全体用户
   @Override
   public void send(AbstractUser from, String message) 
      System.out.println("GeneralRoom:send() [from: " + from.getName() + " message: " + message + "]");
      for (AbstractUser user : users) 
         user.recieve(from, message);
      
   

 

 

抽象用户类

 

// AbstractUser.java 定义抽象用户类,聚合中介者
public abstract class AbstractUser 

   private String name;
   public RoomMediator roomMediator;

   public AbstractUser(String name) 
      this.name = name;
   

   public void setRoomMediator(RoomMediator roomMediator) 
      this.roomMediator = roomMediator;
   

   public abstract void send(String message);

   public abstract void sendTo(AbstractUser to, String message);

   public abstract void recieve(AbstractUser from, String message);

   public String getName() 
      return this.name;
   

 

 

具体用户对象

 

// CommonUser.java 普通用户继承了抽象用户,实现了具体功能
public class CommonUser extends AbstractUser 

   public CommonUser(String name) 
      super(name);
   

   @Override
   public void send(String message) 
      System.out.println("CommonUser:send() [user: " + this.getName() + " message:" + message + "]");
      // 通过中介者来中转消息
      this.roomMediator.send(this, message);
   

   @Override
   public void sendTo(AbstractUser to, String message) 
      System.out.println("CommonUser:sendTo() [user: " + this.getName() + " message:" + message + " to: " + to.getName() + "]");
      // 通过中介者来中转消息
      this.roomMediator.sendTo(this, to, message);
   

   @Override
   public void recieve(AbstractUser from, String message) 
      System.out.println("CommonUser:recieve() [user: " + this.getName() + " message: " + message + " from:" + from.getName() + "]");
   

 

 

// MemberUser.java 会员用户继承了抽象用户,实现了具体功能
public class MemberUser extends AbstractUser 
   public MemberUser(String name) 
      super(name);
   

   @Override
   public void send(String message) 
      System.out.println("MemberUser:send() [user: " + this.getName() + " message:" + message + "]");
      // 通过中介者来中转消息
      this.roomMediator.send(this, message);
   

   @Override
   public void sendTo(AbstractUser to, String message) 
      System.out
            .println("MemberUser:sendTo() [user: " + this.getName() + " message:" + message + " to: " + to.getName() + "]");
      // 通过中介者来中转消息
      this.roomMediator.sendTo(this, to, message);
   

   @Override
   public void recieve(AbstractUser from, String message) 
      System.out.println("MemberUser:recieve() [user: " + this.getName() + " message: " + message
            + " from user:" + from.getName() + "]");
   

 

 

测试调用

 

public class Test 

  public static void start() 
  /**
   * 中介者模式用一个中介对象来封装一系列动作,让对象之间利用中介者来交流。
   * 这里聊天室就是一个中介者,用户和用户之间的消息传递,全部依靠聊天室来完成。
   * 先给聊天室注册用户,也给用户绑定上中介者,双方彼此持有关系。
   * 中介者的成员之间的消息传递通过中介者来代理传达。
   */

    // 声明一个聊天室
    GeneralRoom generalRoom = new GeneralRoom();
    // 给聊天室添加点不同的人
    AbstractUser user1 = new CommonUser("user1");
    generalRoom.register(user1);
    CommonUser user2 = new CommonUser("user2");
    generalRoom.register(user2);
    MemberUser user3 = new MemberUser("user3");
    generalRoom.register(user3);

    // user1 发送给全体
    user1.send("hi, I\'m " + user1.getName() + ".");
    // user2 发送给 user3
    user2.sendTo(user3, "haha.");

  

  public static void main(String[] args) 
    System.out.println("test start:");
    start();
  


 

 

Go代码

 

中介者抽象接口

 

// RoomMediator.go 定义中介者接口或抽象类
type RoomMediator interface 
  Register(user AbstractUser)
  SendTo(from AbstractUser, to AbstractUser, message string)
  Send(from AbstractUser, message string)

 

 

通用中介者

 

// GeneralRoom.go 中介者的具体实现类
type GeneralRoom struct 
  users []AbstractUser


func (g *GeneralRoom) Register(user AbstractUser) 
  // 用户注册登记,追加用户列表
  user.SetRoomMediator(g)
  g.users = append(g.users, user)


// 作为中介者通知给某个用户
func (g *GeneralRoom) SendTo(from AbstractUser, to AbstractUser, message string) 
  fmt.Println("GeneralRoom::SendTo() [from: " + from.GetName() + " message: " + message + " to: " + to.GetName() + "]")
  // 定向发送给某个用户,名称唯一
  to.Recieve(from, message)


// 作为中介者通知给全体用户
func (g *GeneralRoom) Send(from AbstractUser, message string) 
  fmt.Println("GeneralRoom::Send() [from: " + from.GetName() + " message: " + message + "] ")
  for _, user := range g.users 
    user.Recieve(from, message)
  

 

 

抽象用户类

 

// AbstractUser.go 定义抽象用户类,聚合中介者
type AbstractUser interface 
  SetRoomMediator(roomMediator RoomMediator)
  Send(message string)
  SendTo(to AbstractUser, message string)
  Recieve(from AbstractUser, message string)
  GetName() string

 

 

具体用户对象

 

// CommonUser.go 普通用户继承了抽象用户,实现了具体功能
type CommonUser struct 
  Name         string
  roomMediator RoomMediator


func (c *CommonUser) SetRoomMediator(roomMediator RoomMediator) 
  c.roomMediator = roomMediator


func (c *CommonUser) Send(message string) 
  fmt.Println("CommonUser:Send() [user: " + c.GetName() + " message: " + message + "]")
  // 通过中介者来中转消息
  c.roomMediator.Send(c, message)


func (c *CommonUser) SendTo(to AbstractUser, message string) 
  fmt.Println("CommonUser:SendTo() [user: " + c.GetName() + " message: " + message + "]")
  // 通过中介者来中转消息
  c.roomMediator.SendTo(c, to, message)


func (c *CommonUser) Recieve(from AbstractUser, message string) 
  fmt.Println("CommonUser:Recieve() [user: " + c.GetName() + " message: " + message + " from: " + from.GetName() + "]")


func (c *CommonUser) SetName(name string) 
  c.Name = name


func (c *CommonUser) GetName() string 
  return c.Name

 

 

// MemberUser.go 会员用户继承了抽象用户,实现了具体功能
type MemberUser struct 
  // Name首字母大写以便跨模块访问
  Name         string
  roomMediator RoomMediator


func (c *MemberUser) SetRoomMediator(roomMediator RoomMediator) 
  c.roomMediator = roomMediator


func (c *MemberUser) Send(message string) 
  fmt.Println("MemberUser:Send() [user: " + c.GetName() + " message: " + message + "]")
  // 通过中介者来中转消息
  c.roomMediator.Send(c, message)


func (c *MemberUser) SendTo(to AbstractUser, message string) 
  fmt.Println("MemberUser:SendTo() [user: " + c.GetName() + " message: " + message + "]")
  // 通过中介者来中转消息
  c.roomMediator.SendTo(c, to, message)


func (c *MemberUser) Recieve(from AbstractUser, message string) 
  fmt.Println("MemberUser:Recieve() [user: " + c.GetName() + " message: " + message + " from: " + from.GetName() + "]")


func (c *MemberUser) SetName(name string) 
  c.Name = name


func (c *MemberUser) GetName() string 
  return c.Name

 

 

测试调用

 

func main() 
  fmt.Println("test start:")
  /**
   * 中介者模式用一个中介对象来封装一系列动作,让对象之间利用中介者来交流。
   * 这里聊天室就是一个中介者,用户和用户之间的消息传递,全部依靠聊天室来完成。
   * 先给聊天室注册用户,也给用户绑定上中介者,双方彼此持有关系。
   * 中介者的成员之间的消息传递通过中介者来代理传达。
   */
  // 声明一个聊天室
  var generalRoom = new(src.GeneralRoom)
  // 给聊天室添加点不同的人
  var user1 = &src.CommonUser
    Name: "user1",
  
  generalRoom.Register(user1)

  var user2 = &src.CommonUser
    Name: "user2",
  
  generalRoom.Register(user2)

  var user3 = &src.MemberUser
    Name: "user3",
  
  generalRoom.Register(user3)

  // user1 发送给全体
  user1.Send("hi, I\'m " + user1.GetName() + ".")
  // user2 发送给 user3
  user2.SendTo(user3, "haha.")

 

 

C语言简版

 

#include <stdio.h>

/**
 * 中介者模式是一种行为型设计模式,它可以用来减少类之间的直接依赖关系,
 * 将对象之间的通信封装到一个中介者对象中,从而使得各个对象之间的关系更加松散。
 * 在中介者模式中,对象之间不再直接相互交互,而是通过中介者来中转消息。
 */

typedef struct Colleague Colleague;
typedef struct Mediator Mediator;

// 定义抽象交互对象类
struct Colleague

  int id;
  struct Mediator *mediator;
  void (*send)(struct Colleague *sender, char *message);
  void (*send_to)(struct Colleague *sender, Colleague *receiver, char *message);
  void (*receive)(struct Colleague *receiver, Colleague *sender, char *message);
;

// 定义中介者类
struct Mediator

  struct Colleague **colleagues;
  int colleagues_length;
  void (*send)(Colleague *sender, char *message);
  void (*send_to)(Colleague *sender, Colleague *receiver, char *message);
;

// 交互对象发送消息
void colleague_send(Colleague *sender, char *message)

  printf("\\r\\n colleague_send() [sender->id=%d message=%s]", sender->id, message);
  Mediator *mediator = sender->mediator;
  // 由中介者代为转发消息给全体接收者
  mediator->send(sender, message);


// 交互对象发送消息
void colleague_send_to(Colleague *sender, Colleague *receiver, char *message)

  printf("\\r\\n colleague_send_to() [sender->id=%d receiver->id=%d message=%s]", sender->id, receiver->id, message);
  Mediator *mediator = sender->mediator;
  // 由中介者代为转发消息给指定接收者
  mediator->send_to(sender, receiver, message);


// 对象接收消息
void colleague_receive(Colleague *receiver, Colleague *sender, char *message)

  printf("\\r\\n colleague_receive() [receiver->id=%d send->id=%d message=%s]", receiver->id, sender->id, message);


// 中介者发送消息
void mediator_send(Colleague *sender, char *message)

  printf("\\r\\n mediator_send() [sender->id=%d message=%s]", sender->id, message);
  Colleague *receiver;
  for (int i = 0; i < sender->mediator->colleagues_length; i++)
  
    // 中介者通知全体接收者
    receiver = sender->mediator->colleagues[i];
    receiver->receive(receiver, sender, message);
  


// 中介者发送消息
void mediator_send_to(Colleague *sender, Colleague *receiver, char *message)

  // 中介者通知指定接收者
  printf("\\r\\n mediator_send_to() [sender->id=%d receiver->id=%d message=%s]", sender->id, receiver->id, message);
  receiver->receive(receiver, sender, message);


// 初始化交互对象类
void colleague_constructor(Colleague *self, int id)

  self->id = id;
  self->send = colleague_send;
  self->send_to = colleague_send_to;
  self->receive = colleague_receive;


// 初始化中介者类
void mediator_constructor(Mediator *self, Colleague **colleagues, int colleagues_length)

  self->colleagues = colleagues;
  self->colleagues_length = colleagues_length;
  // 给每个对象绑定中介者
  for (int i = 0; i < self->colleagues_length; i++)
  
    self->colleagues[i]->mediator = self;
  
  self->send = &mediator_send;
  self->send_to = &mediator_send_to;


// test

int main()

  // 初始化具体交互对象a,分别绑定相关函数
  Colleague colleague_a;
  colleague_constructor(&colleague_a, 1);

  // 初始化具体交互对象b,分别绑定相关函数
  Colleague colleague_b;
  colleague_constructor(&colleague_b, 2);

  // 定义交互对象数组
  Colleague *colleagues[] = &colleague_a, &colleague_b;
  int colleagues_length = sizeof(colleagues) / sizeof(colleagues[0]);

  printf("colleagues_length=%d", colleagues_length);

  // 初始化中介者类,绑定交互对象
  Mediator mediator;
  mediator_constructor(&mediator, colleagues, colleagues_length);

  // 交互类发送消息
  colleague_a.send(&colleague_a, "Hi, all! I\'m a.");
  printf("\\r\\n ==== \\r\\n");
  colleague_a.send_to(&colleague_a, &colleague_b, "Hello, colleague_b. I am a!");
  printf("\\r\\n ==== \\r\\n");
  colleague_b.send_to(&colleague_b, &colleague_a, "Hi, colleague_a! I\'m b.");
  return 0;

 

 

更多语言版本

不同语言实现设计模式:https://github.com/microwind/design-pattern

架构师内功心法,干过中介干过快递的代理模式详解

一、代理模式的应用场景

在我们的生活中,经常会见到这样的场景,如:租售房中介、婚介、经纪人、快递等,这些都是代理模式的现实生活体现。代理模式(Proxy Pattern)是指为其它对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象中间起到了中介的作用,使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。

技术图片

技术图片

技术图片

代理模式的类图结构:

技术图片

Subject是顶层设计的接口,RealSubject是真实的对象,Proxy是代理对象,代理对象持有真实对象的引用,客户端Client调用代理对象的方法,同时也调用真实对象的方法,在代理对象前后增加一些处理。我们一想到代理模式,就会理解为代码增强,其实就是在原本的代码逻辑前后增加一些逻辑,而使得调用者无感知。代理模式分为静态代理和动态代理。

二、代理模式的分类

2.1 静态代理

我们直接来举例说明静态代理,青年男女到了适婚的年龄,如果没有对象,周围的亲戚朋友总是张罗着要给某某某介绍对象,这个介绍对象相亲的过程,就是一种我们人人都有份的代理。来看代码实现:

顶层接口设计Person类:

public interface Person {

    /**
     * 寻找伴侣
     */
    void lookForMate();
}

女儿要求找对象,实现Person接口:

public class Daughter implements Person {
    @Override
    public void lookForMate() {
        System.out.println("女儿要求:高大英俊且有钱!");
    }
}

母亲要帮闺女相亲,实现Mother类:

public class Mother {

    private Daughter daughter;
    //如何扩展呢
    public Mother(Daughter daughter) {
        this.daughter = daughter;
    }

    //目标对象的引用daughter拿到,可以调用
    public void lookForMate() {
        System.out.println("母亲物色女儿的对象");
        daughter.lookForMate();
        System.out.println("双方同意交往并确立关系");
    }
    
}

测试内容:

public static void main(String[] args) {
    //只能帮女儿找对象,不能帮表妹、不能帮陌生人
    Mother mother = new Mother(new Daughter());
    mother.lookForMate();
}

运行结果:

技术图片

上面的这个例子是生活中的例子,我们用代码实现了生活中的代理模式。再来一个具体的实际业务场景的例子吧。我们经常会对数据库进行分库分表,分库分表后用Java代码来操作,就需要配置多个数据源,通过设置数据源路由来动态动态切换数据源。 创建订单实体类:

/**
 * 订单实体类
 */
public class Order {

    private String id;

    private Object orderInfo;

    private Long createTime;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Object getOrderInfo() {
        return orderInfo;
    }

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

    public Long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }
}

创建OrderDao持久层操作类:

public class OrderDao {
    public int insert(Order order) {
        System.out.println("创建order对象成功!");
        return 1;
    }
}

创建 IOrderService 接口:

public interface IOrderService {
    int createOrder(Order order);
}

创建 OrderService 实现类:

public class OrderService implements IOrderService {

    private OrderDao orderDao;

    public OrderService(OrderDao orderDao) {
        orderDao = new OrderDao();
    }

    @Override
    public int createOrder(Order order) {
        System.out.println("OrderService调用OrderDao创建订单");
        return orderDao.insert(order);
    }
}

我们来使用静态代理,完成订单创建时间自动按年份进行分库,通过使用代理对象来完成接下来的代码。创建数据源路由对象,使用ThreadLocal单例实现,DynamicDataSourceEntity:

public class DynamicDataSourceEntity {

    /**
     * 默认数据源
     */
    public static final String DEFAULT_DATA_SOURCE = null;

    private static final ThreadLocal<String> local = new ThreadLocal<>();

    private DynamicDataSourceEntity() {}

    /**
     * 获取当前正在使用的数据源
     * @return
     */
    public static String get() {
        return local.get();
    }

    /**
     * 设置已知名字的数据源
     * @param dataSource
     */
    public static void set(String dataSource) {
        local.set(dataSource);
    }

    /**
     * 还原当前切面的数据源
     */
    public static void restore() {
        local.set(DEFAULT_DATA_SOURCE);
    }

    /**
     * 根据年份动态设置数据源
     * @param year
     */
    public static void set(int year) {
        local.set("DB_" + year);
    }

    /**
     * 清空数据源
     */
    public static void remove() {
        local.remove();
    }

}

创建切换数据源代理OrderServiceStaticProxy:

public class OrderServiceStaticProxy implements IOrderService {

    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");

    private IOrderService orderService;

    public OrderServiceStaticProxy(IOrderService orderService) {
        this.orderService = orderService;
    }

    @Override
    public int createOrder(Order order) {
        before();
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
        System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据。");
        DynamicDataSourceEntity.set(dbRouter);
        orderService.createOrder(order);
        after();
        return 0;
    }

    private void before(){
        System.out.println("代理方法执行开始了......");
    }
    private void after(){
        System.out.println("代理方法执行结束了......");
    }
}

main方法的代码:

public static void main(String[] args) throws ParseException {

    Order order = new Order();
    order.setId("010101001");
    //Date today = new Date();
    //order.setCreateTime(today.getTime());

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
    Date date = sdf.parse("2019/02/01");
    order.setCreateTime(date.getTime());


    IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
    orderService.createOrder(order);

}

运行结果是:

技术图片

符合我们的预期效果。现在我们再来回顾一下类图,看是不是和我们最先画的类结构一致:

技术图片

2.2 动态代理

动态代理和静态代理的思路基本是一致的,只不过动态代理的功能更加强大,随着业务的扩展适应性更强。前面说到的母亲替闺女找对象的例子,如果找对象的业务发展为一个行业,那么就是婚姻中介了。来升级代码的实现过程,以满足帮助更多的单身人士找对象的需求。下面使用JDK的方式实现婚姻介绍所。

2.2.1 JDK实现方式

创建婚姻介绍JDKMarriage类:

public class JDKMarriage implements InvocationHandler {

    private Object target;

    public Object getInstance(Object target) {
        this.target = target;

        Class<?> clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object object = method.invoke(this.target, args);
        after();
        return object;
    }

    private void before(){
        System.out.println("我是婚姻介绍所:要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");
    }
    private void after(){
        System.out.println("如果合适的话,就准备办事");
    }
}

创建单身客户Customer类:

public class Customer implements Person {
    @Override
    public void lookForMate() {
        System.out.println("高富帅");
        System.out.println("身高180cm");
        System.out.println("有房有车");
    }
}

测试main方法代码:

 public static void main(String[] args) {

    JDKMarriage marriage = new JDKMarriage();

    Person person = (Person) marriage.getInstance(new Customer());
    person.lookForMate();
}

运行结果:

技术图片

上面的动态代理案例通过实现InvocationHandler接口来完成的,在前面的数据源路由业务,也要用动态代理来实现一下,我们来看下代码:

public class OrderServiceDynamicProxy implements InvocationHandler {

    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");

    public Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Class<?> clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

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

    public void before(Object target) {
        try {
            System.out.println("代理方法执行开始了......");
            Long time = (Long)target.getClass().getMethod("getCreateTime").invoke(target);
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("动态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据");
            DynamicDataSourceEntity.set(dbRouter);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public void after() {
        System.out.println("代理方法执行结束了......");
    }
}

测试main方法代码:

public static void main(String[] args) throws ParseException {
        Order order = new Order();
        order.setId("010101001");
//        Date today = new Date();
//        order.setCreateTime(today.getTime());

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date date = sdf.parse("2019/02/01");
        order.setCreateTime(date.getTime());

        IOrderService orderService = (IOrderService) new OrderServiceDynamicProxy().getInstance(new OrderService());
        orderService.createOrder(order);
}

运行结果:

技术图片

依然可以达到想要的运行效果。但是,动态代理实现之后,我们不仅能实现 Order 的数据源动态路由,还可以实现其他任何类的数据源路由。

2.2.2 CGLib代理调用API及原理分析

pom依赖:

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

还是以婚姻介绍所为例,创建CglibMarriage类:

public class CglibMarriage implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) throws Exception {
        Enhancer enhancer = new Enhancer();
        //要把哪个类设置成为生成的新类的父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);

        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects,
                            MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(o, objects);
        after();
        return obj;
    }

    private void before(){
        System.out.println("我是婚姻介绍所:要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");
    }
    private void after(){
        System.out.println("如果合适的话,就准备办事");
    }
}

接着创建单身客户类Customer:

public class Customer {
    
    public void lookForMate() {
        System.out.println("高富帅");
        System.out.println("身高180cm");
        System.out.println("有房有车");
    }
}

注意:CGLib代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现动态代理的。 来看测试代码:

public static void main(String[] args) {

        try {
            Customer customer = (Customer)new CglibMarriage().getInstance(Customer.class);
            customer.lookForMate();
        } catch (Exception e) {
            e.printStackTrace();
        }

}

CGLib代理执行代理对象的方法效率之所以比JDK的高,是因为CGLib采用了FastClass机制,FastClass的原理是:为代理类和被代理类各生成一个class,这个class会为代理类或被代理类的方法分配一个index(int类型),这个index当作入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。

2.2.3 CGLib和JDK动态代理对比

1、JDK动态代理实现了被代理对象的接口,CGLib代理继承了被代理对象。

2、JDK和CGLib都在运行期间生成字节码,JDK动态代理直接生成class字节码,CGLib代理通过asm框架生成class字节码,CGLib代理实现更复杂,生成代理类比JDK动态代理效率低。

3、JDK动态代理调用代理方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用方法的,CGLib代理的执行效率高。

三、Spring与代理模式

3.1 代理模式在Spring源码中的应用

先看 ProxyFactoryBean 核心的方法就是 getObject() 方法,我们来看一下源码:

@Nullable
public Object getObject() throws BeansException {
    this.initializeAdvisorChain();
    if (this.isSingleton()) {
        return this.getSingletonInstance();
    } else {
        if (this.targetName == null) {
            this.logger.info("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the ‘targetName‘ property.");
        }

        return this.newPrototypeInstance();
    }
}

在 getObject()方法中,主要调用 getSingletonInstance() 和newPrototypeInstance() ; 在 Spring 的配置中,如果不做任何设置,那么 Spring 代理生成的 Bean 都是单例对象。如果修改 scope则每次创建一个新的原型对象。newPrototypeInstance()里面的逻辑比较复杂,我们后面的课程再做深入研究,这里我们先做简单的了解。

3.2 Spring 中的代理选择原则

Spring 利用动态代理实现 AOP 有两个非常重要的类,一个是 JdkDynamicAopProxy 类和 CglibAopProxy 类,来看一下类图:

技术图片

  • 当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理;

  • 当 Bean 没有实现接口时,Spring 选择 CGLib。

三、 静态代理和动态的本质区别

1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。

2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。

3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。

四、代理模式的优缺点

使用代理模式具有以下几个优点:

  • 代理模式能将代理对象与真实被调用的目标对象分离;

  • 一定程度上降低了系统的耦合度,扩展性好;

  • 可以起到保护目标对象的作用;

  • 可以对目标对象的功能增强。

当然,代理模式也是有缺点的:

  • 代理模式会造成系统设计中类的数量增加;

  • 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢;

  • 增加了系统的复杂度。

来源:迅闻网

以上是关于中介者设计模式详解C/Java/JS/Go/Python/TS不同语言实现的主要内容,如果未能解决你的问题,请参考以下文章

架构师内功心法,干过中介干过快递的代理模式详解

架构师内功心法,干过中介干过快递的代理模式详解

Javascript常用的设计模式详解

代理模式详解

20160227.CCPP体系详解(0037天)

Java设计模式之五大创建型模式(附实例和详解)