企业开发中,除了功能性需求(Functional Requirement)之外,非功能性需求(Non Functional Requirement)也同样重要。在非功能性需求中描述了项目的诸多系统性质量(Systemic Quality)。这些质量包括了灵活性、可维护性、可扩展性。大师编写的代码,质量往往比较高。很多新手在编写项目的同时,bug定位难、经常返工,归根结底在于:不注重设计。
如果新手与大师之间必然要有一道分水岭,那么这一道分水岭,唯系统设计这一个能力,就足以说明问题了。然而 系统设计能力,又应该怎样去提升呢?实际上不论任何行业,学习技能的诀窍不外乎三点: 守、破、离,也就是: 模仿、突破、开拓。那么首先要做的,就是模仿。
所谓设计,我们要模仿的,是前辈们在系统设计时,为我们总结好的经验;以及前辈们在系统设计时,遇到问题后,如何利用模式解决这些问题的。也就是——设计模式。
什么叫设计呢?设计的英文——Design。
在旅游之前,需要规划好出行路线;需要准备好常用药;需要打包好换洗衣服、洗漱化妆用品。
开始一天的工作之前,列好今日工作任务清单
演讲之前,写好草稿
...
以上的这些,都叫做设计。
那么程序员们所说的设计,又是什么呢?这里的设计,即OOD(Object-Oriented Design)——面向对象设计。也就是在实际地编写代码之前,先要规划好思路,不管你的思路到底有没有落到图纸上,哪怕只是打一个腹稿,这也叫做设计!
注: OOD与之对应的又有OOA(Object-Oriented Analysis)——面向对象分析,以及OOP(Object-Oriented Programming)——面向对象编程。它们之间的顺序是: 先分析;再设计;最后编码
什么又叫模式呢?模式的英文——Pattern
- 当篮球运动员带着篮球,到达了三分线的位置时,为了能够让篮球投到篮框里,手指、手臂将使用多大的力量——这已经形成了他自己的模式
- 厨师在在做菜时,要先煮米饭,再炒菜;要先把菜、肉、各类食材调料准备好,再热锅放油
- ...
这些都是生活以及工作、学习中,你去解决某些问题时,所形成的模式。
那么如何一句话概括:什么是模式?模式就是: 通用问题的通用解决方案。
RESTful接口,想必大部分的Java程序员都接触过。当我们使用大篇幅的if - else解决问题时、当我们大段大段地复制粘贴我们的代码时,我们有没有思考过,是否可以更优雅地解决这些问题呢?
本篇文章,将籍由常见的RESTful接口,为大家讲解三个设计模式:
本篇使用的项目,将采用Maven + Spring Boot来搭建,其中maven配置文件及Spring Boot启动类如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.futureweaver</groupId>
<artifactId>sample</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.futureweaver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
比如一个系统里有三个接口:
/user/login
登录
/user/signout
注册
/user/forget
忘记密码
对客户端调用后的响应信息来说,不论调用的是哪个方法,系统中都会有两个通用的字段:
code
响应码。
方法执行正确,即为0
方法执行如果出错,即非0值。
客户端在接收到响应码之后,如果是0,走常规流程;如果非0,走异常流程。
msg
响应信息
每个响应码对应一条响应信息。一般情况下是英文。
客户端可以利用国际化技术,将英文转换成中文
响应信息便于客户端调试
基于以上的信息,大部分程序员们的设计将会是这样的:
code、msg

