传送门:【设计模式系列(三)】彻底搞懂原型模式
## 需求
1、 有一个程序,会有多个不同的用户并发访问
2、程序中包含多个场景,根据用户首次访问的输入内容匹配得到符合条件的场景
3、不同的场景需要对外提供统一的匹配和处理输入的接口,但处理逻辑和方法又各不相同
4、同一个用户存在多轮次访问的情况,并且场景会根据多轮访问的情况决定当前轮次的处理方法,要求每个用户要有一个独立的场景对象处理用户的输入内容
5、每个用户有一个独立的id标识
## 类图
1、定义一个场景抽象类,具体场景类继承抽象类,并实现初始化方法和处理输入方法
2、抽象类实现Cloneabel接口以实现原型模式,实现Serializable接口以实现对象的深度克隆
3、定义SceneMatchWord类,实现场景的匹配功能
4、定义SceneContext类用来存储场景实例对象,并提供匹配和获取场景的唯一接口方法
5、定义Main类,作为模拟程序的入口接口,通过控制台输入模拟用户输入
## 主要代码
### 场景抽象类
代码语言:javascript复制/**
* 场景超类
* @author daijiyong
*/
abstract public class AbstractScene implements Cloneable, Serializable {
/**
* 场景名字
*/
public SceneNameCode sceneNameCode;
/**
* 当前场景的匹配词
*/
protected LinkedList<SceneMatchWord> matchWords;
public AbstractScene() {
}
public AbstractScene(SceneNameCode sceneNameCode) {
this.sceneNameCode = sceneNameCode;
}
/**
* 对外统一处理操作,子类进行重写
*/
public abstract String deal(String userInput);
/**
* 初始化操作,子类进行重写
*/
protected abstract void init();
/**
* 根据用户输入与当前场景进行匹配
*
* @param userInput 用户输入内容
* @return 匹配结果
*/
public Boolean match(String userInput) {
// TODO
// 根据用户的输入进行匹配
// 匹配成功则返回true,失败则返回false
for (SceneMatchWord smw : matchWords) {
if (smw.match(userInput)) {
return true;
}
}
return false;
}
/**
* 深克隆
*
* @return 克隆结果
*/
@Override
public AbstractScene clone() {
AbstractScene clone = null;
try {
clone = (AbstractScene) super.clone();
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bo);
oos.writeObject(this);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
clone = (AbstractScene) oi.readObject();
} catch (IOException | ClassNotFoundException | CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
### 场景子类
代码语言:javascript复制public class NumberReverseQueryScene extends AbstractScene {
public NumberReverseQueryScene(SceneNameCode sceneNameCode) {
super(sceneNameCode);
init();
}
@Override
public String deal(String userInput) {
// TODO
// 当前场景对用户输入的具体实现
// 并返回结果
return String.format("%s处理用户的输入内容%sn", sceneNameCode.getName(), userInput);
}
/**
* 初始化当前场景数据
*/
@Override
public void init() {
// 初始化匹配内容
matchWords = new LinkedList<>();
//5个或5个以上数字,不含字母,非1非0开头号码
matchWords.add(new SceneMatchWord(Pattern.compile("[2-9]\d{4,}")));
//5个或5个以上数字,不含字母,1开头号码
matchWords.add(new SceneMatchWord(Pattern.compile("1\d{4,}")));
}
}
### 场景匹配规则类
代码语言:javascript复制/**
* 场景匹配词
*
* @author daijiyong
*/
public class SceneMatchWord implements Serializable {
// 匹配规则
private Pattern rule;
/**
* 根据用户输入内容进行匹配
* 匹配成功返回true,失败返回false
*
* @param userInput 用户输入
* @return 匹配成功或失败
*/
public Boolean match(String userInput) {
return matchRule(userInput);
}
private boolean matchRule(String str) {
if (rule == null) {
return false;
}
return rule.matcher(str).find();
}
}
### 场景上下文类
初始化场景对象
调用场景的匹配方法,根据用户的输入为每一个用户创建一个特有的场景对象 并使用ConcurrentHashMap来存储不同用户的场景对象
代码语言:javascript复制public class SceneContext {
private final static Map<String, AbstractScene> CONTEXT = new ConcurrentHashMap<>();
private final static ArrayList<AbstractScene> SCENES = new ArrayList<>();
static {
// 初始化场景集合
SCENES.add(SceneFactory.createNumberReverseQueryScene());
SCENES.add(SceneFactory.createMoveCarScene());
SCENES.add(SceneFactory.createAreaCodeReverseQueryScene());
}
public static AbstractScene currentScene(String queryId, String userInput) {
if (getContext().get(queryId) == null && SCENES.size() > 0) {
for (AbstractScene as : SCENES) {
if (as.match(userInput)) {
CONTEXT.put(queryId, as.clone());
System.out.printf("用户%s匹配到场景:%sn", queryId, as.sceneNameCode.getName());
return getContext().get(queryId);
}
}
}
return getContext().get(queryId);
}
public static Map<String, AbstractScene> getContext() {
return CONTEXT;
}
}
### 模拟程序入口类
代码语言:javascript复制/*
模拟入口程序
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
// 输入两个内容,用户id和用户请求内容,中间用空格隔开
String input = scanner.nextLine();
String[] inputArr = input.split(" ");
AbstractScene scene = SceneContext.currentScene(inputArr[0], inputArr[1]);
System.out.println(scene.deal(inputArr[1]));
}
}
}
运行程序,可实现为每个用户创建独立的场景对象
点击阅读原文访问完整代码地址
包路径:com/dai/designpattern/prototype/practice
文/戴先生@2021年9月13日
---end---