By Chen Changyi (Changyi) at Alibaba.
Some time ago, Don Roberts proposed an approach to refactoring: "The first time you do something, you just do it. The second time you do something similar, you wince at the duplication, but you do the duplicate thing anyway. The third time you do something similar, you refactor."
Similarly, when writing code multiple times, we need to think about whether there is a way to increase our coding speed. Today, Chen Changyi, an technical expert working with Alibaba's mobile map and navigation app AMAP, has been engaged in agile development for many years and would like to summarize some coding methods here to help programmers write code in a fast and efficient manner.
Most novice Java programmers will repeatedly type out the following code on their development tools as if it were a ritual:
public class Test {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
Yes, this is the classic "Hello world" code, which typically is the first program that most programmers will manually write.
Manual coding can in many ways reflect the competence of a programmer. Many companies use computer programming examinations as an important part of their interview processes. The interviewee needs to select a programming tool, such as Eclipse, based on the examination requirements and then manually write, debug and run the code. Naturally, when coding, the interviewee cannot search for answers on the Internet or view any online help documentation. The interviewee must write the code manually. Such examinations test the interviewee's ability to manually coding, and also specific aspects like the coder's syntax, functions, logic, conception, algorithms, and general hands-on knowhow.
Manual coding is a basic and essential skill that a good programmer must possess. Writing code manually is like writing an article. The syntax is the method for making sentences. Functions are the words and sentences that make up the article. The class libraries are anecdotes for quoting. The architecture is the genre for expressions. The functionality is the main purpose of writing the article. The algorithms are the logic for organizing the language. So, in other words, you need to master the syntax of a programming language, learn a bunch of functions of basic class libraries, quote some required third-party class libraries, select a mature and stable architecture, clarify the features involved in product requirements, and select an algorithm to implement the logic. Then, as you can see, once you've mastered all of this, manual coding can be as easy as writing an article.
As one Chinese saying goes, "When you have learned to recite 300 poems of the Tang Dynasty by heart, you'll surely be able to write poems." The same is true for coding. The first step of coding is imitation, which is just a fancy way of saying copying and pasting code. In my opinion, copying and pasting code is something of an art. When properly done, this method allows you to write code with half the effort. However, code that is not tested cannot be trusted. When you see the code you need, carefully check it before copying and pasting it. And, it's also important to know that code suitable for one scenario may not be suitable for another scenario. Any qualified programmer knows that he cannot simply take and use the code without first checking it.
So, in short, copying and pasting code is like other coding methods, and it is in no ways a method that is superior to any of the other methods. It is just a method that you can use or abuse. And, you need to remember that, if you copy and paste code, you are still responsible for the results, so copy and paste code carefully!
Below is a piece of code that has been written to implement user queries:
/** 查询用户服务函数 */
public PageData<UserVO> queryUser(QueryUserParameterVO parameter) {
Long totalCount = userDAO.countByParameter(parameter);
List<UserVO> userList = null;
if (Objects.nonNull(totalCount) && totalCount.compareTo(0L) > 0) {
userList = userDAO.queryByParameter(parameter);
}
return new PageData<>(totalCount, userList);
}
/** 查询用户控制器函数 */
@RequestMapping(path = "/queryUser", method = RequestMethod.POST)
public Result<PageData<UserVO>> queryUser(@Valid @RequestBody QueryUserParameterVO parameter) {
PageData<UserVO> pageData = userService.queryUser(parameter);
return Result.success(pageData);
}
If you want to write code to implement company queries, then the code format you'll need is similar to the user query code. The replacement is as simple as this example: wherever "User" (with a captial "U") appears, replace it with "Company", and wherever "user" (with a lowercase "u") is, change it to be "company". The logic of it really is that simple. Of course, things can get complicated quick, so you'll still need to be careful.
You can use a text editor, such as Notepad or EditPlus, to replace the common text, but make sure to do so in a case-sensitive mode so to minimize mistakes. The final result should look something like this:
/** 查询公司服务函数 */
public PageData<CompanyVO> queryCompany(QueryCompanyParameterVO parameter) {
Long totalCount = companyDAO.countByParameter(parameter);
List<CompanyVO> companyList = null;
if (Objects.nonNull(totalCount) && totalCount.compareTo(0L) > 0) {
companyList = companyDAO.queryByParameter(parameter);
}
return new PageData<>(totalCount, companyList);
}
/** 查询公司控制器函数 */
@RequestMapping(path = "/queryCompany", method = RequestMethod.POST)
public Result<PageData<CompanyVO>> queryCompany(@Valid @RequestBody QueryCompanyParameterVO parameter) {
PageData<CompanyVO> pageData = companyService.queryCompany(parameter);
return Result.success(pageData);
}
If you generate code by using the method of text replacement, the code generation time needed will typically not exceed more than one minute.
Excel formulas are very powerful and can be used to compile some formulaic code rather quickly.
For this, first, you'll want to copy the interface model definition from Wiki to Excel. The sample data is shown below:
Now, you'll need to write an Excel formula like the one below:
= "/** "&D6&IF(ISBLANK(F6), "", "("&F6&")")&" */ "&IF(E6 = "否", IF(C6 = "String", "@NotBlank", "@NotNull"), "")&" private "&C6&" "&B6&";"
Use the formula to generate code like the one here:
/** 用户标识 */ @NotNull private Long id;
/** 用户名称 */ @NotBlank private String name;
/** 用户性别(0:未知;1:男;2:女) */ @NotNull private Integer sex;
/** 用户描述 */ private String description;
Then, create a model class and organize the code:
/** 用户DO类 */
public class UserDO {
/** 用户标识 */
@NotNull
private Long id;
/** 用户名称 */
@NotBlank
private String name;
/** 用户性别(0:未知;1:男;2:女) */
@NotNull
private Integer sex;
/** 用户描述 */
private String description;
......
}
You'll want to copy the enumeration definition from Wiki to Excel. The sample data is as follows:
Next, you'll need to write an Excel formula like the one below:
="/** "&D2&"("&B2&") */"&C2&"("&B2&", """&D2&"""),"
And use the formula to generate the code:
/** 空(0) */NONE(0, "空"),
/** 男(1) */MAN(1, "男"),
/** 女(2) */WOMAN(2, "女"),
And then you'll want to create an enumeration class and organize the code:
/** 用户性别枚举 */
public enum UserSex {
/** 枚举定义 */
/** 空(0) */
NONE(0, "空"),
/** 男(1) */
MAN(1, "男"),
/** 女(2) */
WOMAN(2, "女");
......
}
The company list should be sorted in the order shown in the example below. This is important to get right because you'll need to write SQL statements to insert the records directly into the database based on this list.
Then, you'll want to write an Excel formula like the one below:
= "('"&B2&"', '"&C2&"', '"&D2&"', '"&E2&"'),"
And use the formula to generate SQL statements:
('高德', '首开大厦', '(010)11111111', 'gaode@xxx.com'),
('阿里云', '绿地中心', '(010)22222222', 'aliyun@xxx.com'),
('菜鸟', '阿里中心', '(010)33333333', 'cainiao@xxx.com'),
Add the "into" statement header and sort the SQL statements should look something like this:
insert into t_company(name, address, phone, email) values
('高德', '首开大厦', '(010)11111111', 'gaode@xxx.com'),
('阿里云', '绿地中心', '(010)22222222', 'aliyun@xxx.com'),
('菜鸟', '阿里中心', '(010)33333333', 'cainiao@xxx.com');
In this method, you'll use an existing tool to generate code. Many development toolkits provide tools to generate code, for example, to generate constructors, reload base classes or interface functions, generate Getter/Setter functions, and generate toString functions. These tools can save you the trouble of needing to manually coding, and many of them are free. You can also use some code generation plug-ins to generate code that suits certain application scenarios.
The example I'm sharing here uses MyBatis Generator to show just how you can use a tool to generate code.
Installing and running the plug-in isn't that hard, so I'll not going to go into too much detail here, but here's a list of plug-ins to get you started.
Now, assuming that you've installed and started to run MyBatis Generator, let's proceed to our example.
The content of the User.java
file should look something like this:
......
public class User {
private Long id;
private String user;
private String password;
private Integer age;
......
}
The content of the UserMapper.java
file will look like this:
......
public interface UserMapper {
User selectByPrimaryKey(Long id);
......
}
The content of the UserMapper.xml
file:
......
<mapper namespace="com.test.dao.UserMapper" >
<resultMap id="BaseResultMap" type="com.test.pojo.User" >
<id column="id" property="id" jdbcType="BIGINT" />
<result column="user" property="user" jdbcType="VARCHAR" />
<result column="password" property="password" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List" >
id, user, password, age
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from test_user
where id = #{id,jdbcType=BIGINT}
</select>
......
</mapper>
For this method, you'll write the code by yourself and generate code in your own style. The following uses the MyBatis-based database access code as an example to explain this method.
First, you'll need to obtain the table and column information required for code generation from the database.
The statements for querying table information should look something like this:
select t.table_name as '表名称'
, t.table_comment as '表备注'
from information_schema.tables t
where t.table_schema = ?
and t.table_type = 'BASE TABLE'
and t.table_name = ?;
In this, the first question mark indicates the value assigned to the database name, and the second one indicates the value assigned to the table name.
The table information query result will look like this:
The statements for querying column information should look something like this:
select c.column_name as '列名称'
, c.column_comment as '列备注'
, c.data_type as '数据类型'
, c.character_maximum_length as '字符长度'
, c.numeric_precision as '数字精度'
, c.numeric_scale as '数字范围'
, c.column_default as ''
, c.is_nullable as '是否可空'
, c.column_key as '列键名'
from information_schema.columns c
where c.table_schema = ?
and c.table_name = ?
order by c.ordinal_position;
For this code, like before, the first question mark indicates the value assigned to the database name, and the second one indicates the value assigned to the table name.
The column information query result will look like this:
/** 生成模型类文件函数 */
private void generateModelClassFile(File dir, Table table, List<Column> columnList) throws Exception {
try (PrintWriter writer = new PrintWriter(new File(dir, className + "DO.java"))) {
String className = getClassName(table.getTableName());
String classComments = getClassComment(table.getTableComment());
writer.println("package " + groupName + "." + systemName + ".database;");
......
writer.println("/** " + classComments + "DO类 */");
writer.println("@Getter");
writer.println("@Setter");
writer.println("@ToString");
writer.println("public class " + className + "DO {");
for (Column column : columnList) {
String fieldType = getFieldType(column);
String fieldName = getFieldName(column.getColumnName());
String fieldComment = getFieldComment(column);
writer.println("\t/** " + fieldComment + " */");
writer.println("\tprivate " + fieldType + " " + fieldName + ";");
}
writer.println("}");
}
}
/** 生成DAO接口文件函数 */
private void generateDaoInterfaceFile(File dir, Table table, List<Column> columnList, List<Column> pkColumnList) throws Exception {
try (PrintWriter writer = new PrintWriter(new File(dir, className + "DAO.java"))) {
String className = getClassName(table.getTableName());
String classComments = getClassComment(table.getTableComment());
writer.println("package " + groupName + "." + systemName + ".database;");
......
writer.println("/** " + classComments + "DAO接口 */");
writer.println("public interface " + className + "DAO {");
writer.println("\t/** 获取" + classComments + "函数 */");
writer.print("\tpublic " + className + "DO get(");
boolean isFirst = true;
for (Column pkColumn : pkColumnList) {
if (!isFirst) {
writer.print(", ");
} else {
isFirst = false;
}
String fieldType = getFieldType(pkColumn);
String fieldName = getFieldName(pkColumn.getColumnName());
writer.print("@Param(\"" + fieldName + "\") " + fieldType + " " + fieldName);
}
writer.println(");");
......
writer.println("}");
}
}
/** 生成DAO映射文件函数 */
private void generateDaoMapperFile(File dir, Table table, List<Column> columnList, List<Column> pkColumnList) throws Exception {
try (PrintWriter writer = new PrintWriter(new File(dir, className + "DAO.xml"))) {
String className = getClassName(table.getTableName());
String classComments = getClassComment(table.getTableComment());
writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
......
writer.println("<!-- " + classComments + "映射 -->");
writer.println("<mapper namespace=\"" + groupName + "." + systemName + ".database." + className + "DAO\">");
writer.println("\t<!-- 所有字段语句 -->");
writer.println("\t<sql id=\"fields\">");
if (CollectionUtils.isNotEmpty(columnList)) {
boolean isFirst = true;
String columnName = getColumnName(pkColumn.getColumnName());
for (Column column : columnList) {
if (isFirst) {
isFirst = false;
writer.println("\t\t" + columnName);
} else {
writer.println("\t\t, " + columnName);
}
}
}
writer.println("\t</sql>");
writer.println("\t<!-- 获取" + classComments + "函数语句 -->");
writer.println("\t<select id=\"get\" resultType=\"" + groupName + "." + systemName + ".database." + className + "DO\">");
writer.println("\t\tselect");
writer.println("\t\t<include refid=\"fields\"/>");
writer.println("\t\tfrom " + table.getTableName());
boolean isFirst = true;
for (Column pkColumn : pkColumnList) {
String columnName = getColumnName(pkColumn.getColumnName());
String fieldName = getFieldName(pkColumn.getColumnName());
writer.print("\t\t");
if (isFirst) {
writer.print("where");
isFirst = false;
} else {
writer.print("and");
}
writer.println(" " + columnName + " = #{" + fieldName + "}");
}
writer.println("\t</select>");
writer.println("</mapper>");
}
}
/** 组织公司DO类 */
@Getter
@Setter
@ToString
public class OrgCompanyDO {
/** 公司标识 */
private Long id;
/** 公司名称 */
private String name;
/** 联系地址 */
private String address;
/** 公司描述 */
private String description;
}
/** 组织公司DAO接口 */
public interface OrgCompanyDAO {
/** 获取组织公司函数 */
public OrgCompanyDO get(@Param("id") Long id);
}
<!-- 组织公司映射 -->
<mapper namespace="xxx.database.OrgCompanyDAO">
<!-- 所有字段语句 -->
<sql id="fields">
id
, name
, address
, description
</sql>
<!-- 获取组织公司函数语句 -->
<select id="get" resultType="xxx.database.OrgCompanyDO">
select
<include refid="fields"/>
from org_company
where id = #{id}
</select>
</mapper>
In a dream world, of course, the ultimate method of coding would let you tell the computer what you need and have the computer automatically generate the code. Interestingly, this may become a reality as technology develops in the future. But today, this method is unrealistic. In reality, you cannot simply say what you want and receive the necessary code, unless you are a boss, product manager, or technical manager.
The best method of coding currently available is to use whatever methods that are appropriate, instead of sticking to only a single method. Of course, all the coding methods listed in this article have their own advantages and disadvantages and are applicable to different scenarios. So, again, in my opinion, flexibly using all of the various coding methods discussed in this article is the best coding method since we're not living in a dream world just quite yet.
One last thing, many of the coding methods that I've discussed in this post require you to manually compile sample code. But, it's important to take note that, if your code does not comply with the specific code specifications needed for your work, it may be difficult to find commonalities between the relevant bits of code in the sample, and it also may be difficult to abstract the sample code to be used as the standard code. So, if the sample code that functions as a standard does not comply with any of your specific code specifications, then the generated code will of course also fail to comply with your code specifications. And, this noncompliance will only be magnified by tens, hundreds, or even thousands of times, as you continue to use it! So, above all else, I want to emphasize here that code standardization should also be your top priority when coding.
Alibaba Cloud Community - September 13, 2021
gangz - December 10, 2020
Alibaba Clouder - January 19, 2021
gangz - December 10, 2020
Alibaba Clouder - July 27, 2020
Adrian Peng - February 1, 2021
Alibaba Cloud (in partnership with Whale Cloud) helps telcos build an all-in-one telecommunication and digital lifestyle platform based on DingTalk.
Learn MoreOffline SDKs for visual production, such as image segmentation, video segmentation, and character recognition, based on deep learning technologies developed by Alibaba Cloud.
Learn MoreA low-code development platform to make work easier
Learn MoreHelp enterprises build high-quality, stable mobile apps
Learn More