基于队列模型编写一个入岗检查站

Posted 将码农当成一辈子的事业来干!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于队列模型编写一个入岗检查站相关的知识,希望对你有一定的参考价值。

一、背景

想象一下这种场景,我们在汽车生产车间,会将汽车生产步骤分做不同的流程,提前在车间安装好,然后,我们将汽车零部件依次放入流水线,经过不同流程处理,最终组装成一台汽车。

它是将每个处理流程提前安装在了流水线上,要想生产汽车,我们只需要将各种汽车配件放入,就可以得到一辆完整的汽车。那么这种处理过程,我们就可以看做是一种队列模型处理逻辑。

二、模型场景

接下来,我们来设定我们的使用场景。

某工厂招工,一大批员工来面试,面试条件有两个:

  1. 只要男生
  2. 必须年满18岁

针对上面的要求,工厂安排了两场面试,第一场检查面试者是否为男生,第二场检查身份证年龄,是否年满18岁。

我们下面利用代码队列来实现这个场景!

三、实现逻辑

代码中会使用到插件lombok,请同学们自行安装!

1.实体类

首先,建立面试者实体类:

package cn.wxson.chain.bean;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
@AllArgsConstructor
public class User {
    private String name;
    private int age;
    private String gender;
}

2.上下文类

全局上下文

针对实例类来建立全局上下文,其中包括两个方法,一个获取上下文服务;另一个是交接任务到下一个节点。

package cn.wxson.chain.content;

import cn.wxson.chain.service.ContextService;

/**
 * Title 全局上下文
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
public interface Context<T> {

    /**
     * 获取服务类
     *
     * @return 服务类
     */
    ContextService<T> service();

    /**
     * 业务逻辑的节点传递
     *
     * @param data 数据
     */
    void transmit(T data);
}

处理业务抽象上下文类

为了保持链式结构,我们需要为后面的每个上下文对象提供它的前一个上下文与后一个上下文对象,所以,我们增加两个属性,并实现业务传递操作方法:transmit(T data)。

package cn.wxson.chain.content;

import lombok.Getter;
import lombok.Setter;

/**
 * Title 上下文业务处理抽象类
 * 后面的第一个节点上下文、最后一个节点上下文、普通节点上下文都继承自它
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
@Setter
@Getter
public abstract class AbstractContext<T> implements Context<T> {

    /**
     * 前一个上下文
     */
    private AbstractContext<T> pre;
    /**
     * 后一个上下文
     */
    private AbstractContext<T> next;

    /**
     * 业务逻辑的节点传递
     *
     * @param data 数据
     */
    @Override
    public void transmit(T data) {
        // 直接进行下个节点的业务操作
        this.next.service(data);
    }

    /**
     * 将具体数据业务处理抽象出来,便于节点操作
     *
     * @param data 数据
     */
    public void service(T data) {
        this.service().execute(this, data);
    }
}

第一个、最后一个上下文与普通上下文

第一个、最后一个上下文比较特殊,其内部不需要做业务处理,只需要传递即可。

package cn.wxson.chain.content.impl;

import cn.wxson.chain.content.AbstractContext;
import cn.wxson.chain.service.ContextService;

/**
 * Title 第一个节点上下文
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
public final class FirstContextImpl<T> extends AbstractContext<T> implements ContextService<T> {

    /**
     * 获取服务类
     *
     * @return 第一个节点返回自身
     */
    @Override
    public ContextService<T> service() {
        return this;
    }

    /**
     * 第一个节点,直接执行下一个节点数据,不做业务处理
     *
     * @param context 上下文
     * @param data    数据
     */
    @Override
    public void execute(AbstractContext<T> context, T data) {
        // 不做任何自身业务逻辑处理,直接传递给下个节点处理
        context.transmit(data);
    }
}
package cn.wxson.chain.content.impl;

import cn.wxson.chain.content.AbstractContext;
import cn.wxson.chain.service.ContextService;

