问题场景:项目中需要实现PC端和APP端推广二维码的功能,其实采用一种简单的实现方式就是使用if-else判断是使用PC端还APP端的逻辑,这种做法虽然可以实现,但是如果再加上Mac端,H5端等等就需要在原代码上添加很多的if,代码看起来很不优雅而且修改了原代码,基于对修改关闭对扩展开放的原则,我使用了工厂模式 + 策略模式 + 模板方法模式来实现这一场景。
设备工厂类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class DeviceFactory {
private static Map<String, AbstractDevice> strategyMap = Maps.newHashMap();
public static AbstractDevice getInvokeStrategy(String deviceName) { return strategyMap.get(deviceName); } public static void register(String deviceName, AbstractDevice abstractDevice) { if (StringUtils.isEmpty(deviceName) || null == abstractDevice) { return; } strategyMap.put(deviceName, abstractDevice); } }
|
抽象设备类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| public abstract class AbstractDevice implements InitializingBean {
private static Logger logger = LoggerFactory.getLogger(AppHandler.class);
private static final String URL = "https://openerp.banksteel.com/m/register.html"; private static final String MOBILE = "mobile"; private static final String INVITE_CONTENT_UP = "邀请码: "; private static final String INVITE_CONTENT_DOWN = "邀请人: ";
@Autowired private UserInfoService userInfoService; @Autowired private SysConfigService sysConfigService;
public InviteResultVO getQRCode(InviteUserVO inviteUserVO, String device) { throw new UnsupportedOperationException(); }
public List<UserInfo> getUserInfoByMobile(String mobile) { SaasParameter.setMemberId("-1"); Map<String, Object> map = new HashMap<>(); map.put(MOBILE, mobile); return userInfoService.queryByMap(map); }
public InviteResultVO getURLAndAssembleInfo(Long userId, String userName, String device) { byte[] logoFileByte; try { ClassPathResource classPathResource = new ClassPathResource("image/SaaS-logo.png"); logoFileByte = FileCopyUtils.copyToByteArray(classPathResource.getInputStream()); int length = logoFileByte.length; logger.info("=================getInviteQR-errorlength:{}" + length); } catch (Exception e) { logger.error("================getInviteQR-error:{}", e); throw new ParamsValidException(e); } String url = URL + "?inviteId=" + userId + "?device=" + device; SysConfig envType = sysConfigService.findSysConfigByKey("environmentType"); String value ; if (envType == null ) { value = ""; } else { if (!org.apache.commons.lang3.StringUtils.isBlank(envType.getValue()) && envType.getValue().length() > 2) { value = envType.getValue().substring(0, 2); } else { value = ""; } } String qRBase64 = QrCodeUtils.generateQrCodeAsBase64(url, logoFileByte, " " + INVITE_CONTENT_UP + userId + value, " "+INVITE_CONTENT_DOWN + userName); logger.error("================getInviteQR-qRBase64:{}", qRBase64); InviteResultVO resultVO = new InviteResultVO(); resultVO.setInviteCode(qRBase64); resultVO.setInviteUrl(url); return resultVO; } }
|
APP设备类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Component public class AppHandler extends AbstractDevice {
@Override public InviteResultVO getQRCode(InviteUserVO inviteUserVO, String device) { List<UserInfo> userInfos = super.getUserInfoByMobile(inviteUserVO.getMobile()); if (CollectionUtils.isEmpty(userInfos)) { throw new ParamsValidException("获取用户信息异常!"); } return super.getURLAndAssembleInfo(userInfos.get(0).getId(), userInfos.get(0).getName(), device); }
@Override public void afterPropertiesSet() throws Exception { DeviceFactory.register("app", this); } }
|
PC设备类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @Component public class PcHandler extends AbstractDevice{
@Autowired private UserRegisterAO userRegisterAO;
@Override public InviteResultVO getQRCode(InviteUserVO inviteUserVO, String device) { List<UserInfo> userInfos = super.getUserInfoByMobile(inviteUserVO.getMobile()); Long userId; String userName; if (CollectionUtils.isEmpty(userInfos)) { userId = userRegisterAO.quickRegister(inviteUserVO); userName = inviteUserVO.getUserName(); } else { userRegisterAO.checkVerifyCode(inviteUserVO.getImgCheckCode(), inviteUserVO.getImgVerifyCode()); userRegisterAO.checkMobilCheckCode(inviteUserVO.getMobile(), inviteUserVO.getMobilCheckCode(), SmsConstants.QUICK_MOBILE_CHECKCODE_REGISTER); userId = userInfos.get(0).getId(); userName = userInfos.get(0).getName(); } return super.getURLAndAssembleInfo(userId, userName, device); }
@Override public void afterPropertiesSet() throws Exception { DeviceFactory.register("pc", this); } }
|
调用COntroller类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Controller @RequestMapping("/user/register") @Api(tags = {"用户相关"}) public class UserRegisterController extends BaseController {
@ResponseBody @RequestMapping(value = "/get/device/inviteQR", method = RequestMethod.POST) @WriteBuzLog(buzModel = "用户管理", buzName = "获取二维码") @ApiOperation(value = "获取PC端和APP端邀请二维码", httpMethod = "POST", consumes = "application/json;charset=UTF-8",notes = "commond命令:get_device_invite_QR") @PermissionSource(command = "get_device_invite_QR", level=PermissionLevel.ALL) public ResponseEntity<InviteResultVO> getDeviceInviteQR(@RequestBody @Valid InviteUserVO inviteUserVO, @ApiParam(name = "device", value = "PC端或者APP端",required = true) @RequestParam String device) { AbstractDevice strategy = DeviceFactory.getInvokeStrategy(device); return returnSuccess(200, "生成二维码成功", strategy.getQRCode(inviteUserVO, device)); } }
|
总结:
以上实现方式优点是:如果需要添加一个Mac端只需要添加一个类去继承AbstractDevice就可以做到,无需改动原来的任何代码,符合开闭原则,也去除所有的if-else。
上述代码都是一些简单代码,我只是希望通过上面的例子告诉自己在写代码时不要只是单纯的只是想完成功能,而要更多的思考如何去设计代码,使代码更具可扩展性,写出更优雅的代码。