preface
There is a well-known design principle for development: the open closed principle, open for extension, closed for modification. But in the actual development of few people can use proficiency, shaolin in the development of contact with the example is, most people are if… The else… This is difficult to extend the conditional judgment. So how do you elegantly simplify complex logical judgments? Of course, abstract commonness is the optimization scheme from the perspective of product thinking, and what Shaoxia wants to say today is to achieve it through technical means.
Business background
First of all, I will briefly introduce the business background. The background is very simple, that is, there are several channel sources, such as Alibaba and Tencent. Different channels need different data processing logic, and the channel sources will continue to expand.
The preliminary implementation
Start by creating a simple enumeration class:
/ * * *@author Carson
* @date2020/8/24 3:10 PM */
public enum SourceEnum {
/** * alibaba */
ALIBABA("ALIBABA"),
/** * Tencent */
TENCENT("TENCENT"),;public String name;
SourceEnum(String name) {
this.name = name;
}
/ / match
public static SourceEnum match(String name){
SourceEnum[] values = SourceEnum.values();
for (SourceEnum value : values) {
if(value.name.equals(name)){
returnvalue; }}return null;
}
public String getName(a) {
returnname; }}Copy the code
Take a look at the business layer interface and the implementation classes (note that the @service annotation on the implementation class is aliased for use with the @qualifier annotation later) :
/ * * *@author Carson
* @date2020/8/24 3:10 PM */
public interface DataService {
String dataProcess(a);
}
/ * * *@author Carson
* @date2020/8/24 3:14 PM */
@Service("alibabaServiceImpl")
public class AlibabaServiceImpl implements DataService {
@Override
public String dataProcess(a) {
return "Alibaba Process++++++++++++++"; }}/ * * *@author Carson
* @date2020/8/24 3:12 PM */
@Service("tencentServiceImpl")
public class TencentServiceImpl implements DataService {
@Override
public String dataProcess(a) {
return "Tencent Process++++++++++++++"; }}Copy the code
Let’s take a look at the implementation of the interface layer, which is mainly to let the user pass in the channel source source (in real environment, this may be obtained from the user information in the cookies, and will not be discussed here), and then judge according to different channels, pay attention to the switch in the code… Case… Statement, if the channel is extended later, this is bound to be changed. Some people may feel that looking at the broad ah, clean and tidy, but after a lot of channels, ten thousand one hundred and eighty channels, here looks very bloated, and in case of error investigation is not convenient, to say a professional point, is not good scalability.
/ * * *@author Carson
* @date 20-8-20 下午5:00
*/
@RestController
public class CommonController {
private final Logger logger = LoggerFactory.getLogger(CommonController.class);
@Qualifier(value = "alibabaServiceImpl")
@Autowired
private AlibabaServiceImpl alibabaServiceImpl;
@Qualifier(value = "tencentServiceImpl")
@Autowired
private TencentServiceImpl tencentServiceImpl;
@GetMapping("/dataHandler")
public String dataHandler(String source) {
if (StringUtils.isBlank(source)) {
return "Empty data";
}
SourceEnum sourceEnum = SourceEnum.match(source);
if (sourceEnum == null) {
return "Empty data";
}
switch (sourceEnum) {
case ALIBABA:
return alibabaServiceImpl.dataProcess();
case TENCENT:
return tencentServiceImpl.dataProcess();
default:
return "Empty data"; }}}Copy the code
Code optimization
The first step in optimization is to start with the enumeration classes and specify the corresponding Service implementation class object for each enumeration class. Note that Spring interfaces cannot be loaded as Bean instances, so you need to add @lookup annotations for properties and methods.
@Service
public interface DataService {
// Note that the @lookup annotation must be added here, otherwise the container will not start
@Lookup
String dataProcess(a);
}
Copy the code
public class AlibabaServiceImpl implements DataService {
public AlibabaServiceImpl(a){}
@Override
public String dataProcess(a) {
return "Alibaba Process++++++++++++++"; }}public class TencentServiceImpl implements DataService {
@Override
public String dataProcess(a) {
return "Tencent Process++++++++++++++"; }}Copy the code
Then there is the improved enumeration class:
/ * * *@author Carson
* @date2020/8/24 3:10 PM */
public enum SourceEnum {
/** * alibaba */
ALIBABA("ALIBABA".new AlibabaServiceImpl()),
/** * Tencent */
TENCENT("TENCENT".new TencentServiceImpl()),
;
public String name;
public DataService dataService;
SourceEnum(String name, DataService dataService) {
this.name = name;
this.dataService = dataService;
}
/ / match
public static SourceEnum match(String name) {
SourceEnum[] values = SourceEnum.values();
for (SourceEnum value : values) {
if (value.name.equals(name)) {
returnvalue; }}return null;
}
public String getName(a) {
return name;
}
public DataService getDataService(a) {
returndataService; }}Copy the code
Then the interface layer is modified as follows, notice that the annotation here only configures the interface autowiring (not annotating all the implementation classes), and then implements the specific invocation through polymorphism.
@RestController
public class CommonController {
private final Logger logger = LoggerFactory.getLogger(CommonController.class);
@Autowired
private DataService dataService;
@GetMapping("/dataHandler")
public String dataHandler(String source) {
if (StringUtils.isBlank(source)) {
return "Empty data";
}
SourceEnum sourceEnum = SourceEnum.match(source);
if (sourceEnum == null) {
return "Empty data";
}
AbstractDataService dataService = sourceEnum.dataService;
if (dataService == null) {
return "Empty data";
}
returndataService.dataProcess(); }}Copy the code
summary
By using enumeration classes, properties are bound to rule concrete implementations in enumeration. If we need new channels, we can just implement the DataService interface when we write the rule implementation class and add the new enumeration to the enumeration class without changing any of the original code. This conforms to the open close principle. Since Spring is a singleton by default, it is normal to develop an interface with multiple implementation classes specified one by one (@qualifier). However, if you want to take advantage of polymorphism and let your application decide which implementation class to implement, you can configure the @Service annotation on the interface. Also add @lookup annotations for interface properties/methods.