PS:稍微讲点题外话,本来猫哥想的是jsp+Servlet培训班作业管理系统就按最最简单的实现,后来发现一句句代码硬敲实在是太费劲,所以折腾出了个后端设计。等设计到本篇所讲的这一版,猫哥发现不能再修改了,因为设计之路是无穷尽的,包容的功能越多就越复杂,最后可能发现自己是在设计一种模式、或者设计一种框架了,没那个必要,因为毕竟目前大家的水平也远远达不到设计框架的水准(当然也可以硬设计一个难用的框架),但是从设计的过程来讲,反而起到了对框架加深理解的作用,无心插柳柳成荫,这个好。纯属PS….
在前一篇中,我们实现了通用CRUD后端的设计,根据设定的规则,针对实体开发增删改查功能时很多重复代码得到了省略。但是遇到非CRUD的功能,比如用户登录,比如查看人员时只查看学生,比如学生选课等等,都是CRUD后端无法实现的,需要另外写Servlet和对应的逻辑,这就导致CRUD后端相当的鸡肋。
那么,为啥导致了鸡肋,我们想要的是大腿啊。其实从设计开始,就目光短浅了,设计之处,就把后端定位为“解决实体对应的增删改查”功能的后端,后边在实现的时候,就是按这个实现的,所以面对超出设计的功能时,实现必然也跟不上。
那么我们重新来设计下整个思路,首先我们把整个网站的作用抽象为“请求-应答”,这个够抽象了吧。然后我们依然借助于Servlet来接受请求,并返回应答,但是我们把Servlet接收到请求后,如何返回应答定义为一个Action(动作)。也就是说Servlet仅承担输入、输出功能,具体逻辑交给Action来处理。
好的,那么Action应该具备一个处理的功能吧,没问题,我们先设计一个接口规范Action需要遵循的标准如下:
package inter;import action.support.ActionContext;import exception.MyException;//Servlet分配任务后Action类应实现的功能public interface IAction { //执行具体动作,并返回消息,期间可能抛出异常 public ActionContext execute()throws MyException;}就IAction
简单的解释几点:
1,IAction是一个接口,接口可以认为是一种规范,实现我这个接口的类必须遵循这种规范。所谓的规范就是要会一种“功夫”,此处的功夫就是execute,即执行具体的动作处理业务逻辑。
2,执行动作必然需要得到相关的信息,同时呢,还得返回处理的结果,这些信息猫哥把他们封装在了ActionContext中,所谓的ActionContext就是动作上下文的意思,上下文的意思就是运行环境,所以翻译为:动作执行所需要的环境。小伙伴们若是将其命名为ActionParam也无可厚非,毕竟萝卜白菜各有所爱,但是猫哥觉得context这个词很是高端,爱不释手啊。
3,既然是执行具体的业务逻辑,肯定可能有异常,比如数据库连接异常,参数异常等等,那么的话就给execute添加一个thows MyException表示“俺办execute这事时可能会出事,不过出事我不管,谁让我办事谁负责”,这个非常合理,主谋负主要责任,杀手么,人家手持免责声明呢,法律意识强,人生就是棒!
OK,看来大家还真不能小瞧了小小的一个接口,接口即是一种程序结构的基础逻辑定义,又是一种可靠的规范(实现接口的类必须实现接口定义的方法),你找个有厨师证的人可能他不会炒菜,你找个实现了IAction规范的类它必然会execute,就是这么叼。
好的,所以不管是什么动作,我就实现IAction接口,然后根据具体的消息作不同的处理就是了。
那么下面一个关键的问题,就是接口中出现的ActionContext ,动作上下文。也就是动作的输入和输出了,前面我们讲了Servlet只管承担输入、输出功能,动作负责接受输入、并输出结果。那么最简单的ActionContext可以设计如下:
public class ActionContext { PRivate Map<String,Object> inputParams=null;//需要携带的参数 private Map<String,Object> outputParams=null;//需要返回的结果 //get set省略...OK,这个太强了,管它Servlet有多少输入参数,直接逐一放在inputParams里面。处理完之后想返回多少结果,直接放outputParams里面。比如删除用户输入参数类似method=delete&entityType=User&entityId=1 那么我么可以
inputParams.put("OperationType","delete");inputParams.put("entityType","User");inputParams.put("entityId",1);好的,实际上猫哥是这样设计的:
package action.support;import java.util.List;import java.util.Map;public class ActionContext { //请求信息,注意因为前四个参数常用,所以猫哥并未把它们放在inputParams里面,实际上也可以放在里面 private String operationType=null;//操作类型比如view add edit save delete 此处可拓展 private String entityType=null;//实体类型 Course Job Lesson Role User Work private String page=null;//页码 private int entityId=-1;//实体对应唯一编号 private Map<String,Object> inputParams=null;//需要携带的参数 //返回信息,注意因为actionUrl基本上都需要返回,所以单独设置一个参数 private String actionUrl=null;//需要跳转到网页 private Map<String,Object> outputParams=null;//需要返回的结果 public Map<String, Object> getInputParams() { return inputParams; } public void setInputParams(Map<String, Object> inputParams) { this.inputParams = inputParams; } public Map<String, Object> getOutputParams() { return outputParams; } public void setOutputParams(Map<String, Object> outputParams) { this.outputParams = outputParams; } public String getEntityType() { return entityType; } public void setEntityType(String entityType) { this.entityType = entityType; } public String getOperationType() { return operationType; } public void setOperationType(String operationType) { this.operationType = operationType; } public int getEntityId() { return entityId; } public void setEntityId(int entityId) { this.entityId = entityId; } public String getPage() { return page; } public void setPage(String page) { this.page = page; } public String getActionUrl() { return actionUrl; } public void setActionUrl(String actionUrl) { this.actionUrl = actionUrl; }}注意哈,之所以变得这么复杂,纯属猫哥把常用的参数从Params中提取出来了,便于快速使用。其中需要特别注意的是private String actionUrl=null;
这个参数,它负责携带动作执行完成后需要显示的网页的地址。
那么问题就来了,所有的Action都需要ActionContext吗,答案是肯定的,因为起码所有的Action都得有个处理结果返回吧,至少得返回Action执行完要跳哪个网页吧,所以所有的Action必然得有个ActionContext属性。OK,那么我们每次写Action都得设置上这个属性,然后实现其set、get方法,烦不烦?
非常烦!!基于避免重复的原则,猫哥新增一类:
package action.support;import inter.IAction;/** * 抽象类Action 目的是为了子类自动具有ActionContext类型的成员 * @author 猫哥 * @date 2017.2.10 * 注意该抽象类继承了IAction接口,所以子类也必遵循IAction标准:实现execute */public abstract class Action implements IAction{ protected ActionContext context; public Action(ActionContext context){ this.context=context; }}这个类的注释已经写的非常清楚了,有了这个类,子类直接就具备ActionContext属性,并且由于该类遵循IAction规范,所以也子类也必须实现execute。
至此基本的设计都完成了,基本流程也清晰如下:
1,Servlet接受参数2,生成参数对应的ActionContext3,交给对应的Action处理4,Action处理完成后,返回带结果的ActionContext5,根据ActionContext返回信息,显示网页其中有两块变化的内容,即2、3,由不同的请求决定了2、3中的内容不一样,所以2和3应该单独放在一个固定的类中处理,并且该类跟请求密切相关,其实最好是形成一个xml配置文件,新增一种context-Action,先保存到配置文件,然后根据配置文件装配context然后调用Action,猫哥为了简单直接写为一个ActionController类,也就是控制逻辑的核心类,跟控制相关的功能放于其中。
好的,所有设计讨论完毕,欲知如何实现,且听下回分解。
新闻热点
疑难解答