设计模式---中介模式

中介模式

“中介”

  1. 租房 --“中介”
  2. 找工作---“人才交流中心”
  3. 在 MVC 框架中--“控制器(C)”

定义

定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。大大降低对象之间的耦合性,提高系统的灵活性。

应用场景

  • 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
  • 想通过一个中间类来封装多个类的行为

举例

背景: 六个房东都正在出租自己房屋,房屋都各有特点,租客A来租房,看房东一不满意,房东一让他去看其他房间,开始联系房东二、三、四、五...

变动: 如果房东二在某天发生了变化,房子已经租出去了,这个时候他需要告知其他五位房东,不用给他介绍租客了

问题: 如果中间一个人发生变化,则需要改动其他五个人

优化: 引入中介,所有的房屋交给中介,租客A不满意,中介带她看其他房,如果中间有一个房被出租了,则中介只需要和对应的房东联系即可,其他五位房东不会受影响

不使用中介模式:

使用中介模式:

1eb18123390e61b1f0e912455f3051ff.png

  1. 世界上存在着各种各样的数据库,不同数据库有各自的应用场景,对于同一份数据,最开始可能使用关系型数据库(如MySQL)进行存储查询,使用Redis作为缓存数据库,当数据量较大时使用MySQL进行查询可能较慢,所以需要将数据同步到Elasticsearch或者列式数据库如Hbase中进行大数据查询。个数据源直接同步数据到目标端数据库的,如果数据库有 N 个,那么最多可能的同步作业将达到 N * N 个,当修改了其中一个数据库的某些配置,可能需要修改另外的 N - 1 个数据库的同步作业。DataX 其实相当于一个中介,从数据源读取数据,写入到目标端,数据源不再需要维护到目标端的同步作业,只需要与 DataX 通信即可。DataX 体现了中介者模式的思想

“网状”---》“星状”

引入一个中介者对象,各个同事对象只跟中介者对象打交道

6b3ad98a5c9484b72f9fddbeb8af5730.png

两个职责:

中转作用:通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。

协调作用:中介者可以更进一步的对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。

代码实现

  1. 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
  2. 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
  3. 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
  4. 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

dc599287f7dbd049cd0c8d9c8e0c36c0.png

/**
 * Description:  <br> 抽象中介者
 */
public abstract class Mediator {

    //注册
    public abstract void register(Colleague colleague);

    //转发
    public abstract void relay(Colleague cl);
}


/**
 * Description:  <br> 抽象同事类
 *
 * @author wangfeng@tsintergy.com
 * @create 2021/12/12
 * @since 1.0.0
 */
public abstract class Colleague {

    protected Mediator mediator;

    public void setMedium(Mediator mediator) {
        this.mediator = mediator;
    }

    /**
     * 接收消息
     */
    public abstract void receive();

    /**
     * 发送消息
     */
    public abstract void send();
}


/**
 * Description:  <br> 具体中介
 *
 * @author wangfeng@tsintergy.com
 * @create 2021/12/12
 * @since 1.0.0
 */
public class ConcreteMediator extends Mediator {

    private List<Colleague> colleagues = new ArrayList<Colleague>();

    @Override
    public void register(Colleague colleague) {
        if (!colleagues.contains(colleague)) {
            colleagues.add(colleague);
            colleague.setMedium(this);
        }
    }

    @Override
    public void relay(Colleague cl) {
        for (Colleague ob : colleagues) {
            if (!ob.equals(cl)) {
                ((Colleague) ob).receive();
            }
        }
    }
}
/**
 * Description:  <br> 具体同事类1
 *
 * @author wangfeng@tsintergy.com
 * @create 2021/12/12
 * @since 1.0.0
 */
public class ConcreteColleague1 extends Colleague {

    @Override
    public void receive() {
        System.out.println("具体同事类1收到请求。");
    }
    @Override
    public void send() {
        System.out.println("具体同事类1发出请求。");
        mediator.relay(this); //请中介者转发
    }
}

/**
 * Description:  <br> 具体同事类2
 *
 * @author wangfeng@tsintergy.com
 * @create 2021/12/12
 * @since 1.0.0
 */
