笔试题之理发师问题DDD建模

news2024/10/5 19:14:05

背景

题目

假设有一个理发店只有一个理发师,一张理发时坐的椅子,若干张普通椅子顾客供等候时坐。没有顾客时,理发师睡觉。顾客一到,叫醒理发师。如果理发师没有睡觉,而在为别人理发,他就会坐下来等候。如果所有的椅子都坐满了人,最后来的顾客就会离开。

这是一道笔试题,主要考察面试者的业务建模能力,在短时间内抽象主要业务模型,提炼出模型的属性和方法。然后利用消费者-生存者的模型思维串联业务流程,还有协调者模型的设计,使其能完成桥梁枢纽的功能,这些都是考察的对象。

方案

这里整个领域可以拆分两个上下文,即理发上下文、顾客上下文。

理发上下文在防腐层中,通过开放主机+发布语言的范式获取顾客的详细信息,完成业务的协作。

流程的步骤:

     生产者

  1. 顾客进入理发店,唤醒理发师;
  2. 理发椅是否使用,未使用直接入座;
  3. 理发椅在使用,则进入队列排队;

     消费者

  1. 理发师给理发椅上的顾客理发;
  2. 队列不为空,中拉取顾客,让顾客做到理发椅上。

这是大致的实现思路,下面是我根据描述画了一张业务流程图:

代码 

理发师

它有状态属性,和理发的行为,每次理发需要消耗两秒。

package org.example;

import lombok.Getter;
import lombok.SneakyThrows;

@Getter
public class Barber {
    private BarberStatus status;

    @SneakyThrows
    public void hairCut(CustomerId customerId) {
        System.out.println(String.format("理发师为 %s 理发", customerId.getNum()));
        Thread.sleep(2 * 1000);
    }

    public Barber() {
        this.status = BarberStatus.SLEEP;
    }

    public void weekUp() {
        this.status = BarberStatus.WORKING;
    }
}

 理发师包含睡觉、工作两种状态:

public enum BarberStatus {
    SLEEP, WORKING
}

理发椅

它作为协调者资源共享对象,所以这里有加锁。理发椅在顾客坐下时绑定了顾客,提供完成理发后的资源释放行为。

package org.example;

import lombok.Getter;

@Getter
public class BarberChair {
    private CustomerId customerId;
    private BarberChairStatus status;

    public BarberChair() {
        this.status = BarberChairStatus.FREE;
    }

    public synchronized void seated(CustomerId customerId) {
        this.status = BarberChairStatus.SEATED;
        this.customerId = customerId;
        System.out.printf("用户 %s 坐下%n", customerId.getNum());
    }

    public synchronized void free() {
        this.status = BarberChairStatus.FREE;
        System.out.printf("顾客 %s 理发结束%n", customerId.getNum());
    }

}

理发椅包含被坐还是空闲两种状态: 

public enum BarberChairStatus {
    SEATED, FREE;
}

理发店

它作为一个聚合对象,是整个领域模型的入口,内聚了所有内部对象,所有业务操作都在这里向外暴露。

理发店的属性:理发师、等待的顾客、供顾客坐的椅子数、理发椅。

理发店拥有两个行为:接单、派单

这里可以将理发店理解位理发店系统,系统完成顾客接单,放入队列中。然后异步地派单给理发师,从队列中获取并通知顾客去理发椅理发。

package org.example;

import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

import java.util.concurrent.ArrayBlockingQueue;

@Setter
@Getter
public class BarberStore {

    private Barber barber;

    private ArrayBlockingQueue<CustomerId> waitingCustomerIds;

    private int customerChairSize;

    private BarberChair barberChair;


    @SneakyThrows
    public void receiveOrder(CustomerId customerId) {
        barber.weekUp();
        if (barberChair.getStatus() == BarberChairStatus.FREE) {
            barberChair.seated(customerId);
            return;
        }
        if (waitingCustomerIds.size() == customerChairSize) {
            System.out.printf("用户 %s 离开%n", customerId.getNum());
            return;
        }
        System.out.printf("用户 %s 等待%n", customerId.getNum());
        waitingCustomerIds.add(customerId);

    }

    public void dispatchOrder() {
        if (barberChair.getStatus() == BarberChairStatus.SEATED) {
            hairCutting(barberChair.getCustomerId());
            return;
        }
        if (!waitingCustomerIds.isEmpty()) {
            CustomerId customerId = waitingCustomerIds.poll();
            barberChair.seated(customerId);
            hairCutting(customerId);
        }
    }

