在项目的开发中,我们很难做到开发一套标准的流程来解决所有客户的需求。比如,我们当前的计量项目,分别运行于赤峰市和河北省。虽然两个区域处理的业务相同,但是对细节的实现要求却不同。前面也学习过计量检定软件,其为了解决各个定制者使用的功能需求,最后采取的方案是:将基础项目复制多份,进而满足不同的客户需求。优点当然是有的,但比起缺点来,优点便不值一提。缺点很明显,总结为一句话就是:项目变得难以维护。所以,当前让我们看到的就是,几个开发人员,每天处于解决问题当中。本文将给出一种方案,来有效的规避上述问题。
资源与环境
示例代码: https://github.com/mengyunzhi/springbootsamplecode/tree/master/dynamic-autowire
开发环境:java1.8 + spring-boot:2.1.3.release需求假设
假设使用本项目的人员为:中国人、美国人,分别能接受的语言为中文和英文。 项目运行后,可以根据当前的访问人员是国籍来动态显示: 你好 或 hello 有新的需求后,比如:增加德国人并显示 hallo 。增加功能时,不更改核心代码。 不使用if else注意:如果你看完需求假设后,毫无触动,请忽略本文以下内容
解决方案
解决方案中,我们涉及了两种设计模块,分别为: 策略模式 及 工厂模式 。
策略模式:一般用于将具体的 算法 进行抽象及剥离。此项目中,我们的具体算法是 说你好 。
工厂模式:一般用于根据环境来动态的创建bean的情况下。引项目中,我们将根据不同国家的人,来返回不同的 说你好 这个算法。
先给出uml图:
speakservice
speakservice 即为我们供其它模块调用的 说话服务 ,调用其中的 sayhello() 来完成 说你好 功能。
1 2 3 4 5 6 7 8 |
package com.mengyunzhi.demo.dynamicautowire;
/** * 你好 */ public interface speakservice { void sayhello(); } |
在其实现类中,我们注入 sayhellofactory ,让其来返回正确的 sayhelloservice ,最终调用 sayhello() 来完成目标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.mengyunzhi.demo.dynamicautowire;
import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.service;
/** * 你好 */ @service public class speakserviceimpl implements speakservice { private final sayhellofactory sayhellofactory; // 说话工厂
@autowired public speakserviceimpl(sayhellofactory sayhellofactory) { this .sayhellofactory = sayhellofactory; }
@override public void sayhello() { this .sayhellofactory.getsayhelloservice().sayhello(); } } |
sayhellofactory
1 2 3 4 5 6 7 8 9 10 11 |
package com.mengyunzhi.demo.dynamicautowire;
/** * 说话工厂 */ public interface sayhellofactory {
void setcountrycode(countrycode countrycode);
sayhelloservice getsayhelloservice(); } |
在此,我们增加一个 countrycode 表示当前访问者的国家。其实在获取访问者国家时,我们也可以调用其它bean的其它来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.mengyunzhi.demo.dynamicautowire;
/** * 国家代码 */ public enum countrycode { china(( byte ) 0 , "中国" ), usa(( byte ) 1 , "美国" ); private byte code; private string name;
countrycode( byte code, string name) { this .code = code; this .name = name; }
public byte getcode() { return code; } public string getname() { return name; } } |
使用 enum 来控制范围,避免 factory 在获取bean时发生异常。
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 |
package com.mengyunzhi.demo.dynamicautowire;
import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.service;
import java.util.hashmap; import java.util.list; import java.util.map;
/** * 说话工厂 */ @service public class sayhellofactoryimpl implements sayhellofactory { /** * bean列表 */ private final map< byte , sayhelloservice> servicesbycode = new hashmap<>(); /** * 国家代码 */ private countrycode countrycode = countrycode.china;
@override public void setcountrycode(countrycode countrycode) { this .countrycode = countrycode; }
/** * 初始化 * * @param sayhelloservices spring获取到的所以实现了speakservice的bean */ @autowired public void init(list<sayhelloservice> sayhelloservices) { for (sayhelloservice sayhelloservice : sayhelloservices) { this .register(sayhelloservice.getcode(), sayhelloservice); } }
/** * 注册bean * * @param code 代码 * @param sayhelloservice bean */ private void register( byte code, sayhelloservice sayhelloservice) { this .servicesbycode.put(code, sayhelloservice); }
/** * 获取bean * * @return 对应的sayhelloservice bean */ @override public sayhelloservice getsayhelloservice() { return this .servicesbycode.get( this .countrycode.getcode()); } } |
增加 map<byte, sayhelloservice> servicesbycode 来存储对应国家的 sayhelloservice bean。增加 getsayhelloservice() 来根据当前国家代码来返回相应的bean。
sayhelloservice
1 2 3 4 5 6 7 8 9 10 |
package com.mengyunzhi.demo.dynamicautowire;
/** * 说话 */ public interface sayhelloservice { void sayhello();
byte getcode(); } |
将 sayhello() 方法抽离, getcode() 以获取国家代码。
中国人你好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.mengyunzhi.demo.dynamicautowire;
import org.springframework.stereotype.component;
/** * 中国话 */ @component public class sayhelloservicechineseimpl implements sayhelloservice { @override public void sayhello() { system.out.println( "您好" ); }
@override public byte getcode() { return countrycode.china.getcode(); } } |
美国人hello
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.mengyunzhi.demo.dynamicautowire;
import org.springframework.stereotype.component;
/** * 美国话 */ @component public class sayhelloserviceenglishimpl implements sayhelloservice { @override public void sayhello() { system.out.println( "hello" ); }
@override public byte getcode() { return countrycode.usa.getcode(); } } |
测试
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 |
package com.mengyunzhi.demo.dynamicautowire;
import org.junit.test; import org.junit.runner.runwith; import org.springframework.beans.factory.annotation.autowired; import org.springframework.boot.test.context. springboot test; import org.springframework.test.context.junit4.springrunner;
@springboottest @runwith (springrunner. class ) public class speakserviceimpltest { @autowired speakservice speakservice; @autowired sayhellofactory sayhellofactory;
@test public void sayhello() { // 默认说你好 speakservice.sayhello();
// 将国家设置为美国,再说你好 sayhellofactory.setcountrycode(countrycode.usa); speakservice.sayhello();
// 将国家设置为中国,再说你好 sayhellofactory.setcountrycode(countrycode.china); speakservice.sayhello(); } } |
您好
hello
您好
时序图
增加德国人
增加德国人 sayhelloservicegermanyimpl .
在 countrycode 中,增加德国.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.mengyunzhi.demo.dynamicautowire;
import org.springframework.stereotype.component;
@component public class sayhelloservicegermanyimpl implements sayhelloservice { @override public void sayhello() { system.out.println( "hallo" ); }
@override public byte getcode() { return countrycode.germany.getcode(); } } |
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 |
package com.mengyunzhi.demo.dynamicautowire;
/** * 国家代码 */ public enum countrycode { china(( byte ) 0 , "中国" ), usa(( byte ) 1 , "美国" ), germany(( byte ) 2 , "德国" ); private byte code; private string name;
countrycode( byte code, string name) { this .code = code; this .name = name; }
public byte getcode() { return code; }
public string getname() { return name; } } |
单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@test public void sayhello1() { // 默认说你好 speakservice.sayhello();
// 将国家设置为美国,再说你好 sayhellofactory.setcountrycode(countrycode.usa); speakservice.sayhello();
// 将国家设置为德国,再说你好 sayhellofactory.setcountrycode(countrycode.germany); speakservice.sayhello();
// 将国家设置为中国,再说你好 sayhellofactory.setcountrycode(countrycode.china); speakservice.sayhello(); } |
测试结果如下:
您好
hello
hallo
您好
总结
在解决问题时,只所有我们看的不够远,可能是由于自己站的不够高。同样的问题,困惑我了多日,直到近期系统的学习 设计模式 、 angular官方教程 、 spring 实战 后,结合近期项目变更带来的新需求,才在使用设计模式解决此问题上有所启发。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
原文链接:https://segmentfault.com/a/1190000018673552
查看更多关于详解spring-boot下如何满足多生产环境中个性化定制功能的详细内容...