/**
 * Title 最后一个节点上下文
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
public final class LastContextImpl<T> extends AbstractContext<T> implements ContextService<T> {

    /**
     * 获取服务类
     *
     * @return 最后一个节点返回自身
     */
    @Override
    public ContextService<T> service() {
        return this;
    }

    /**
     * 最后一个节点,不做任何处理
     *
     * @param context 上下文
     * @param data    数据
     */
    @Override
    public void execute(AbstractContext<T> context, T data) {
        // 不做任何业务处理,也不需要再传递下去
        // NOOP
    }
}
package cn.wxson.chain.content.impl;

import cn.wxson.chain.content.AbstractContext;
import cn.wxson.chain.service.ContextService;

/**
 * Title 普通节点的上下文
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
public class CommonContextImpl<T> extends AbstractContext<T> {

    /**
     * 具体业务实现服务类
     */
    private ContextService<T> service;

    /**
     * 无参构造
     */
    public CommonContextImpl() {
        super();
    }

    /**
     * 带参构造
     *
     * @param service 服务类
     */
    public CommonContextImpl(ContextService<T> service) {
        this.service = service;
    }

    /**
     * 获取服务类
     *
     * @return 返回传进来的服务类
     */
    @Override
    public ContextService<T> service() {
        return this.service;
    }
}

3.上下文业务服务类

上下文业务服务接口

只包含一个具体业务处理方法:

package cn.wxson.chain.service;

import cn.wxson.chain.content.AbstractContext;

/**
 * Title 上下文业务服务类
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
public interface ContextService<T> {

    /**
     * 业务逻辑处理
     *
     * @param context 上下文
     * @param data    数据
     */
    void execute(AbstractContext<T> context, T data);
}

抽象的默认上下文业务服务

第一个节点上下文与最后一个节点上下文已在FirstContextImpl.class和LastContextImpl.class中实现,处理逻辑可参考代码说明。普通节点的上下文处理逻辑我们抽象出一个静态类,做业务剥离,将具体业务转移到节点实现类中去,这里只做抽象业务处理和节点传递。

package cn.wxson.chain.service;

import cn.wxson.chain.content.AbstractContext;

/**
 * Title 默认上下文业务服务类
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
public abstract class AbstractDefaultContextService<T> implements ContextService<T> {

    /**
     * 业务逻辑处理
     *
     * @param context 上下文
     * @param data    数据
     */
    @Override
    public void execute(AbstractContext<T> context, T data) {
        // 1.本节点的业务逻辑处理
        this.execute(data);
        // 2.传递逻辑到下个节点
        context.transmit(data);
    }

    /**
     * 本节点的业务逻辑处理
     *
     * @param data 数据
     */
    public abstract void execute(T data);
}

具体上下文服务类

针对年龄和性别两场面试,我们分别实现业务处理:

package cn.wxson.chain.service.impl;

import cn.wxson.chain.bean.User;
import cn.wxson.chain.service.AbstractDefaultContextService;
import lombok.extern.slf4j.Slf4j;

/**
 * Title 年龄说明
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
@Slf4j
public class AgeServiceImpl extends AbstractDefaultContextService<User> {

    /**
     * 根据人员年龄进行检查
     * 必须成年,大于等于18岁
     *
     * @param user 数据
     */
    @Override
    public void execute(User user) {
        String rs = user.getAge() >= 18 ? "合格" : "不合格";
        log.info("人员姓名:{},年龄检查结果:{}", user.getName(), rs);
    }
}
package cn.wxson.chain.service.impl;

import cn.wxson.chain.bean.User;
import cn.wxson.chain.service.AbstractDefaultContextService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * Title 姓名说明
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
@Slf4j
public class GenderServiceImpl extends AbstractDefaultContextService<User> {

    /**
     * 根据人员性别进行检查
     * 只要男性
     *
     * @param user 数据
     */
    @Override
    public void execute(User user) {
        String rs = StringUtils.equals(user.getGender(), "男") ? "合格" : "不合格";
        log.info("人员姓名:{},姓名检查结果:{}", user.getName(), rs);
    }
}

