黑马程序员---银行业务调度系统学习笔记

------- Java、.NetAndroid培训期待与您交流!------- 

一、银行业务调度系统的项目需求

二、面向对象的分析与设计

        对该系统的需求分析,至少可以得到三个对象:客户、取号机、业务窗口。

  • 客户类。客户分为三种类型:普通客户、快速客户和VIP客户,所以很容易得出客户类用枚举来声明在合适不过。因为不涉及到对客户属性的调用,所以不必声明其他方法,该枚举很简单。
  • 取号机。现在的银行业务应用中,都会用一台产生号码的机器来管理客户的业务顺序,只有被叫到的客户才会到业务窗口办理业务。该机器会产生三种类型的号码,这个是根据客户的类型来判断的。所以定义一个 NumberMachine 类来管理号码段,且一个银行中只有一台取号机,所以这个类被定义成单例模式(作用是实现多个线程的数据共享),这样保证了数据的号码唯一性,不会出现重号的情况。
  • 业务窗口。需求中提供了6个业务窗口,分三种来对应处理三种类型客户的业务。实际应用中,业务窗口数是可调的,即三种类型业务窗口具有不确定性,快速窗口和VIP窗口也可能被分配为普通窗口,所以把对业务窗口的生成用判断语句来实现,这样可以随意更改窗口类型,一般业务窗口个数都是固定值。在窗口办理业务的时候,是通过叫号来完成。而叫号其实是在调用取号机上的正等待服务的号码,所以取号机上有一个产生号码的方法。
  • 除上诉直观关对象外,对于生成新号码还有一个号码管理器来对其进行管理,这些号码都是正在等待服务的客户号码。学习视屏之后,根据面向对象的思想,可以发现生成新号码这个方法是在调用号码管理器中的属性,所以把号码生成的方法定义在该类中。

        该系统类图:

 三、程序分析

  1、NumberMachine 和NumberManager 两个类是办理窗口业务的前提。

  • 前面分析过取号机,需要被设计成单例,才能满足多线程的数据共享。而共享数据分为三类,分别是普通类型、快速类型和VIP类型,这三类号码都是号码管理器的一个实例。所以将这三类号码定义在取号机NumberMachine  中,保证它们的唯一性。该部分代码比较简单,重点在于能够想到通过单例设计来完成。
  • 通过前面分析号码产生机制,可以发现,号码的生成可以用一个计时器来实现,这里多线程的访问方式是一个在产生(generateNewNumber)号码,一个在取出(fetchNumber)号码,所以这两个方法设计成同步函数。不同线程在调用这两个方法时,当调用其中的一个,另一个方法也会被锁住。号码的存储同交通管理系统中同 Road 类上的车的设计一样,存入集合中,都要求满足先进先出的原则(可以悬在LinkedList,这里换ArrayList ,那么我们只需移除0角标的元素即可)。
      
            号码的生成我们通过计时器来完成,交通管理系统中有详细讲解。号码生成时间比为:VIP客户:普通客户:快速客户 =  1:6:3,若1s生成一个普通客户,那么6s生成一个VIP客户,2s生成一个快速客户。我们的计时器也按照这个时间频率来设置。代码如下:  
    public void getCustomerNumber(final CustomerType type){
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
            new Runnable(){
                @Override
                public void run() {
                    Integer serviceNumber = generateNewNumber();
                    System.out.println("第" + serviceNumber + "号" + type.toString() + "客户正在等待服务...");
                }
            }, 
            0, 
            getPeriod(type), 
            TimeUnit.SECONDS);
    }
    //通过类型判断来传入不同的时间频率
    private long getPeriod(CustomerType type){
        long period = 2147483647;
        long commonTime = Constants.COMMON_CUSTOMER_INTERVIEW_TIME;
        switch (type) {
            case COMMON:
                period = commonTime;
                break;
            case EXPRESS:
                period = commonTime * 2;
                break;
            case VIP:
                period = commonTime * 6;
                break;
        }
        return period;
    } 

            为了提供代码复用性,定义了getPeriod 方法来传递时间频率,这样只需定义一个计时器就可以对三种类型号码进行操作了。这里涉及到了常量的应用,需求中也有说明,即办理业务的最大值和最小值,还有普通客户的的产生频率,若为了体现的更真实,可以将普通客户的产生频率定义为一个随机值,其它两类客户产生频率也随这个随机值变化。这里为了方便,将普通客户的产生频率固定在1s一个。关于常量值的定义也可以通过配置文件的方式来传入,这样操作会更简单,不会涉及到源码。

  2、业务窗口类 ServiceWindow

        该系统设计的第二个难点,关于三种类型窗口的定义。需求中一共有6个窗口,也就是6个线程,该6个线程对普通号码都是共享的。

        一个ServiceWindow 对象即一个线程,首先得定义一个开启线程的方法。这里也用到了线程池工具类 Executors,查阅API 可得在 Executor 接口中有一个开启线程的方法 execute(Runnable command),使用其子接口ScheduledExecutorService 来定义该窗口类型。

        public void start(){
                Executors.newSingleThreadScheduledExecutor().execute(
                        new Runnable() { 
                                public void run() {
                                        while (true) {
                                                if(type.name()=="COMMON")
                                                        commonWindow();
                                                else
                                                        serviceWindow();
                                        }
                                }
                        });
        }

        下面是处理具体的办理类型的声明,这个细节难度很大,容易出错。这块代码涉及到了所有定义过的类,容易搞糊涂,所以主要是弄清细节。原理其实很简单,客户到对应业务窗口办理相关业务,办理业务时间有最小到最大这么一个随机值,业务事件通过sleep 方法来完成。然后就是一个在测试类中循环调用的过程,所以原理简单,但是细节麻烦。先把普通窗口的处理声明写好,方法声明如下:

        private void commonWindow(){
                String windowName = windowID + "号" + type + "窗口";
                System.out.println(windowName + "正在获取普通任务...");
                Integer serviceNumber = NumberMachine.getInstance().getCommonManager().fetchNumber();
                if (serviceNumber != null) {
                        System.out.println(windowName + "开始为" + serviceNumber + "号普通客户服务...");
                        int serviceTime = new Random().nextInt(Constants.MAX_SERVICE_TIME) + 1;
                        try {
                                Thread.sleep(serviceTime * 1000);
                        } catch (InterruptedException e) {
                                throws new RuntimeException(e);
                        }
                        System.out.println(windowName + "完成为" + serviceNumber + "号普通客户服务,耗时" + serviceTime + "秒!");
                } else {
                        System.out.println(windowName + "没有取到普通任务,休息1秒。");
                        try {
                                Thread.sleep(1000);
                        } catch (InterruptedException e) {
                                throws new RuntimeException(e);
                      }
                }
        }

        快速和VIP窗口的声明就是普通型的一个子方式,只需在客户类型上做哈修改就满足条件。需求中还要求快速和VIP窗口也能处理普通业务,所以这两个业务最后需要调用普通业务的方法。快速和VIP业务都属于特殊业务,所以可以通过抽取代码通过客户类型来判断办理业务的类型,这样提高代码的复用性。代法如下:

	//随机服务类型窗口,根据服务类型变更。
	private void serviceWindow(){
		String windowName = windowID + "号" + type + "窗口";
		System.out.println(windowName + "正在获取" + type + "任务...");
		Integer serviceNumber = getServiceNumber();
		if (serviceNumber != null) {
			System.out.println(windowName + "开始为" + serviceNumber + "号" + type + "客户服务...");
			int serviceTime = getServiceTime();
			try {
				Thread.sleep(serviceTime * 1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(windowName + "完成为" + serviceNumber + "号" + type + "客户服务,耗时" + serviceTime + "秒!");
		} else {
			commonWindow();
		}
	}
	/*根据服务类型(客户类型)获取取号机生成的对应类型的号码*/
	private Integer getServiceNumber(){
		Integer serviceNumber = null;
		switch (type) {
			case EXPRESS:
				serviceNumber = NumberMachine.getInstance().getExpressManager().fetchNumber();
				break;
			case VIP:
				serviceNumber = NumberMachine.getInstance().getVIPManager().fetchNumber();
				break;
		default:
			break;
		}
		return serviceNumber;
	}
	/*三种类型的客户办理业务的随机时间不同,但快速客户耗时最少(设置为1s)*/
	private int getServiceTime(){
		int serviceTime;
		if (type == CustomerType.VIP) {
			serviceTime = new Random().nextInt(Constants.MAX_SERVICE_TIME) + 1;
		} else {
			serviceTime = Constants.MIN_SERVICE_TIME;
		}
		return serviceTime;
	}

四、总结

        交通灯系统和银行系统的对比:

  • 相似点:都涉及到集合的应用、枚举的定义和计时器技术。
  • 着重点:交通灯处理的重点是关于 Road 类的简化分析,枚举元素的定义方式和计时器的应用。
                    银行系统的着重点针对客户类型而产生的三种类型的业务号码,计时器的应用以及多线程技术的安全问题。
  • 互比性:交通灯系统处理都是单线程,所以不用考虑安全性。但银行系统用到了多线程,这时就需要考虑安全性,多线程的问题也是交通灯中所没有遇到的,我们在对数据进行加锁时,需要找准需要被锁定的数据,即明确哪些数据是大家共享的。
  • 面向对象:对需求的分析,得把握住所有必须的对象,同时抓住隐藏的对象,这点最难,这个不是一蹴而就的,需要在实际的开发中慢慢积累,见的多了,就会了。就像张老师在所说的,如果没有这方面的经验,他也不知道用这样的方式来进行设计。所以先得多理解前辈的设计思想,在将其应用,然后转换成自己的设计思想。面向对象的分析,记住一点:谁拥有数据,谁就是对外提供操作这些数据的方法。
一、 内容概要 本资源提供了一个完整的“金属板材压弯成型”非线性仿真案例,基于ABAQUS/Explicit或Standard求解器完成。案例精确模拟了模具(凸模、凹模)与金属板材之间的接触、压合过程,直至板材发生塑性弯曲成型。 模型特点:包含完整的模具-工件装配体,定义了刚体约束、通用接触(或面面接触)及摩擦系数。 材料定义:金属板材采用弹塑性材料模型,定义了完整的屈服强度、塑性应变等真实应力-应变数据。 关键结果:提供了成型过程中的板材应力(Mises应力)、塑性应变(PE)、厚度变化​ 云图,以及模具受力(接触力)曲线,完整再现了压弯工艺的力学状态。 二、 适用人群 CAE工程师/工艺工程师:从事钣金冲压、模具设计、金属成型工艺分析与优化的专业人员。 高校师生:学习ABAQUS非线性分析、金属塑性成形理论,或从事相关课题研究的硕士/博士生。 结构设计工程师:需要评估钣金件可制造性(DFM)或预测成型回弹的设计人员。 三、 使用场景及目标 学习目标: 掌握在ABAQUS中设置金属塑性成形仿真的全流程,包括材料定义、复杂接触设置、边界条件与载荷步。 学习如何调试和分析大变形、非线性接触问题的收敛性技巧。 理解如何通过仿真预测成型缺陷(如减薄、破裂、回弹),并与理论或实验进行对比验证。 应用价值:本案例的建模方法与分析思路可直接应用于汽车覆盖件、电器外壳、结构件等钣金产品的冲压工艺开发与模具设计优化,减少试模成本。 四、 其他说明 资源包内包含参数化的INP文件、CAE模型文件、材料数据参考及一份简要的操作要点说明文档。INP文件便于用户直接修改关键参数(如压边力、摩擦系数、行程)进行自主研究。 建议使用ABAQUS 2022或更高版本打开。显式动力学分析(如用Explicit)对计算资源有一定要求。 本案例为教学与工程参考目的提供,用户可基于此框架进行拓展,应用于V型弯曲
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值