Copyright notice: This article is the blogger’s original article, shall not be reproduced without the permission of the blogger
Github:github.com/AnliaLee
If you see any mistakes or have any good suggestions, please leave a comment
preface: Some time ago, the company’s server developer was short of manpower, and the project was in urgent need of docking the SMS sending interface of a platform, so I undertook this task. Looked at the original text messaging tools code, found several platforms SMS interface methods are piled together, Israel had only as to distinguish between each other, the whole utility class hundreds of lines of code, call the relevant method and to maintain the original code, or integrate new SMS platform is very inconvenient, so decided to learn the knowledge of the object-oriented design, Start to rebuild the SMS tool class. This issue will take several common SMS interfaces on the market as examples to talk about the packaging process of tools with single function (sending SMS) but multiple schemes (multi-platform), hoping to inspire project development and functional integration for everyone
Preparation work prior to encapsulation
Before we get started, we need to think about how to design the utility class. First of all, why do you want to encapsulate
Purpose of encapsulation
- Let call SMS tool class users as much as possible to reduce the use of cost
- The maintenance tool developers can integrate other SMS platforms and maintain the original code more easily
According to the requirements of the first point, I hope that there is only one unified entrance when calling the SMS tool class eventually. Then, I simply select the SMS platform through a parameter, and pass in the corresponding parameters according to the requirements of the platform to complete the operation of SMS sending and obtain the returned data. Considering that the names of the parameters vary from platform to platform and there may be custom extensions, we decided to use the Builder mode to design the tool class entry
Look at the second request, if all will be like the original short message platform interface on the same tools, all kinds of analytic function is also on this, the maintenance will be abnormal trouble, also may be because of the integration of new SMS platform is introduced into the unknown bugs, so we need to isolate the docking platform code
Then after analyzing how to start the development of the SMS tool class, according to the idea of “top-down design, bottom-up implementation”, we start from the integration of the platform SMS interface to achieve this tool class step by step
Abstract Integrated SMS platform interface
The original SMS tool class integrates the SMS sending interfaces of Ali Cloud, netease Cloud, Yunxin and other platforms. Some of them can only use POST for data transmission, while others can use BOTH POST and GET. Therefore, we need to take these two transmission modes into account when carrying out abstraction and create the SMSModel abstract class
public abstract class SMSModel {
public abstract String post(Map<String, String> map);
public abstract String get(Map<String, String> map);
}
Copy the code
Taking the SMS sending interface of Ali Cloud as an example, the new ALModel inherits SMSModel to realize the specific request process
/** * Aliyun SMS platform */
public class ALModel extends SMSModel{
@Override
public String post(Map<String, String> map){
// Omit specific code implementation...
return result;
}
@Override
public String get(Map<String, String> map) {// Because the platform does not support get transmission of data, just return an error message
return "Request failed! The platform does not support GET requests."; }}Copy the code
Then according to the development document of Ali Cloud SMS platform, the SMSParameter is created according to the input parameter list, which is convenient for users to inject parameters
public class SMSParameter {
// SMS interface platform
public static final String SMS_MODEL_AL = "AL";// Aliyun SMS platform
/ / ali cloud message interface parameters, document: https://help.aliyun.com/document_detail/55284.html?spm=5176.doc55289.6.557.J43llA
public static final String AL_KEYID = "AccessKeyId";
public static final String AL_KEYSECRET = "AccessKeySecret";
public static final String AL_PHONENUMBERS = "PhoneNumbers";// Phone number for receiving SMS messages
public static final String AL_SIGNNAME = "SignName";// SMS signature
public static final String AL_TEMPLATECODE = "TemplateCode";// ID of the SMS template
public static final String AL_TEMPLATEPARAM = "TemplateParam";// SMS template variable replaces JSON string
public static final String AL_SMSUPEXTENDCODE = "SmsUpExtendCode";// Extension code of uplink SMS
public static final String AL_OUTID = "OutId";// External pipelining extension fields
}
Copy the code
The same is true for other SMS platform integrations, which I won’t go into too much detail
To achieve the construction of Builder class and middleware Request class
When using our encapsulation tool class, users do not need to know how the SMS interface is called. Hiding the calling process of the SMS interface can avoid a series of problems caused by user invocation errors and effectively reduce the user cost. Therefore, we create SMSBuilder class to manage and configure the user to call the parameter of the utility class, and realize the free extension and construction of the utility class. The SMSRequest class is created to connect SMSBuilder and SMSModel and act as a bridge between them
Let’s take a look at the SMSBuilder class. We tentatively define several parameters for initializing the tool class, use enumeration (ModelType) to limit the SMS platform that the user can choose, and instantiate the corresponding SMSModel according to the user’s choice in setModelType method. Finally, when the user completes the utility class initialization with the build() method, instantiate a SMSRequest class to perform the operation of calling the SMS interface. The SMSBuilder code is as follows
public abstract class SMSBuilder {
public String builderType;// The builder type is post and GET
public String modelType;// SMS platform selected by the user
public Map<String, String> map;// Save the SMS platform parameters
public SMSModel smsModel;
/** * SMS platform: * SMS_MODEL_AL (Ali SMS platform) */
public enum ModelType{
SMS_MODEL_AL
}
public SMSBuilder(a) {}
public SMSBuilder setModelType(ModelType modelType) {
if(modelType ! =null) {
switch (modelType) {
case SMS_MODEL_AL:
this.modelType = SMSParameter.SMS_MODEL_AL;
smsModel = new ALModel();
break;
default:
this.modelType = "";
break; }}return this;
}
public SMSBuilder addMapParams(Map<String, String> map){
if(this.map == null) {this.map = map;
}
return this;
}
public SMSRequest build(a){
return new SMSRequest(this); }}Copy the code
The SMSRequest class performs specific call interface operations (toRequest) based on the parameters that the user initializes with SMSBuilder. The code is as follows
public class SMSRequest {
private SMSModel smsModel;
private String builderType;
private String modelType;
private Map<String, String> paramsMap;
private String result;
private String errorMessage;
public SMSRequest(SMSBuilder builder){
builderType = builder.builderType;
modelType = builder.modelType;
paramsMap = builder.map;
smsModel = builder.smsModel;
result = "";
errorMessage = modelType+":"+builderType;
if(builder.modelType == null || builder.modelType.equals("")){
result = builderType+"Request failed! SMS interface type cannot be empty";
return;
}
toRequest();
}
/** * synchronizes the return data */
public String execute(a){
return result;
}
private void toRequest(a){
if(paramsMap==null){
result = errorMessage+"Request failed! Map cannot be empty!";
return;
}
if(builderType.equals("post")){
result = smsModel.post(paramsMap);
}else if(builderType.equals("get")){ result = smsModel.get(paramsMap); }}}Copy the code
This section takes the most basic function configuration as an example. If you need to expand more functions, such as timeout notification, group SMS, or asynchronous callback, you can modify them on this basis
Implement the user-oriented SMSUtils class
Finally, let’s take a look at the SMSUtils class directly contacted by users. The code is relatively simple. Here, singleton mode is used to instantiate SMSUtils, and PostBuilder and GetBuilder are defined to distinguish POST and GET requests
public class SMSUtils {
private volatile static SMSUtils mInstance;
private SMSUtils(a){}
private static class SMSUtilsHolder{
private static final SMSUtils mInstance = new SMSUtils();
}
public static SMSUtils getInstance(a){
return SMSUtilsHolder.mInstance;
}
public class PostBuilder extends SMSBuilder{
public PostBuilder(a){
this.builderType = "post"; }}public class GetBuilder extends SMSBuilder{
public GetBuilder(a){
this.builderType = "get"; }}public static PostBuilder post(a){
return getInstance().new PostBuilder();
}
public static GetBuilder get(a){
returngetInstance().new GetBuilder(); }}Copy the code
Once the entire utility class is wrapped, the user can call the SMS sending interface in the future by writing a few lines of code as shown in the following example
String result = "";
Map<String, String> map = newHashMap<String, String>(); Map. put(SMSParameter.XXX, parameter content); .// Set parameters based on platform requirements
result = SMSUtils
.post()// Post (): Indicates that the request type is POST. Get (): indicates that the request type is GET
.setModelType(ModelType.SMS_MODEL_AL)// Select an SMS platform
.addMapParams(map)// Inject parameters map
.build()// Initialization is complete
.execute();
System.out.println("result:"+result);
Copy the code
The directory structure of the whole tool class is as follows, among which SMSUnitTest is used for unit test, model is used to store the specific implementation classes of each SMS platform, parameter is used to store the common parameter constant class, utils is used to store some needed tool classes such as XML and JSON parsing class
Having introduced the entire utility class, we return to our original two-point encapsulation purpose. Does our utility class effectively reduce the user’s cost? Yes, I think, as opposed to looking through hundreds or thousands of lines of utility classes to find the SMS interface you need to use. The initialization process of the chain structure in Builder mode makes it easier for users to call our utility classes and the code is logical and readable. Does it reduce development costs for maintenance staff? Taking the integration of new SMS platform as an example, maintenance personnel only need to create a new platform implementation class under the Model package, and then configure the corresponding parameters in SMSBuilder, which greatly reduces the coupling degree of tool class, and also reduces the possibility of conflict caused by multi-person development. If a platform connection BUG occurs, you only need to DEBUG it in the corresponding implementation class
This blog ends here, due to my limited personal ability, some places are certainly not good enough, if you have any suggestions welcome comments pointed out, keep writing bugs and then fixing bugs can learn more things, mutual encouragement ~