4.上下文队列

通过以上步骤,我们就实现了具体业务的处理逻辑,接下来,我们将业务采用队列形式操作。

package cn.wxson.chain;

import cn.wxson.chain.content.AbstractContext;
import cn.wxson.chain.content.impl.CommonContextImpl;
import cn.wxson.chain.content.impl.FirstContextImpl;
import cn.wxson.chain.content.impl.LastContextImpl;
import cn.wxson.chain.service.ContextService;

/**
 * Title 上下文队列
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
public class Pipeline<T> {

    /**
     * 第一个上下文
     */
    private final AbstractContext<T> first;
    /**
     * 最后一个上下文
     */
    private final AbstractContext<T> last;

    /**
     * 初始化队列第一个上下文与最后一个上下文,并将它们连起来
     */
    public Pipeline() {
        this.first = new FirstContextImpl<T>();
        this.last = new LastContextImpl<T>();
        this.first.setNext(this.last);
        this.last.setPre(this.first);
    }

    /**
     * 新增服务类作为普通上下文节点包装
     * 并把它排到最后一个上下文前面,即:普通节点的队尾
     *
     * @param service 服务类
     */
    public void add(ContextService<T> service) {
        AbstractContext<T> context = new CommonContextImpl<T>(service);
        AbstractContext<T> backwardsSecond = this.last.getPre();
        context.setPre(backwardsSecond);
        context.setNext(this.last);
        backwardsSecond.setNext(context);
        this.last.setPre(context);
    }

    /**
     * 新增服务类作为普通上下文节点包装
     * 并把它排到第一个上下文后面,即:普通节点的队首
     *
     * @param service 服务类
     */
    public void addFirst(ContextService<T> service) {
        AbstractContext<T> context = new CommonContextImpl<T>(service);
        AbstractContext<T> forwardsSecond = this.first.getNext();
        this.first.setNext(context);
        forwardsSecond.setPre(context);
        context.setPre(this.first);
        context.setNext(forwardsSecond);
    }

    /**
     * 从队列第一个上下文节点开始处理数据
     *
     * @param data 数据
     */
    public void handler(T data) {
        this.first.transmit(data);
    }
}

这样,我们就针对工厂的两场面试准备工作都做好了,接下来测试一下。

5.测试

package cn.wxson.chain;

import cn.wxson.chain.bean.User;
import cn.wxson.chain.service.ContextService;
import cn.wxson.chain.service.impl.AgeServiceImpl;
import cn.wxson.chain.service.impl.GenderServiceImpl;

/**
 * Title 测试类
 *
 * @author Ason(18078490)
 * @date 2020-07-28
 */
public class Domain {

    public static void main(String[] arg) {
        // 创建预处理队列
        Pipeline<User> pipeline = new Pipeline<User>();
        ContextService<User> nameService = new GenderServiceImpl();
        ContextService<User> ageService = new AgeServiceImpl();
        pipeline.add(nameService);
        pipeline.add(ageService);
        // 放入人员信息,逐个检查
        pipeline.handler(new User("张三", 18, "男"));
        pipeline.handler(new User("李四", 20, "女"));
        pipeline.handler(new User("王五", 13, "男"));
    }
}

运行结果:

结果

四、总结

通过这个例子可以看到,我们提前将面试流程安排好,只需要让面试者逐个进入,就可以达到逐个检查的流水线效果,实现人员是否满足面试邀请的考核工作!

以上是关于基于队列模型编写一个入岗检查站的主要内容,如果未能解决你的问题,请参考以下文章

# Java 常用代码片段

# Java 常用代码片段

这个代码片段有啥作用?

IPC System V 消息队列 - 发送一个数组块

是否需要锁定阅读对象?

Linux多线程_(线程同步,基于阻塞队列的生产者消费者模型)