    public BarberStore(int customerChairSize) {
        this.barber = new Barber();
        this.customerChairSize = customerChairSize;
        this.barberChair = new BarberChair();
        this.waitingCustomerIds = new ArrayBlockingQueue<>(customerChairSize);
    }

    @SneakyThrows
    private void hairCutting(CustomerId customerId) {
        barber.hairCut(customerId);
        barberChair.free();
    }
}

顾客

顾客通过顾客Id和理发店聚合进行业务关联。为什么使用CustomerId进行关联呢?

因为顾客并不是理发店聚合内的实体,顾客的生命周期并不和理发店生命周期一致,这家理发店倒闭了,顾客可以去下一家理发店。且顾客也存在较多自己领域内的业务规则。

package org.example;

import lombok.Getter;

@Getter
public class CustomerId {
    private int num;

    public CustomerId(int num) {
        this.num = num;
    }
}

 

测试客户端

模拟两个线程,一个生产者接单,一共接10个单,接单一次休息1s。一个消费者线程派单,一直不停地派单,理发椅有人则去理发,理发完成后让顾客坐到理发椅上,一直重复直到队列为空。

package org.example;

import lombok.SneakyThrows;

/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) {
        BarberStore barberStore = new BarberStore(3);

        // 理发师椅子是顾客和理发师连接的纽带
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                barberStore.receiveOrder(new CustomerId(i));
                mockPauseSecond(1);
            }
        }).start();


        new Thread(() -> {
            while (true) {
                barberStore.dispatchOrder();
            }
        }).start();
    }

    @SneakyThrows
    private static void mockPauseSecond(int i) {
        Thread.sleep(i * 1000);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1619385.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

minicom安装及使用

1.安装 sudo apt-get install minicom 2.配置 sudo minicom -s 光标在 Serial port setup 上按enter键进入配置 ls /dev/ttyUSB* 查看对应 usb 节点 按相应字母进入配置 回到主菜单选择 Save setup as dfl 保存为默认配置&#xff0c;然后退出重新打开 3. 使用 sudo mini…

LabVIEW专栏八、类

该章目的是可以开发仪器类。 一、类的概述 一般来说类有三大特性&#xff0c;封装&#xff0c;继承和多态。 在实际项目中&#xff0c;最主要是继承和多态&#xff0c;要搞清楚这两者的概念和在LabVIEW中是怎样应用的。在LabVIEW中&#xff0c;面向对象编程用到的就是LabVIE…

红海云签约南都物业,助力物管行业人力资源数智一体化

南都物业服务集团股份有限公司&#xff08;以下简称“南都物业”&#xff09;是中国第一代、浙江省第一批注册成立的独立第三方物业服务企业&#xff0c;也是国内A股主板市场物业行业第一股&#xff08;股票简称&#xff1a;南都物业&#xff0c;股票代码&#xff1a;603506&am…

PS入门|用PS设计物品尺寸,需要注意的几个重要问题

注&#xff1a;仅学习使用 【PS24】2024版本更新的内容比较多&#xff0c;对电脑配置的要求也是比较高的。建议配置&#xff1a;第十代i5或以上CPU。 如果是第十代i3或以下的CPU&#xff0c;建议安装PS2021或者以下版本。 ---这是一条不正经的分割线--- 讲了那么久的PS教程…

设计模式-构建者模式

作者持续关注 WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS二次开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 目录 定义 特点 使用场景 优缺点 (1) 优点 …

重看Spring聚焦Environment分析

目录 一、理解Environment的设计 &#xff08;一&#xff09;整体理解 &#xff08;二&#xff09;聚焦Profiles分析 &#xff08;三&#xff09;聚焦Properties分析 二、Environment类图结构分析 三、PropertyResolver源码分析 &#xff08;一&#xff09;源码展示说明…

广东理工学院携手泰迪智能科技成功部署人工智能实验室

广东理工学院是经国家教育部批准设立的全日制普通本科院校&#xff0c;入选全国应用型人才培养工程培养基地、国家级众创空间试点单位、广东省高校电子商务人才孵化基地。开设34个本科专业&#xff0c;涵盖工学、经济学、管理学、文学、艺术学、教育学等6大学科门类&#xff0c…

Power BI 如何创建页面导航器?(添加目录按钮/切换页面按钮)

Power BI 中页导航是什么&#xff1f; 在Power BI中&#xff0c;页导航&#xff08;Page Navigation&#xff09;是指在报告中创建多个页面&#xff08;页&#xff09;&#xff0c;然后允许用户在这些页面之间进行导航的功能。 如下图所示&#xff0c;页导航的选项和报告中的…

李飞飞团队《2024 年人工智能指数报告》AI十大趋势:中国AI专利数全球第一

《2024 年人工智能指数报告》 当地时间4月15日&#xff0c;斯坦福大学“以人为本”人工智能研究院&#xff08;Human Centered Artificial Intelligence&#xff0c;简称HAI&#xff09;发布了第七个年度AI Index报告&#xff0c;这是关于AI行业现状的最全面的报告之一。 报告…

聊聊go语言中的GMP模型

写在文章开头 我们都知道go语言通过轻量级线程协程解决并发问题&#xff0c;按照go语言的思想这些协程运行完成后即焚&#xff0c;那么go语言如何保证并发线程有序获取协程呢&#xff1f; 带着这个问题我们从go语言底层的源码来阐述这个问题&#xff1a; Hi&#xff0c;我是 s…

K-means聚类算法:如何在杂乱无章的数据中找出规律?

什么是K-means聚类算法&#xff1f; 在编程的世界里&#xff0c;K-means聚类算法就像一位无私的指路人&#xff0c;它不需要我们给出明确的指示&#xff0c;只需要我们提供数据&#xff0c;它就能帮助我们找到数据的归属&#xff0c;找到数据的“家”。 K-means聚类算法的名字…

经典的目标检测算法有哪些?

一、经典的目标检测算法有哪些&#xff1f; 目标检测算法根据其处理流程可以分为两大类&#xff1a;One-Stage&#xff08;单阶段&#xff09;算法和Two-Stage&#xff08;两阶段&#xff09;算法。以下是一些经典的目标检测算法&#xff1a; 单阶段算法: YOLO (You Only Loo…

【御控工业物联网】JAVA JSON结构转换、JSON结构重构、JSON结构互换(5):对象To对象——转换映射方式

御控官网&#xff1a;https://www.yu-con.com/ 文章目录 御控官网&#xff1a;[https://www.yu-con.com/](https://www.yu-con.com/)一、JSON结构转换是什么&#xff1f;二、术语解释三、案例之《JSON对象 To JSON对象》四、代码实现五、在线转换工具六、技术资料 一、JSON结构…

Linux中的vi与vim:编辑器的王者之争与深度探索

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Linux &#xff1a;从菜鸟到飞鸟的逆袭》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、Linux的起源与发展 2、vi与vim的历史与发展 …

【C++】手撕list(list的模拟实现)

目录 01.节点 02.迭代器 迭代器运算符重载 03.list类 &#xff08;1&#xff09;构造与析构 &#xff08;2&#xff09;迭代器相关 &#xff08;3&#xff09;容量相关 &#xff08;4&#xff09;访问操作 &#xff08;5&#xff09;插入删除 我们在学习数据结构的时候…

Laravel 6 - 第十一章 中间件

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

Unity 如何制作和发布你的 Package

一、制作你的第一个 Package Unity Package 不做过多赘述&#xff0c;像 URP 本质上也是一个 Package&#xff0c;在 Unity 中可以通过菜单栏 → Window → Package manager 来管理你当前的所有 Package 本篇文章主要介绍&#xff1a;如何制作并发布属于你的 Package 1.1 Pac…

将本地项目推送至gitlab仓库

1. gitlab上新建一个空白项目 gitlab上点击new project按钮&#xff0c;新建一个项目 新建空白项目 项目名称与本地新建项目名称相同&#xff0c;其余根据具体需要选择 2. 初始化本地仓库并commit项目 进入本地项目根目录下&#xff0c;右击 git bash here打开命令窗口 初始化…

8.4.2 实验2:配置Trunk

1、实验目的 通过本实验可以掌握&#xff1a; Native VLAN 的含义和配置。IEEE802.1q 封装Trunk 配置和调试方法。 2、实验拓扑 配置 Trunk 的实验拓扑如下图所示。 图8-6 配置 Trunk 的实验拓扑 3、实验步骤 3.1 在交换机S1、S2上创建 VLAN 并把端口划分到相应的VLAN中 …

ASP.NET教务管理平台-权限及公共模块设计与开发

摘 要 随着教育改革的不断深化&#xff0c;高等院校的建设与发展对国民整体素质的提高起着越来越重要的作用&#xff0c;建立一套能够适应这些改变的行政管理方案也就显得尤为重要。对于教务处来说&#xff0c;将信息技术用于校务管理中便是迫切的要求。 教务系统中的用户…