----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
项目分析及项目需求:(把需求写进类文件了)
Demand类(项目需求分析)
/**
* 模拟实现银行调度系统逻辑,需求如下:
* 1、银行内有6个业务窗口,1-4窗口为普通窗口,5,号为快速窗口,
* 6号窗口为VIP窗口。
*
* 2、有三种对应类型的客户:vip客户,普通客户,快速客户(办理交费
* 业务的客户)
*
* 3、异步随机生成各种类型的客户,生成各种类型用户的概率比例为;
* vip 客户:普通客户:快速客户 1:6:3
*
* 4、客户办理业务需求时间有最大值和最小值,在该范围内随机设定每个
* VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间
* 为最小值(提示:办理业务的过程可通过线程的方式模式)。
*
* 5、各类型客户在其中对应窗口按顺序依次办理业务。
*
* 6、当VIP(6号)窗口和快速业务(5号)窗口没有客户等待办理业务的时候,
* 这两个窗口可以处理普通客户的业务,而一旦有对应的客户等待办理业务
* 的时候,则优先处理对应客户的业务。
*
* 7、随机生成客户时间间隔以及业务办理时间最大值和最小值自定,可以设置。
*
* 8、不要求实现GUI,只考虑系统逻辑实现,可通过LOG方式展现程序运行结果。
*
* 面向对象的分析与设计:
* 分析:有三种对应类型的客户:VIP客户,普通用户,快速用户,异步生成各种
* 类型的客户,个类型客户在其对应窗口按顺序依次办理业务。
*
* 1、首先,每一个客户其实就是由银行的一个取号机器产生号码的方式来表示的,所以
* 我想到要有一个(号码管理器)对象,让这个对象不断的产生号码,就等于随机生成了
* 客户。
*
* 2、由于有三类客户,每类客户的号码编排都是完全独立的,所以,我想到本系统
* 一共要产生(三个号码管理对象),各自管理一类用户的排队号码,这三个号码管理
* 器对象统一由一个号码机器对象进行管理。这个(号码机器)在整个系统中始终只能有一
* 个所以,他要被设计成(单例)
*
* 分析:各类型客户在其对应窗口按顺序依次办理业务,准确的说,应该是窗口依次叫号。
*
* 1、各个窗口怎么知道该叫那一个号了呢?它一定是文的相应的号码管理器,即
* 服务窗口每次找到号码管理器获取当前要被服务的号码。
*
*
*
*/
package net.csdn.project.bank;
public class Demand {
}
NumberManager类:(号码管理器)
难点分析:同步代码块,(老师写的代码很难区分是不是简单工厂模式,但是我还把它归类与简单工厂模式)我自己把这种类称为总厂,只是我自己的理解。 (由于对数组集合只进行了删除操作,所以感觉用链表集合LinkedList,比较高效。至于线程集合安全问题,使用数据时已经进行了同步互斥处理。)
package net.csdn.project.bank;
import java.util.ArrayList;
import java.util.List;
//根据分析生成不断产生号码(客户)的类。
/**
* 号码管理器
* 1、定义一个用于储存上一个客户号码的成员变量和用于储存所有等待 服务的客户号码的队列集合。
* 2、定义一个产生新号码的方法和
* 3、定义一个获取马上要为之服务的号码的方法,
*
* (由于 这两个方法被不同的线程操作了相同的数据,所以要加同步。)
*/
public class NumberManager {
/**
* 给队列附初始值(1),并不断叠加(以++自增)。
*/
private int lastNumber = 1;
/**
* 创建号码队列数组
*/
private List<Integer> queueNumbers = new ArrayList<Integer>();
/**
*
* 产生一个新的号码,追加到队列里。
* @param 无
* @return Integer
* 当前新创建的号码值 。
*
*/
public synchronized Integer generateNewNumber() {
//把号码追加到队列中。
queueNumbers.add(lastNumber);
return lastNumber++;
}
/**
* 获取当前号码队列中第一个值。
*
* @return Integer
* 号码队列中第一个值。
*
*/
public synchronized Integer fetchNumber() {
//将队列数组中第一个值删除并返回。
if(queueNumbers.size()>0){
return (Integer)queueNumbers.remove(0);
}else{
return null;
}
/*
* 如果返回是int,我集合泛型添加的类型为Integer
* 如果当时集合中没有元素,默认为null,
* 这是我返回int类型会出出现空指针异常。
* 所以修改返回值类型。
* 涉及知识点,自动拆箱装箱。
*/
}
}
NumberMachine类(号码机类)
难点分析: 线程池,(分厂),单例(饿汉式)
package net.csdn.project.bank;
//需求分析:
//1、产生三个号码管理对象的类,用来生成不同类型用户。
//2、这个(号码机器)在整个系统中始终只能有一 个所以,他要被设计成(单例)
//使用的到的技术,工厂模式(简单工厂模式)根据工厂的对象创建,具体的产品。
//但是简单工厂模式违反了高内聚分配责任,所以只有在程序简单的情况下使用。
/**
* 号码机器
* 1、创建三个相同属性的 号码管理对象。
* 2、分别对三个对象,对外提供访问对象的方法,
* 3、使用单例(饿汉式),保证本类对象的唯一性。
*
*/
public class NumberMachine {
//普通用户对象
private NumberManager commonMannger = new NumberManager();
//快速用户对象
private NumberManager expressMannger = new NumberManager();
//VIP用户对象
private NumberManager vipMannger = new NumberManager();
/**
* 对外提供访问普通用户对象的方法。
* @return NumberManager
*
*/
public NumberManager getCommonManager() {
return commonMannger;
}
/**
* 对外提供访问快速用户对象的方法。
* @return NumberManager
*
*/
public NumberManager getExpressManager() {
return expressMannger;
}
/**
* 对外提供访问VIP用户对象的方法。
* @return NumberManager
*
*/
public NumberManager getVipManager() {
return vipMannger;
}
//将此类单例(懒汉式)
/**
* 默认构造私有化
*/
private NumberMachine(){}
//创建本类属性对象
private static NumberMachine instence=new NumberMachine();
public static NumberMachine getInstance(){
return instence;
}
}
CustomerType (枚举窗口元素类)
难点分析:重写枚举的toString()方法,改变其返回值。
package net.csdn.project.bank;
//枚举对象在其他类进行赋值操作时,会直接调用其枚举对象的toString()并返回;
public enum CustomerType {
/**
* COMMON 普通窗口
*/
COMMON,
/**
* EXPRESS 快速窗口
*/
EXPRESS,
/**
* VIP VIP窗口
*/
VIP;
/**
* 重写枚举的toString()当枚举元素被调用时返回自定义值
*
* @return COMMON:普通 EXPRESS:快速 VIP:VIP
*/
public String toString() {
switch (this) {
case COMMON:
return "普通";
case EXPRESS:
return "快速";
case VIP:
return name();
}
return null;
}
}
Constants(常量类)
存放了,需要计算时间的常量数据。
package net.csdn.project.bank;
//常量(时间)类,其实可以成枚举使用
public class Constants {
/**
* @category 最大时间值
*/
public static final int MAX_SERVICE_TIME = 10000; //10秒!(10000为毫秒值)
/**
* @category 最小时间值
*/
public static final int MIN_SERVICE_TIME = 1000; //1秒! (1000为毫秒值)
/**
* @category 普通用户间隔时间值
*/
public static final int COMMON_CUSTOMER_INTERVAL_TIME = 1;
}
ServiceWindow(服务窗口类)
难点分析:用最简单的代码,写出复杂的需求。
窗口义务互换操作,(让VIP窗口空闲的时候,去处理普通用户,如果代码思路不清晰,很容易被绕进去。不是具有开发经验的人,很难想到,用着这种方式实现。降低代码耦合性(非直接耦合),使得在方法在循环里面,还可以调用在循环里的其他方法。)
package net.csdn.project.bank;
import java.util.Random;
import java.util.concurrent.Executors;
//需求分析:
//根据各类型客户在其对应窗口按顺序依次办理业务,准确的说,应该是窗口依次叫号。
// 服务窗口每次找到号码管理器获取当前要被服务的号码。
/**
* 服务窗口
* 1、创建叫号的方法。
*
* 2、创建单个线程池,叫到谁给谁办理业务。
* 由于有不同的客户所以根据不同的客户需求创建不同的方法。
*
*/
public class ServiceWindow {
//枚举窗口类型,初始化值为 普通窗口
private CustomerType type = CustomerType.COMMON;
//初始化窗口数,默认值为1;(主要是为VIP窗口、快速窗口初始化值,在创建普通用户的时候叠加值。
private int number = 1;
/**无参构造
* 默认生成普通窗口,
* 如果想生成其他窗口
* 请
*
*/
public ServiceWindow() {}
/**
* 设置窗口属性。
* @param type 参数类型:CustomerType
* @return 无
*/
public void setType(CustomerType type) {
this.type = type;
}
/**
* 设置窗口号
* @param number 参数类型:int
* @return 无
*/
public void setNumber(int number){
this.number = number;
}
/**在这个方法中启用一个单线程,
* 通过调用此方法,让窗口开始工作,处理业务。
* 至于你想创建什么窗口,由你创建ServiceWindow对象之后,
* 调用 setType(CustomerType type)方法 实现你想创建的业务窗口。
* 如果是无参构造函数创建窗口,默认为 普通窗口
* (注意:用当前对象调用start(),再调用其他方法将不起任何作用。
* @param 无
* @return 无
*/
/*
* 方法内部通过,线程池创建了单个线程。并根据你所设置的窗口属性
* 来创建,所对应窗口的业务流程。
* 所调用的业务有:普通窗口业务,快速窗口业务、VIP窗口业务。
*
*/
public void start(){
Executors.newSingleThreadExecutor().execute(
new Runnable(){
public void run(){
do{
switch(type){
case COMMON:
commonService();
break;
case EXPRESS:
expressService();
break;
case VIP:
vipService();
break;
}
}while(true);
}
}
);
}
/*需求分析:
* 客户办理业务需求时间有最大值和最小值,在该范围内随机设定每个
* VIP客户以及普通客户办理业务所需的时间,快速客户办理业务所需时间
* 为最小值(提示:办理业务的过程可通过线程的方式模式)。
*/
/**
*
* 普通窗口业务实现:
*/
/*分析代码(其实代码看着多但是没东西):
* 1、调用窗口属性,并进行用户交互。
* 2、拿到,用户数列的第一个元素,并将它从用户数列中移除。()
* 3、判断元素是否为空:
* |——否 :开始为用户(serviceNumber)办理业务
* 并计算时间:1、生成随机数一个1000~10000的随机数,并获取maxRandom
* 2、sleep(maxRandom),来实现业务办理所化的时间。
* 3、打印窗口及用户信息,及所用秒数(毫秒/1000)。
* |——是: 打印交互信息,并休眠一秒。
*
*
*/
private void commonService(){
String windowName = "第" + number + "号" + type + "窗口";
System.out.println(windowName + "开始获取普通任务!");
Integer serviceNumber = NumberMachine.getInstance().getCommonManager().fetchNumber();
if(serviceNumber != null ){
System.out.println(windowName + "开始为第" + serviceNumber + "号普通客户服务");
int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
int serviceTime = new Random().nextInt(maxRandom)+1 + Constants.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号普通客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{
System.out.println(windowName + "没有取到普通任务,正在空闲一秒");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
*
* 快速窗口业务实现:
*/
/*分析代码(其实代码看着多但是没东西):
* 1、调用窗口属性,并进行用户交互。
* 2、拿到,用户数列的第一个元素,并将它从用户数列中移除。()
* 3、判断元素是否为空:
* |——否 :开始为用户(serviceNumber)办理业务
* 并计算时间:1、获取最小时间值,
* 2、sleep(MIN_SERVICE_TIME),来实现业务办理所化的时间。
* 3、打印窗口及用户信息,及所用秒数(毫秒/1000)。
* |——是: 1、打印交互信息,
* 2、让当前线程去调用普通窗口业务(实现要求)
*
*
*
*/
private void expressService(){
Integer serviceNumber = NumberMachine.getInstance().getExpressManager().fetchNumber();
String windowName = "第" + number + "号" + type + "窗口";
System.out.println(windowName + "开始获取快速任务!");
if(serviceNumber !=null){
System.out.println(windowName + "开始为第" + serviceNumber + "号快速客户服务");
int serviceTime = Constants.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号快速客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{
System.out.println(windowName + "没有取到快速任务!");
commonService();
}
}
/**
*
* VIP窗口业务实现:
*/
/*
* 此方法,和快速窗口业务 方法一样不再分析。
*/
private void vipService(){
Integer serviceNumber = NumberMachine.getInstance().getVipManager().fetchNumber();
String windowName = "第" + number + "号" + type + "窗口";
System.out.println(windowName + "开始获取VIP任务!");
if(serviceNumber !=null){
System.out.println(windowName + "开始为第" + serviceNumber + "号VIP客户服务");
int maxRandom = Constants.MAX_SERVICE_TIME - Constants.MIN_SERVICE_TIME;
int serviceTime = new Random().nextInt(maxRandom)+1 + Constants.MIN_SERVICE_TIME;
try {
Thread.sleep(serviceTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(windowName + "完成为第" + serviceNumber + "号VIP客户服务,总共耗时" + serviceTime/1000 + "秒");
}else{
System.out.println(windowName + "没有取到VIP任务!");
commonService();
}
}
}
MainClass(创建窗口及排号类)
(万事俱备,只欠东风啊)排号机有了,窗口有了!根据需求,实现功能。
package net.csdn.project.bank;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
//其实可是使用有参构造器,创建ServiceWindow对象,但是没有添加。
/*需求分析:(创建窗口)
* 1、银行内有6个业务窗口,1-4窗口为普通窗口,5,号为快速窗口,
* 6号窗口为VIP窗口。
*
*/
/**
* 实现类:
* 1、创建窗口:通过创建 ServiceWindow 对象实现。(内部使用的是线程池)
* |——创建普通窗口 :创建 4个窗口,使用默认构造器, 给窗口号赋值、开启线程。
* |——创建快速窗口:创建 1个窗口,使用默认构造器,给窗口号赋值、给窗口赋值、开启线程。
* |——创建VIP窗口:创建 1个窗口,使用默认构造器, 给窗口号赋值、给窗口赋值、开启线程。
*/
/*需求分析:
* 异步随机生成各种类型的客户,生成各种类型用户的概率比例为;
* vip 客户:普通客户:快速客户 1:6:3
*/
/**
* 2、使用ScheduledExecutorService(定时器)创建线程实现,用户拿票。(由于定时器已经限定了时间所以基本要求以实现)
* (唯一不同的是,拿到的号不同,定时器(安排线程)限定时间参数不同)
* |——普通客户拿号:(简单工厂模式调用)拿号,界面交互。
* |——普通客户拿号:(简单工厂模式调用)拿号,界面交互。
* |——普通客户拿号:(简单工厂模式调用)拿号,界面交互。
*/
public class MainClass {
public static void main(String[] args) {
// 产生4个普通窗口
for (int i = 1; i < 5; i++) {
ServiceWindow window = new ServiceWindow();
window.setNumber(i);
window.start();
}
// 产生1个快速窗口
ServiceWindow expressWindow = new ServiceWindow();
expressWindow.setNumber(5);
expressWindow.setType(CustomerType.EXPRESS);
expressWindow.start();
// 产生1个VIP窗口
ServiceWindow vipWindow = new ServiceWindow();
vipWindow.setNumber(6);
vipWindow.setType(CustomerType.VIP);
vipWindow.start();
// 普通客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
Integer serviceNumber = NumberMachine.getInstance()
.getCommonManager().generateNewNumber();
System.out.println("第" + serviceNumber + "号普通客户正在等待服务!");
}
}, 0, Constants.COMMON_CUSTOMER_INTERVAL_TIME, TimeUnit.SECONDS);
// 快速客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
Integer serviceNumber = NumberMachine.getInstance()
.getExpressManager().generateNewNumber();
System.out.println("第" + serviceNumber + "号快速客户正在等待服务!");
}
}, 0, Constants.COMMON_CUSTOMER_INTERVAL_TIME * 2, TimeUnit.SECONDS);
// VIP客户拿号
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
Integer serviceNumber = NumberMachine.getInstance()
.getVipManager().generateNewNumber();
System.out.println("第" + serviceNumber + "号VIP客户正在等待服务!");
}
}, 0, Constants.COMMON_CUSTOMER_INTERVAL_TIME * 6, TimeUnit.SECONDS);
}
}
项目 总结:5K 与7K的区别是,熟练度代码。而7K与1W,那就是思想上的不同。
及时知道同样的方法,也不知道,如何使用它。空有宝藏的钥匙而不知道门在哪里(崔老师说的)!