public class ConcreteColleague2 extends Colleague {

    @Override
    public void receive() {
        System.out.println("具体同事类2收到请求。");
    }
    @Override
    public void send() {
        System.out.println("具体同事类2发出请求。");
        mediator.relay(this); //请中介者转发
    }
}



    public static void main(String[] args) {
        Mediator md = new ConcreteMediator();
        Colleague c1 = new ConcreteColleague1();
        Colleague c2 = new ConcreteColleague2();
        md.register(c1);
        md.register(c2);
        c1.send();
        System.out.println("-------------");
        c2.send();
    }
具体同事类1发出请求。
具体同事类2收到请求。
-------------
具体同事类2发出请求。
具体同事类1收到请求。

优点

1、松散耦合、将多个对象之间的联系紧耦合封装到中介对象中,做到松耦合。不会导致一动牵全身。

2、将多个对象之间的交互联系集中在中介对象中。发送变化仅需修改中介对象即可、提供系统的灵活性、使同事对象独立而易于复用。

3、符合迪米特原则。就是说一个对象应当对其他对象有尽可能少的了解。减少各个对象之间的了解。

缺点

如果各个同事间的交互非常多并且复杂情况下,都交给中介者会导致中介者变得十分复杂,不易维护和管理。

源码中的中介者模式

  • Timer#schedule

Timer:

  1. Timer类中多个scheduler()的重载方法
  2. 所有的scheduler()的方法内部都调用了私有方法sched()

12be7956d81c2ea219193f1d0ffab13c.png

  1. schde()方法源码---安排指定的定时器任务在指定的时间执行 
   private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");

        // Constrain value of period sufficiently to prevent numeric
        // overflow while still being effectively infinitely large.
        if (Math.abs(period) > (Long.MAX_VALUE >> 1))
            period >>= 1;
       // 获取任务队列的锁
        synchronized(queue) {
        / /如果定时调度线程已经终止了,则抛出异常结束
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");
            synchronized(task.lock) {
            判断线程的状态
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                        "Task already scheduled or cancelled");
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED;
            }
          // 将任务加入任务队列 
            queue.add(task);
            // 如果当前加入的任务是需要第一个被执行的(也就是他的下一次执行时间离现在最近)// 则唤醒等待queue的线程
            if (queue.getMin() == task)
                queue.notify();
        }
    }
    
    
    
    
import java.util.TimerTask;

public class TimerThread extends Thread {
 
    boolean newTasksMayBeScheduled = true;

    /**
     * 任务队列的引用
     */
    private TaskQueue queue;

    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            mainLoop();
        } finally {
            // 任务结束,例如Timer 执行了cancel
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  //清除废弃对象的引用
            }
        }
    }

    /**
     * 
     */
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // 如果暂时没有任务,并且TimerThread线程没有被取消就等待
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
                    if (queue.isEmpty())
                        break; 
                    long currentTime, executionTime;
                    task = queue.getMin();
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;//下一次执行的时间
                        //如果下一次执行时间小于等于当前时间说明可以执行了
                        if (taskFired = (executionTime<=currentTime)) {
                            if (task.period == 0) {
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } 

                            else { 
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) 
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  
                    task.run();//只是调用的run方法没有启用新的线程
            } catch(InterruptedException e) {
            }
        }
    }
}
  1. Timer 这个中介者的功能就是定时调度我们写的各种任务,将任务添加到 TaskQueue 任务队列中,给 TimerThread 执行,让任务与执行线程解耦
  2. Timer 是中介者,TimerTask是抽象同事类,我们自己写的任务则是具体同事类

回顾相似模式

外观模式

代理模式

三者区别

中介者模式:注重的是将多对多的关系处理封装到一对多、通过一个中介者对象对多个对象之间的依赖进行解耦

外观模式:  外观模式注重的是简化接口,简化组件系统与外部程序的依赖关系。提供一个更高层次的统一接口,使子系统更加简便的使用

代理模式: 注重的是对其他对象的访问控制、倾向于的是一对一的关系处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值