package com.futureweaver.controller;
import com.futureweaver.response.ForgetPasswordResp;
import com.futureweaver.response.LoginResp;
import com.futureweaver.response.SignoutResp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(path = "/user")
public class UserController {
// 忘记密码接口
@RequestMapping(path = "/forget_password", method = RequestMethod.POST)
@ResponseBody
public ForgetPasswordResp forgetPassword() {
ForgetPasswordResp result = new ForgetPasswordResp();
// 业务处理
// if (用户名不存在) {
// result.code = 2230;
// result.msg = "user not found";
// return result;
// } else if (...) {
// ...
// return result;
// }
// ...
result.code = 0;
result.msg = "success";
return result;
}
// 登录接口
@RequestMapping(path = "/login", method = RequestMethod.POST)
@ResponseBody
public LoginResp login() {
LoginResp result = new LoginResp();
// 业务处理
// if (用户名不存在) {
// result.code = 2230;
// result.msg = "user not found";
// return result;
// } else if (...) {
// ...
// return result;
// }
// ...
result.code = 0;
result.msg = "success";
return result;
}
// 注册接口
@RequestMapping(path = "/signout", method = RequestMethod.POST)
@ResponseBody
public SignoutResp signout() {
SignoutResp result = new SignoutResp();
// 业务处理
// if (用户名不存在) {
// result.code = 2230;
// result.msg = "user not found";
// return result;
// } else if (...) {
// ...
// return result;
// }
// ...
result.code = 0;
result.msg = "success";
return result;
}
}
package com.futureweaver.response;
// 除了msg和code之外,无需向客户端响应任何信息
// 客户端如果接收到了code=0,则默认为接收到了重置密码的邮件
public class ForgetPasswordResp {
public String msg;
public int code;
public ForgetPasswordResp() {
}
public ForgetPasswordResp(String msg, int code) {
this.msg = msg;
this.code = code;
}
@Override
public String toString() {
return "ForgetPasswordResp{" +
"msg='" + msg + '\'' +
", code=" + code +
'}';
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
package com.futureweaver.response;
public class LoginResp {
public String token;
public String msg;
public int code;
public LoginResp() {
}
public LoginResp(String token, String msg, int code) {
this.token = token;
this.msg = msg;
this.code = code;
}
@Override
public String toString() {
return "LoginResp{" +
"token='" + token + '\'' +
", msg='" + msg + '\'' +
", code=" + code +
'}';
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
package com.futureweaver.response;
public class SignoutResp {
public String token;
public String msg;
public int code;
public SignoutResp() {
}
public SignoutResp(String token, String msg, int code) {
this.token = token;
this.msg = msg;
this.code = code;
}
@Override
public String toString() {
return "SignoutResp{" +
"token='" + token + '\'' +
", msg='" + msg + '\'' +
", code=" + code +
'}';
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
接口约束如果想换成:
所有的接口函数都得重新改一遍
Spring切面,对所有的正常返回施加干预code及msg抽离出来,封装成ResponseCommon。Response,复用ResponseCommon。在组合复用与继承复用两个选项中,选择组合复用jackson中的JsonUnwrapped缓解
package com.futureweaver.controller;
import com.futureweaver.response.ForgetPasswordResp;
import com.futureweaver.response.LoginResp;
import com.futureweaver.response.SignoutResp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(path = "/user")
public class UserController {
@RequestMapping(path = "/forget_password", method = RequestMethod.POST)
@ResponseBody
public ForgetPasswordResp forgetPassword() {
ForgetPasswordResp result = new ForgetPasswordResp();
// 业务处理
// 将以下代码删除
// result.code = 0;
// result.msg = "success";
return result;
}
@RequestMapping(path = "/login", method = RequestMethod.POST)
@ResponseBody
public LoginResp login() {
LoginResp result = new LoginResp();
// 业务处理
// 将以下代码删除
// result.code = 0;
// result.msg = "success";
return result;
}
@RequestMapping(path = "/signout", method = RequestMethod.POST)
@ResponseBody
public SignoutResp signout() {
SignoutResp result = new SignoutResp();
// 业务处理
// 将以下代码删除
// result.code = 0;
// result.msg = "success";
return result;
}
}
package com.futureweaver.response;
// 除了msg和code之外,无需向客户端响应任何信息
// 客户端如果接收到了code=0,则默认为接收到了重置密码的邮件
public class ForgetPasswordResp {
public ResponseCommon responseCommon;
public ForgetPasswordResp() {
}
public ForgetPasswordResp(ResponseCommon responseCommon) {
this.responseCommon = responseCommon;
}
@Override
public String toString() {
return "ForgetPasswordResp{" +
"responseCommon=" + responseCommon +
'}';
}
public ResponseCommon getResponseCommon() {
return responseCommon;
}
public void setResponseCommon(ResponseCommon responseCommon) {
this.responseCommon = responseCommon;
}
}
package com.futureweaver.response;
public class LoginResp {
public String token;
public ResponseCommon responseCommon;
public LoginResp() {
}
public LoginResp(String token, ResponseCommon responseCommon) {
this.token = token;
this.responseCommon = responseCommon;
}
@Override
public String toString() {
return "LoginResp{" +
"token='" + token + '\'' +
", responseCommon=" + responseCommon +
'}';
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public ResponseCommon getResponseCommon() {
return responseCommon;
}
public void setResponseCommon(ResponseCommon responseCommon) {
this.responseCommon = responseCommon;
}
}
package com.futureweaver.response;
public class SignoutResp {
public String token;
public ResponseCommon respnoseCommon;
public SignoutResp() {
}
public SignoutResp(String token, ResponseCommon respnoseCommon) {
this.token = token;
this.respnoseCommon = respnoseCommon;
}
@Override
public String toString() {
return "SignoutResp{" +
"token='" + token + '\'' +
", respnoseCommon=" + respnoseCommon +
'}';
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public ResponseCommon getRespnoseCommon() {
return respnoseCommon;
}
public void setRespnoseCommon(ResponseCommon respnoseCommon) {
this.respnoseCommon = respnoseCommon;
}
}
package com.futureweaver.response;
public class ResponseCommon {
private String msg;
private int code;
public ResponseCommon() {
}
public ResponseCommon(String msg, int code) {
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
package com.futureweaver.config;
import java.lang.reflect.Field;
import com.futureweaver.response.ResponseCommon;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class ResponseCommonAssembler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
if (returnType.getMethod().getReturnType().equals(void.class)) {
return true;
}
Field[] fields = returnType.getMethod().getReturnType().getDeclaredFields();
for (Field fieldItem : fields) {
if (fieldItem.getType().equals(ResponseCommon.class)) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
Field[] fields = returnType.getMethod().getReturnType().getDeclaredFields();
for (Field fieldItem : fields) {
if (fieldItem.getType().equals(ResponseCommon.class)) {
fieldItem.setAccessible(true);
ResponseCommon responseCommon = (ResponseCommon) ReflectionUtils.getField(fieldItem, body);
if (responseCommon == null) {
responseCommon = new ResponseCommon();
ReflectionUtils.setField(fieldItem, body, responseCommon);
}
if (responseCommon.getCode() == 0) {
responseCommon.setMsg("success");
}
}
}
return body;
}
}
回过头再查看一下ForgetPasswordResp.java,除了一个responseCommon属性之外,再无其它;对于UserController.java中的forgetPassword接口方法,也无需处理其他的业务。非要给它安排一个ForgetPasswordResp响应体,是不是多此一举?
按正常的套路,把方法的返回值改成void就可以了。但是在ResponseCommonAssembler中,我们响应体所有的属性,找到ResponseCommon字段,再将0和success填充进去。把响应体改成了void之后,就没有ResponseCommon字段了,怎么玩?
在ResponseCommonAssembler中,添加"函数返回类型是否为void"的条件判断,直接创建一个新的ResponseCommon对象,将code填充为0,将msg填充为"success",并将这个对象直接返回。
package com.futureweaver.controller;
import com.futureweaver.response.ForgetPasswordResp;
import com.futureweaver.response.LoginResp;
import com.futureweaver.response.SignoutResp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(path = "/user")
public class UserController {
@RequestMapping(path = "/forget_password", method = RequestMethod.POST)
@ResponseBody
public void forgetPassword() {
// 业务处理
// 将以下代码删除
// result.code = 0;
// result.msg = "success";
}
@RequestMapping(path = "/login", method = RequestMethod.POST)
@ResponseBody
public LoginResp login() {
LoginResp result = new LoginResp();
// 业务处理
// 将以下代码删除
// result.code = 0;
// result.msg = "success";
return result;
}
@RequestMapping(path = "/signout", method = RequestMethod.POST)
@ResponseBody
public SignoutResp signout() {
SignoutResp result = new SignoutResp();
// 业务处理
// 将以下代码删除
// result.code = 0;
// result.msg = "success";
return result;
}
}
package com.futureweaver.config;
import java.lang.reflect.Field;
import com.futureweaver.response.ResponseCommon;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class ResponseCommonAssembler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
if (returnType.getMethod().getReturnType().equals(void.class)) {
return true;
}
Field[] fields = returnType.getMethod().getReturnType().getDeclaredFields();
for (Field fieldItem : fields) {
if (fieldItem.getType().equals(ResponseCommon.class)) {
return true;
}
}
return false;
}
@SuppressWarnings("unchecked")
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
if (returnType.getMethod().getReturnType().equals(void.class)) {
ResponseCommon responseCommon = new ResponseCommon();
responseCommon.setMsg("success");
return responseCommon;
}
Field[] fields = returnType.getMethod().getReturnType().getDeclaredFields();
for (Field fieldItem : fields) {
if (fieldItem.getType().equals(ResponseCommon.class)) {
fieldItem.setAccessible(true);
ResponseCommon responseCommon = (ResponseCommon) ReflectionUtils.getField(fieldItem, body);
if (responseCommon == null) {
responseCommon = new ResponseCommon();
ReflectionUtils.setField(fieldItem, body, responseCommon);
}
if (responseCommon.getCode() == 0) {
responseCommon.setMsg("success");
}
}
}
return body;
}
}
现在的问题是,我们并不能保证,客户端的每一次请求,都会得到一个成功的响应。那么当用户请求出问题时,又该如何解决呢?
异常,并不是系统或者框架才能够使用的。当用户请求后,参数填写错误、用户名不存在等错误亦可使用异常机制处理。
依然利用Spring切面,对所有的异常返回施加干预
package com.futureweaver.config;
import com.futureweaver.exception.ExceptionCommon;
import com.futureweaver.response.ResponseCommon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* 异常拦截器
*
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 自定义异常
*
* @see <code>com.futureweaver.exception.*</code>
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = ExceptionCommon.class)
@ResponseBody
public ResponseCommon commonExceptionHandler(HttpServletRequest req, ExceptionCommon e) {
return e.getResponseCommon();
}
}
package com.futureweaver.exception;
import com.futureweaver.response.ResponseCommon;
public class ExceptionCommon extends Exception {
private static final long serialVersionUID = 3867913775058901502L;
private ResponseCommon responseCommon;
public ExceptionCommon() {
super();
}
public ExceptionCommon(ResponseCommon responseCommon) {
super();
this.responseCommon = responseCommon;
}
public ExceptionCommon(Integer respCode, String respMsg) {
super();
responseCommon = new ResponseCommon();
responseCommon.setCode(respCode);
responseCommon.setMsg(respMsg);
}
public ResponseCommon getResponseCommon() {
return responseCommon;
}
public void setResponseCommon(ResponseCommon responseCommon) {
this.responseCommon = responseCommon;
}
}
package com.futureweaver.controller;
import com.futureweaver.response.ForgetPasswordResp;
import com.futureweaver.response.LoginResp;
import com.futureweaver.response.SignoutResp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(path = "/user")
public class UserController {
@RequestMapping(path = "/forget_password", method = RequestMethod.POST)
@ResponseBody
public void forgetPassword() throws ExceptionCommon {
// 异常校验,如果校验失败,则抛出异常
// 业务处理
}
@RequestMapping(path = "/login", method = RequestMethod.POST)
@ResponseBody
public LoginResp login() throws ExceptionCommon {
LoginResp result = new LoginResp();
// 异常校验,如果校验失败,则抛出异常
// 业务处理
return result;
}
@RequestMapping(path = "/signout", method = RequestMethod.POST)
@ResponseBody
public SignoutResp signout() throws ExceptionCommon {
SignoutResp result = new SignoutResp();
// 异常校验,如果校验失败,则抛出异常
// 业务处理
return result;
}
}
科普一下装饰器模式吧
动态地给一个对象添加一些额外的职责。——《设计模式 - 可复用面向对象软件的基础》
想必大部分的程序员在设计功能的时候,直接把增删改查做成四个接口方法了。设计得再稍微复杂一点,可以批量增、批量删、批量改。
查询功能,用户提交的请求数据,只需要带一个token过来就可以了。所以暂只考虑增、删、改功能

package com.futureweaver.controller;
import com.futureweaver.exception.ExceptionCommon;
import com.futureweaver.request.Contact;
import com.futureweaver.request.ContactModifyRequest;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(path = "/manage")
public class ManageController {
@Autowired
private UserMapper userMapper;
@Autowired
private ContactMapper contactMapper;
@ResponseBody
@RequestMapping(path = "/contact_add", method = RequestMethod.POST)
public void contactAdd(@RequestBody ContactModifyRequest model) throws ExceptionCommon {
User user = userMapper.queryByToken(model.getUserToken());
// 判断用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserNotNull(user);
// 判断用户是管理员,若不是管理员,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserIsManager(user);
for (Contact contact : model.getContacts()) {
// 判断被添加的用户不存在,若已存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactIsNull(contact);
contactMapper.addContact(contact);
}
}
@ResponseBody
@RequestMapping(path = "/contact_del", method = RequestMethod.POST)
public void contactDel(@RequestBody ContactModifyRequest model) throws ExceptionCommon {
User user = userMapper.queryByToken(model.getUserToken());
// 判断用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserNotNull(user);
// 判断用户是管理员,若不是管理员,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserIsManager(user);
for (Contact contact : model.getContacts()) {
// 判断被添加的用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactNotNull(contact);
contactMapper.delContact(contact);
}
}
@ResponseBody
@RequestMapping(path = "/contact_mod", method = RequestMethod.POST)
public void contactMod(@RequestBody ContactModifyRequest model) throws ExceptionCommon {
User user = userMapper.queryByToken(model.getUserToken());
// 判断用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserNotNull(user);
// 判断用户是管理员,若不是管理员,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserIsManager(user);
for (Contact contact : model.getContacts()) {
// 判断被添加的用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactNotNull(contact);
contactMapper.modContact(contact);
}
}
}
package com.futureweaver.request;
public class Contact {
private String contactId;
private String mobile;
private String email;
private String avatar;
private String birthday;
private String birthplace;
public Contact() {
}
@Override
public String toString() {
return "Contact{" +
"userId='" + contactId + '\'' +
", mobile='" + mobile + '\'' +
", email='" + email + '\'' +
", avatar='" + avatar + '\'' +
", birthday='" + birthday + '\'' +
", birthplace='" + birthplace + '\'' +
'}';
}
public String getContactId() {
return contactId;
}
public void setContactId(String contactId) {
this.contactId = contactId;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getBirthplace() {
return birthplace;
}
public void setBirthplace(String birthplace) {
this.birthplace = birthplace;
}
}
package com.futureweaver.request;
import java.util.List;
public class ContactModifyRequest {
private String token;
private List<Contact> contacts;
public ContactModifyRequest() {
}
public ContactModifyRequest(String token, List<Contact> contacts) {
this.token = token;
this.contacts = contacts;
}
@Override
public String toString() {
return "ContactModifyRequest{" +
"token='" + token + '\'' +
", contacts=" + contacts +
'}';
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public List<Contact> getContacts() {
return contacts;
}
public void setContacts(List<Contact> contacts) {
this.contacts = contacts;
}
}

package com.futureweaver.request;
public class Contact {
private String contactId;
private String mobile;
private String email;
private String avatar;
private String birthday;
private String birthplace;
private String operationType;
public Contact() {
}
@Override
public String toString() {
return "Contact{" +
"userId='" + contactId + '\'' +
", mobile='" + mobile + '\'' +
", email='" + email + '\'' +
", avatar='" + avatar + '\'' +
", birthday='" + birthday + '\'' +
", birthplace='" + birthplace + '\'' +
", operationType='" + operationType + '\'' +
'}';
}
public String getContactId() {
return contactId;
}
public void setContactId(String contactId) {
this.contactId = contactId;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getBirthplace() {
return birthplace;
}
public void setBirthplace(String birthplace) {
this.birthplace = birthplace;
}
public String getOperationType() {
return operationType;
}
public void setOperationType(String operationType) {
this.operationType = operationType;
}
}
package com.futureweaver.controller;
import com.futureweaver.exception.ExceptionCommon;
import com.futureweaver.request.Contact;
import com.futureweaver.request.ContactModifyRequest;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(path = "/manage")
public class ManageController {
@Autowired
private UserMapper userMapper;
@Autowired
private ContactMapper contactMapper;
@ResponseBody
@RequestMapping(path = "/contact_mod", method = RequestMethod.POST)
public void contactMod(@RequestBody ContactModifyRequest model) throws ExceptionCommon {
User user = userMapper.queryByToken(model.getUserToken());
// 判断用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserNotNull(user);
// 判断用户是管理员,若不是管理员,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserIsManager(user);
for (Contact contact : model.getContacts()) {
if (contact.getOperationType().equals("add")) {
// 判断被添加的用户不存在,若已存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactIsNull(contact);
contactMapper.addContact(contact);
} else if (contact.getOperationType().equals("del")) {
// 判断被添加的用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactNotNull(contact);
contactMapper.delContact(contact);
} else /* if (contact.getOperationType().equals("mod")) */ {
// 判断被添加的用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactNotNull(contact);
contactMapper.modContact(contact);
}
}
}
}
if - else搞得这么多,菜鸟们总是很愿意这么搞,经常会一个方法写得很长。可维护性、可读性都会很差。利用策略模式,将每一个条件分支封装成策略。
利用简单工厂模式,由所有策略类的父类,根据操作类别,创建具体策略。

package com.futureweaver.controller;
import com.futureweaver.controller.strategy.AbstractContactModifyStrategy;
import com.futureweaver.exception.ExceptionCommon;
import com.futureweaver.request.Contact;
import com.futureweaver.request.ContactModifyRequest;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(path = "/manage")
public class ManageController {
@Autowired
private UserMapper userMapper;
@Autowired
private ContactMapper contactMapper;
@ResponseBody
@RequestMapping(path = "/contact_mod", method = RequestMethod.POST)
public void contactMod(@RequestBody ContactModifyRequest model) throws ExceptionCommon {
User user = userMapper.queryByToken(model.getUserToken());
// 判断用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserNotNull(user);
// 判断用户是管理员,若不是管理员,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateUserIsManager(user);
for (Contact contact : model.getContacts()) {
AbstractContactModifyStrategy strategy = AbstractContactModifyStrategy.strategyWithOperationType(contact.getOperationType());
strategy.operate(contact);
}
}
}
package com.futureweaver.controller.strategy;
import com.futureweaver.request.Contact;
public abstract class AbstractContactModifyStrategy {
public final static AbstractContactModifyStrategy strategyWithOperationType(String operationType) throws ExceptionCommon {
if (operationType.equals("add")) {
return new AbstractContactAddStrategy();
} else if (operationType.equals("del")) {
return new AbstractContactDelStrategy();
} else /* if (operationType.equals("mod")) */ {
return new AbstractContactModStrategy();
}
}
public abstract void operate(Contact contact);
}
package com.futureweaver.controller.strategy;
import com.futureweaver.request.Contact;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
public class ContactAddStrategy extends AbstractContactModifyStrategy {
@Autowired
private ContactMapper contactMapper;
@Override
public void operate(Contact contact) throws ExceptionCommon {
// 判断被添加的用户不存在,若已存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactIsNull(contact);
contactMapper.addContact(contact);
}
}
package com.futureweaver.controller.strategy;
import com.futureweaver.request.Contact;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
public class ContactDelStrategy extends AbstractContactModifyStrategy {
@Autowired
private ContactMapper contactMapper;
@Override
public void operate(Contact contact) throws ExceptionCommon {
// 判断被添加的用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactNotNull(contact);
contactMapper.delContact(contact);
}
}
package com.futureweaver.controller.strategy;
import com.futureweaver.request.Contact;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
public class ContactModStrategy extends AbstractContactModifyStrategy {
@Autowired
private ContactMapper contactMapper;
@Override
public void operate(Contact contact) throws ExceptionCommon {
// 判断被添加的用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactNotNull(contact);
contactMapper.modContact(contact);
}
}
if - else条件判断了。但是依然把if - else延迟到了AbstractContactModifyStrategy当中。即: 每添加一个策略,都需要对AbstractContactModifyStrategy进行修改@Component,当Spring扫描到此组件时,会自动加载这个类。这个类的static就会被执行了package com.futureweaver.controller.strategy;
import com.futureweaver.request.Contact;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public abstract class AbstractContactModifyStrategy {
private static Map<String, Class<? extends AbstractContactModifyStrategy>> map = new HashMap<>();
public final static AbstractContactModifyStrategy strategyWithOperationType(String operationType) throws IllegalAccessException, InstantiationException {
return map.get(operationType).newInstance();
}
public final static void registerClass(String operateType, Class<? extends AbstractContactModifyStrategy> clz) {
map.put(operateType, clz);
}
public abstract void operate(Contact contact);
}
package com.futureweaver.controller.strategy;
import com.futureweaver.request.Contact;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ContactAddStrategy extends AbstractContactModifyStrategy {
@Autowired
private ContactMapper contactMapper;
@Override
public void operate(Contact contact) {
// 判断被添加的用户不存在,若已存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactIsNull(contact);
contactMapper.addContact(contact);
}
static {
AbstractContactModifyStrategy.registerClass("add", ContactAddStrategy.class);
}
}
package com.futureweaver.controller.strategy;
import com.futureweaver.request.Contact;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
public class ContactDelStrategy extends AbstractContactModifyStrategy {
@Autowired
private ContactMapper contactMapper;
@Override
public void operate(Contact contact) {
// 判断被添加的用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactNotNull(contact);
contactMapper.delContact(contact);
}
static {
AbstractContactModifyStrategy.registerClass("del", ContactDelStrategy.class);
}
}
package com.futureweaver.controller.strategy;
import com.futureweaver.request.Contact;
import com.futureweaver.validation.LogicValidation;
import org.springframework.beans.factory.annotation.Autowired;
public class ContactModStrategy extends AbstractContactModifyStrategy {
@Autowired
private ContactMapper contactMapper;
@Override
public void operate(Contact contact) {
// 判断被添加的用户已存在,若不存在,则抛异常(由LogicValidation负责抛异常)
LogicValidation.validateContactContactNotNull(contact);
contactMapper.modContact(contact);
}
static {
AbstractContactModifyStrategy.registerClass("mod", ContactModStrategy.class);
}
}
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。——《设计模式 - 可复用面向对象软件的基础》
将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。
本文中,第二部分 version 0.3,多条
if - else,即使用了简单工厂,用于创建策略类。
本篇介绍了三个模式: 装饰器模式、策略模式、简单工厂模式
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最