Good Java skills (summary of 7 years of working experience)

Since graduation in 2013, this year has been my seventh year of work. I am always doing Java related work. Finally, I have time to sit down and write an article on Java writing to discuss. If you are really a java programmer, can you really write Java?

This article is not a boastful article. It will not talk about many advanced architectures. On the contrary, it will explain many basic problems and writing problems. If readers think that both basic problems and writing problems are not problems, please ignore this article and save time to do something meaningful.

1, Development tools

I don't know how many "old" programmers are still using Eclipse. These programmers either stick to the old ways or don't know the existence of other good development tools at all. The phenomenon of Eclipse eating memory and various accidental and inexplicable exceptions tell us that it's time to find new development tools.

1. Replace IDE

I don't want to explain what kind of IDE to change at all. If you want to be a good Java programmer, please change IntelliJ IDEA. Benefits of using IDEA, please search Google.

2. Don't tell me that the shortcut keys are not easy to use

Changing the IDE is not the focus of this article, so I don't want to spend too much time on why to change the IDE. Here, I can only tell you that changing the IDE is only to write Java code better and faster. The reason is slight.

Don't tell me that the shortcut keys are not easy to use, please try something new.

2, Bean

Bean is one of the models we use most. I will explain bean in a large length. I hope readers will have a good experience.

1. Domain package name

According to the "experience" of many Java programmers, a database table corresponds to a domain object, so when many programmers write code, the package name is: com xxx. Domain, which seems to have become a constraint of the industry. The database mapping object should be domain. But you are wrong. Domain is a domain object. In traditional Java software Web development, these domains are anaemic models, have no behavior, or do not have enough domain model behavior. Therefore, according to this theory, these domains should be an ordinary entity object, not a domain object, so please change the package name to: com xxx. entity.

If you don't understand what I said, please take a look at a book called IMPLEMENTING DOMAIN-DRIVEN DESIGN published by Vaughn Vernon, which explains the difference between anemia model and domain model. I believe you will benefit a lot.

2. DTO

For data transmission, we should use DTO objects as transmission objects, which is our agreement, because I have been working on mobile terminal API design for a long time. Many people told me that they think these objects become DTO objects only when transmitting data to mobile terminal (input or output). Please note that! This understanding is wrong. As long as they are objects used for network transmission, we all think they can be regarded as DTO objects. For example, in the e-commerce platform, users place orders, and the data after placing orders will be sent to OMS or ERP system. These docking return values and input parameters are also called DTO objects.

We agree that if an object is a DTO object, the name will be changed to XXDTO. For example, OMS: OMSOrderInputDTO when placing an order.

3. DTO conversion

As we know, DTO is a model object for the interaction between the system and the outside world, so there must be a step to convert DTO objects into BO objects or ordinary entity objects for the service layer to handle.

①. scene

For example, for the operation of adding a member, because it is used for demonstration, I only consider some simple data of the user. When the background administrator clicks to add a user, it only needs to pass the user's name and age. After receiving the data, the back end will add three fields: Creation time, update time and default password, and then save the database.

@RestController
public class UserApi {
 @Autowired
 private UserService userService;
 @PostMapping
 public User addUser(UserInputDTO userInputDTO){
 User user = new User();
 user.setUsername(userInputDTO.getUsername());
 user.setAge(userInputDTO.getAge());
 return userService.addUser(user);
 }
}

 

We only focus on the conversion code in the above code. Please ignore other contents:

User user = new User();
user.setUsername(userInputDTO.getUsername());
user.setAge(userInputDTO.getAge());

②. Please use the tool

Logically speaking, there is no problem with the above code, but this writing method annoys me. There are only two fields in the example. If there are 20 fields, what should we do? set data one by one? Of course, if you do, there will be no problem, but it is certainly not an optimal approach.

There are many tools on the Internet that support shallow or deep copy Utils. For example, we can use org springframework. beans. Beanutils#copyproperties refactor and optimize the code:

@PostMapping
public User addUser(UserInputDTO userInputDTO){
 User user = new User();
 BeanUtils.copyProperties(userInputDTO,user);
 return userService.addUser(user);
}

 

BeanUtils.copyProperties is a shallow copy method. When copying properties, we only need to set the property values of the DTO object and the object to be converted to the same name and ensure the same type. If you always use set for attribute assignment during DTO conversion, please try this way to simplify the code and make the code clearer!

③. Transformed semantics

The above conversion process must be much more elegant after reading, but when we write Java code, we need to consider more semantic operations, and then look at the above code:

User user = new User();
BeanUtils.copyProperties(userInputDTO,user);

Although this code simplifies and optimizes the code well, its semantics is problematic. We need to propose a transformation process, so the code is changed as follows:

@PostMapping
 public User addUser(UserInputDTO userInputDTO){
 User user = convertFor(userInputDTO);
 return userService.addUser(user);
 }
 private User convertFor(UserInputDTO userInputDTO){
 User user = new User();
 BeanUtils.copyProperties(userInputDTO,user);
 return user;
 }

 

This is a better semantic writing method. Although it is a little troublesome, its readability is greatly increased. When writing code, we should try to put the semantic level into one method as much as possible, such as:

User user = convertFor(userInputDTO);
return userService.addUser(user);

 

These two pieces of code do not expose the implementation. They are talking about how to do a set of semantic operations at the same level in the same method, rather than exposing the specific implementation.

As mentioned above, it is a refactoring method. Readers can refer to the Extract Method refactoring method in Martin Fowler's book refactoring improving the design of existing code.

④. Abstract interface definition

When the DTO conversion of several API s is completed in the actual work, we will find that there are many such operations, so we should define an interface to make all such operations proceed according to rules.

If the interface is defined, the semantics of the convertFor method will change. It will be an implementation class.

Take a look at the abstract interface:

public interface DTOConvert<S,T> {
 T convert(S s);
}

 

Although this interface is very simple, here's a thing for us to use generics. If you are a good Java programmer, please do generics for the abstract interface you want to do.

Let's look at the interface implementation again:

public class UserInputDTOConvert implements DTOConvert {
@Override
public User convert(UserInputDTO userInputDTO) {
User user = new User();
BeanUtils.copyProperties(userInputDTO,user);
return user;
}
}

 

After such refactoring, we found that the current code is so concise and standard:

@RequestMapping("/v1/api/user")
@RestController
public class UserApi {
 @Autowired
 private UserService userService;
 @PostMapping
 public User addUser(UserInputDTO userInputDTO){
 User user = new UserInputDTOConvert().convert(userInputDTO);
 return userService.addUser(user);
 }
}
⑤. Review Code

 

If you are a good Java programmer, I believe you should Review your code many times like me.

When we look at the example of saving users, you will find that there are some problems with the return value in the API. The problem is that we should not directly return the User entity, because if so, too much entity related information will be exposed, and such return value is unsafe. Therefore, we should return a DTO object, which we can call UserOutputDTO:

@PostMapping
public UserOutputDTO addUser(UserInputDTO userInputDTO){
 User user = new UserInputDTOConvert().convert(userInputDTO);
 User saveUserResult = userService.addUser(user);
 UserOutputDTO result = new UserOutDTOConvert().convertToUser(saveUserResult);
 return result;
}

 

In this way, your API will be more sound.

I don't know if the reader has found any other problems after reading this code. As an excellent Java programmer, please take a look at this code we have just abstracted:

User user = new UserInputDTOConvert().convert(userInputDTO);

 

You will find that a DTO conversion object such as new is unnecessary, and each conversion object will appear only when DTO conversion is encountered. Then we should consider whether this class can be aggregated with DTO. Take a look at my aggregation results:

public class UserInputDTO {
private String username;
private int age;
 public String getUsername() {
 return username;
 }
 public void setUsername(String username) {
 this.username = username;
 }
 public int getAge() {
 return age;
 }
 public void setAge(int age) {
 this.age = age;
 }
 public User convertToUser(){
 UserInputDTOConvert userInputDTOConvert = new UserInputDTOConvert();
 User convert = userInputDTOConvert.convert(this);
 return convert;
 }
 private static class UserInputDTOConvert implements DTOConvert<UserInputDTO,User> {
 @Override
 public User convert(UserInputDTO userInputDTO) {
 User user = new User();
 BeanUtils.copyProperties(userInputDTO,user);
 return user;
 }
 }
}

 

Then, the transformation in API consists of:

User user = new UserInputDTOConvert().convert(userInputDTO);
User saveUserResult = userService.addUser(user);

 

Became:

User user = userInputDTO.convertToUser();
User saveUserResult = userService.addUser(user);

 

We add the transformation behavior to the DTO object. I believe this operation can make the code more readable and semantic.

⑥. Recheck tools

Let's look at the internal transformation code of DTO. It implements the DTOConvert interface defined by ourselves, but there's really no problem. Don't you need to think about it?

I don't think so. For the conversion semantics of convert, many tool classes have such definitions. Convert is not an interface definition at the business level. It is only an interface definition in the general sense used to convert attribute values between ordinary bean s. Therefore, we should read more other codes containing conversion semantics.

I carefully read the source code of GUAVA and found com google. common. base. Convert is defined as:

public abstract class Converter<A, B> implements Function<A, B> {
 protected abstract B doForward(A a);
 protected abstract A doBackward(B b);
 //Other strategies
}

 

It can be seen from the source code that Convert in GUAVA can complete forward conversion and reverse conversion. Continue to modify the converted code in our DTO:

private static class UserInputDTOConvert implements DTOConvert<UserInputDTO,User> {
 @Override
 public User convert(UserInputDTO userInputDTO) {
 User user = new User();
 BeanUtils.copyProperties(userInputDTO,user);
 return user;
 }
}

 

After modification:

private static class UserInputDTOConvert extends Converter<UserInputDTO, User> {
 @Override
 protected User doForward(UserInputDTO userInputDTO) {
 User user = new User();
 BeanUtils.copyProperties(userInputDTO,user);
 return user;
 }
 @Override
 protected UserInputDTO doBackward(User user) {
 UserInputDTO userInputDTO = new UserInputDTO();
 BeanUtils.copyProperties(user,userInputDTO);
 return userInputDTO;
 }
 }

 

After reading this part of the code, you may ask, what is the use of reverse transformation? In fact, we have many small business requirements. The input participation and output participation are the same, so we can easily transform them. I will convert the UserInputDTO and UserOutputDTO mentioned above into UserDTO and show you.

DTO:

public class UserDTO {
 private String username;
 private int age;
 public String getUsername() {
 return username;
 }
 public void setUsername(String username) {
 this.username = username;
 }
 public int getAge() {
 return age;
 }
 public void setAge(int age) {
 this.age = age;
 }
 public User convertToUser(){
 UserDTOConvert userDTOConvert = new UserDTOConvert();
 User convert = userDTOConvert.convert(this);
 return convert;
 }
 public UserDTO convertFor(User user){
 UserDTOConvert userDTOConvert = new UserDTOConvert();
 UserDTO convert = userDTOConvert.reverse().convert(user);
 return convert;
 }
 private static class UserDTOConvert extends Converter<UserDTO, User> {
 @Override
 protected User doForward(UserDTO userDTO) {
 User user = new User();
 BeanUtils.copyProperties(userDTO,user);
 return user;
 }
 @Override
 protected UserDTO doBackward(User user) {
 UserDTO userDTO = new UserDTO();
 BeanUtils.copyProperties(user,userDTO);
 return userDTO;
 }
 }
}

 

API:

@PostMapping
 public UserDTO addUser(UserDTO userDTO){
 User user = userDTO.convertToUser();
 User saveResultUser = userService.addUser(user);
 UserDTO result = userDTO.convertFor(saveResultUser);
 return result;
 }

 

Of course, the above only indicates the forward or reverse direction of transformation. The DTO objects of many business requirements are different, so you need to tell the program more clearly that reverse cannot be called.

private static class UserDTOConvert extends Converter<UserDTO, User> {
 @Override
 protected User doForward(UserDTO userDTO) {
 User user = new User();
 BeanUtils.copyProperties(userDTO,user);
 return user;
 }
 @Override
 protected UserDTO doBackward(User user) {
 throw new AssertionError("Reverse conversion method is not supported!");
 }
 }

 

Take a look at the doBackward method, which directly throws an assertion exception instead of a business exception. This code tells the caller of the code that this method is not allowed to be called by you. If you call it, I will "assert" that you call it incorrectly.

For a more detailed introduction to exception handling, please refer to this article: "how to design Java exceptions gracefully", which should help you better understand exceptions.

4. Bean verification

If you think the add user API I wrote above is perfect, it only shows that you are not a good programmer. We should ensure that any data entry into the method body is legal.

①. Why verify

Many people will tell me that if these API s are provided for the front end to call, the front end will verify. Why do you need to verify?

In fact, the answer is like this. I never trust anyone who calls my API or method. For example, the front-end verification fails, or some people directly transfer data to my API through some special channels (such as Charles capturing packets). Then I still carry out normal business logic processing, then dirty data may be generated!

"It must be fatal to the generation of dirty data". I hope you will keep this sentence in mind. No matter how small the dirty data is, it may make you find a few all night!

②. JSR 303 validation

I think the implementation of JSR 303 provided by Hibernate is still very excellent. I don't want to talk about how to use it, because you can search a lot of answers on Google!

Let's take an example of the API at work. Now let's check the DTO data:

public class UserDTO {
 @NotNull
 private String username;
 @NotNull
 private int age;
 //Other codes are omitted
}

 

API validation:

@PostMapping
 public UserDTO addUser(@Valid UserDTO userDTO){
 User user = userDTO.convertToUser();
 User saveResultUser = userService.addUser(user);
 UserDTO result = userDTO.convertFor(saveResultUser);
 return result;
 }

 

We need to pass the verification result to the front end, and this exception should be transformed into an API exception (exception with error code).

@PostMapping
public UserDTO addUser(@Valid UserDTO userDTO, BindingResult bindingResult){
 checkDTOParams(bindingResult);
 User user = userDTO.convertToUser();
 User saveResultUser = userService.addUser(user);
 UserDTO result = userDTO.convertFor(saveResultUser);
 return result;
}
private void checkDTOParams(BindingResult bindingResult){
 if(bindingResult.hasErrors()){
 //throw new Verification error with verification code exception
 }
}

 

BindingResult is a result set after Spring MVC verifies DTO. You can refer to the official Spring documentation( http://spring.io/).

After checking the parameters, a "verification error exception with verification code" can be thrown.

5. Hug lombok

I'm tired of reading the DTO code above. I believe readers are the same. It's too annoying to see so many Getter and Setter methods. At that time, what methods can simplify these methods.

Please embrace Lombok. It will help us solve some problems that annoy us.

①. Remove Setter and Getter

In fact, I don't want to say this title, because there are too many online, but because many people tell me that they don't know the existence of Lombok at all, in order to make readers better learn, I'd like to write an example:

@Setter
@Getter
public class UserDTO {
 @NotNull
 private String username;
 @NotNull
 private int age;
 public User convertToUser(){
 UserDTOConvert userDTOConvert = new UserDTOConvert();
 User convert = userDTOConvert.convert(this);
 return convert;
 }
 public UserDTO convertFor(User user){
 UserDTOConvert userDTOConvert = new UserDTOConvert();
 UserDTO convert = userDTOConvert.reverse().convert(user);
 return convert;
 }
 private static class UserDTOConvert extends Converter<UserDTO, User> {
 @Override
 protected User doForward(UserDTO userDTO) {
 User user = new User();
 BeanUtils.copyProperties(userDTO,user);
 return user;
 }
 @Override
 protected UserDTO doBackward(User user) {
 throw new AssertionError("Reverse conversion method is not supported!");
 }
 }
}

 

See, the annoying Getter and Setter methods have been removed.

But the above example is not enough to reflect the power of lombok. I hope to write some instructions on the use of lombok, which is difficult to find on the Internet or rarely explained by people, and the semantics of the program when it is used.

For example: @ Data,@AllArgsConstructor,@NoArgsConstructor I won't explain these one by one. Please check the information yourself.

②. Chain style in Bean

What is chain style? Let me take an example to see the Student Bean:

public class Student {
 private String name;
 private int age;
 public String getName() {
 return name;
 }
 public Student setName(String name) {
 this.name = name;
 return this;
 }
 public int getAge() {
 return age;
 }
 public Student setAge(int age) {
 return this;
 }
}

 

Take a closer look at the set method. This setting is the style of chain. When calling, you can use it as follows:

Student student = new Student()
 .setAge(24)
 .setName("zs");

 

I believe that the rational use of such chain code will bring good readability to more programs. Let's take a look. If Lombok is used for improvement, please use @ accessories (chain = true) to see the following code:

@Accessors(chain = true)
@Setter
@Getter
public class Student {
 private String name;
 private int age;
}

  

This completes a very friendly chain operation for Bean.

③. Static construction method

The semantics and simplification of static construction method are really higher than that of directly new an object. For example, new is a List object. In the past, it was used as follows:

List<String> list = new ArrayList<>();

 

Take a look at the creation method in Guava:

List<String> list = Lists.newArrayList();

 

The naming of Lists is a convention (as the saying goes: Convention is better than configuration). It means that Lists is a tool class of the List class. Is it more direct to use the tool class of List to generate Lists than to directly use a subclass of new? The answer is yes. For example, if there is a tool class called Maps, do you think of the method to create Maps:

HashMap<String, String> objectObjectHashMap = Maps.newHashMap();

 

Well, if you understand the semantics I'm talking about, you're one step closer to becoming a Java programmer.

Looking back at the Student just now, many times, when we write the bean Student, it will have some required fields, such as the name field in the Student. The general processing method is to wrap the name field into a construction method. Only by passing in the construction method such as name can we create a Student object.

Connect the above static construction method and the construction method of required parameters, and use Lombok to change it to the following writing methods (@ RequiredArgsConstructor and @ NonNull):

@Accessors(chain = true)
@Setter
@Getter
@RequiredArgsConstructor(staticName = "ofName")
public class Student {
 @NonNull private String name;
 private int age;
}

 

Test code:

Student student = Student.ofName("zs");

Whether the semantics of the bean constructed in this way is much better than directly new a construction method with parameters (the construction method with name).

Of course, after reading many source codes, I believe that replacing the static construction method ofName with of will be more concise first:

@Accessors(chain = true)
@Setter
@Getter
@RequiredArgsConstructor(staticName = "of")
public class Student {
 @NonNull private String name;
 private int age;
}

 

Test code:

Student student = Student.of("zs");

Of course, it still supports chained calls:

Student student = Student.of("zs").setAge(24);

Writing code in this way is really concise and readable.

④. Using Builder

I don't want to explain the Builder mode any more. Readers can take a look at the Builder mode of Head First.

In fact, what we want to talk about today is a variant of the Builder mode, that is, the Builder mode of building beans. In fact, the main idea is to take you to see what Lombok has brought to us.

Take a look at the original Builder state of the Student class:

public class Student {
 private String name;
 private int age;
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public int getAge() {
 return age;
 }
 public void setAge(int age) {
 this.age = age;
 }
 public static Builder builder(){
 return new Builder();
 }
 public static class Builder{
 private String name;
 private int age;
 public Builder name(String name){
 this.name = name;
 return this;
 }
 public Builder age(int age){
 this.age = age;
 return this;
 }
 public Student build(){
 Student student = new Student();
 student.setAge(age);
 student.setName(name);
 return student;
 }
 }
}

 

Calling method:

Student student = Student.builder().name("zs").age(24).build();

Such Builder code makes me sick, so I plan to reconstruct this code with Lombok:

@Builder
public class Student {
 private String name;
 private int age;
}

Calling method:

Student student = Student.builder().name("zs").age(24).build();

⑤. proxy pattern

As we know, calling the Rest interface in a program is a common behavior. If you have used Spring's RestTemplate like me, I believe you will be as disgusted with the non HTTP status code exception thrown by it as I am.

Therefore, we consider using RestTemplate as the bottom wrapper to design the wrapper mode:

public abstract class FilterRestTemplate implements RestOperations {
 protected volatile RestTemplate restTemplate;
 protected FilterRestTemplate(RestTemplate restTemplate){
 this.restTemplate = restTemplate;
 }
 //realization RestOperations All interfaces
}

 

Then, the FilterRestTemplate is wrapped and extended by the extension class:

public class ExtractRestTemplate extends FilterRestTemplate {
 private RestTemplate restTemplate;
 public ExtractRestTemplate(RestTemplate restTemplate) {
 super(restTemplate);
 this.restTemplate = restTemplate;
 }
 public <T> RestResponseDTO<T> postForEntityWithNoException(String url, Object request, Class<T> responseType, Object... uriVariables)
 throws RestClientException {
 RestResponseDTO<T> restResponseDTO = new RestResponseDTO<T>();
 ResponseEntity<T> tResponseEntity;
 try {
 tResponseEntity = restTemplate.postForEntity(url, request, responseType, uriVariables);
 restResponseDTO.setData(tResponseEntity.getBody());
 restResponseDTO.setMessage(tResponseEntity.getStatusCode().name());
 restResponseDTO.setStatusCode(tResponseEntity.getStatusCodeValue());
 }catch (Exception e){
 restResponseDTO.setStatusCode(RestResponseDTO.UNKNOWN_ERROR);
 restResponseDTO.setMessage(e.getMessage());
 restResponseDTO.setData(null);
 }
 return restResponseDTO;
 }
}

 

The wrapper ExtractRestTemplate perfectly changes the behavior of exception throwing, making the program more fault-tolerant. Here, we don't consider the functions completed by ExtractRestTemplate. Let's focus on FilterRestTemplate and "implement all interfaces of RestOperations". This operation can't be completed in a moment and a half. At that time, I wrote it for almost half an hour before refactoring, as follows:

public abstract class FilterRestTemplate implements RestOperations {
 protected volatile RestTemplate restTemplate;
 protected FilterRestTemplate(RestTemplate restTemplate) {
 this.restTemplate = restTemplate;
 }
 @Override
 public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
 return restTemplate.getForObject(url,responseType,uriVariables);
 }
 @Override
 public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
 return restTemplate.getForObject(url,responseType,uriVariables);
 }
 @Override
 public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
 return restTemplate.getForObject(url,responseType);
 }
 @Override
 public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
 return restTemplate.getForEntity(url,responseType,uriVariables);
 }
 //Other implementation codes are slightly...
}

 

I believe you will feel sick and nauseous after reading the above code. Later, I optimized my code with the proxy annotation provided by Lombok (@ Delegate):

@AllArgsConstructor
public abstract class FilterRestTemplate implements RestOperations {
 @Delegate
 protected volatile RestTemplate restTemplate;
}

 

These lines of code completely replace the lengthy code mentioned above.

Isn't it very concise? Be a programmer who hugs Lombok.

3, Reconstruction

1. Demand case

①. Project requirements

In the project development stage, there is a demand for ordering and shipping:

If you place an order before 3 pm today, the delivery time is tomorrow. If you place an order after 3 pm today, the delivery time is the day after tomorrow. If the determined time is Sunday, then add another day to this time as the delivery time.

②. Thinking and reconstruction

I believe this requirement seems very simple and can be completed no matter how it is written.

Many people may see this requirement and start to write Calendar or Date for calculation to complete the requirement.

My advice is to carefully consider how to write code and then write it. It doesn't mean that all time operations are solved by Calendar or Date. It must depend on the scenario.

For time calculation, we should consider joda time, a similar mature time calculation framework, to write code, which will make the code more concise and easy to read.

Let's see how you can complete this code in Java. Let's see how you can complete this code first:

final DateTime DISTRIBUTION_TIME_SPLIT_TIME = new DateTime().withTime(15,0,0,0);
private Date calculateDistributionTimeByOrderCreateTime(Date orderCreateTime){
 DateTime orderCreateDateTime = new DateTime(orderCreateTime);
 Date tomorrow = orderCreateDateTime.plusDays(1).toDate();
 Date theDayAfterTomorrow = orderCreateDateTime.plusDays(2).toDate();
 return orderCreateDateTime.isAfter(DISTRIBUTION_TIME_SPLIT_TIME) ? wrapDistributionTime(theDayAfterTomorrow) : wrapDistributionTime(tomorrow);
}
private Date wrapDistributionTime(Date distributionTime){
 DateTime currentDistributionDateTime = new DateTime(distributionTime);
 DateTime plusOneDay = currentDistributionDateTime.plusDays(1);
 boolean isSunday = (DateTimeConstants.SUNDAY == currentDistributionDateTime.getDayOfWeek());
 return isSunday ? plusOneDay.toDate() : currentDistributionDateTime.toDate() ;
}

 

When reading this code, you will find that I regard the judgment and possible different results as a variable, and finally return them as a three eye operator. This elegance and readability are obvious. Of course, this code is not achieved overnight. I optimized the code generated three times. Readers can compare their own code with the code I wrote.

③. Improvement method

If you have been a programmer for 3 years, I believe you can easily complete the above requirements, but if you want to be a programmer who can write Java, think about and refactor the code.

Writing code is like writing. Everyone can write the same words, but whether it looks good or not is not certain. If you want to write a good program, you must constantly think and refactor, dare to try and innovate, don't be conservative, and be an excellent Java programmer.

The best way to improve the level of code is to refactor in an organized way! (Note: it's an organized refactoring)

2. Design mode

Design patterns are tools, not an indicator of whether you are a high-level programmer.

I often see a programmer shouting excitedly, which program, which point I use the design pattern, how excellent and how good I write. When I read it carefully, I found that many of them were over designed.

①. Business driven technology or technology driven business

Business driven technology or technology driven business? In fact, this is a topic that has been debated all the time, but many people don't think so. I think it's just that we don't want to admit it. Let me give you a general analysis of how we should judge our position as a Java programmer.

  • Business Driven Technology: if your project is a project with little or no revenue, please don't engage in other innovative things, don't drive how the business should be done, but know what the pain point of the business is now? How can we help the business profit or make the project better and more smoothly.
  • Technology driven business: if your project is a great project, such as Taobao, I can communicate with the business while meeting the business needs, and what technology can better help the business create revenue. For example, when placing an order, I have to enter the queue, and the order status may not be processed until a few minutes later, but it will give users a smoother experience and earn more access traffic, Then I believe that the business is willing to be driven by technology and will agree to the delay of orders. This is technology driven business.

I believe most people are still in the direction of business driven technology.

So since you can't drive the business, please embrace business change.

②. Code design

I've been working on Java back-end projects, and there are often some changes. I believe everyone has encountered them.

For example, when we write a piece of code, we consider mapping the requirements into the state mode of the code. Suddenly, one day, a lot of behavior changes are added to the state mode. At this time, you scratch your head. You forcibly add too many behaviors and changes to the state mode.

Slowly, you will find that these state patterns are actually more like a cluster of algorithms. You should use the strategy pattern. At this time, you should be dizzy.

Having said so much, I mean, as long as you feel reasonable, please change the state mode to the strategy mode. All modes are not imagined out of thin air, but based on refactoring.

There is no silver bullet in Java programming. Please embrace business changes and keep thinking about refactoring, and you will have a better code design!

3. Are you really excellent?

I'm sorry I took such a boring title.

A popular programming method abroad is called pair programming. I believe many domestic companies have not done so. I will not talk about the benefits of pair programming. In fact, it is a process of improving each other while Code Review. Since you can't do this, how can you live in your own world and improve continuously?

"When developing at ordinary times, the code made is always considered to be correct and the writing method is perfect." I believe this is the voice of most people. I return to the question just now. How can I continuously improve in my own world?

The answer is:

  1. Look at the source code of the mature framework
  2. Look back at your code
  3. Diligent in reconstruction

Are you really good? If you finish learning the source code every week, look back at your code, and then be diligent in refactoring, I think you are really excellent.

Even if you're just getting started, you've always insisted that you're a programmer who can really write java code.

4, Skills

1. UML

I don't want to discuss more about UML, but I think if you really can write Java, please learn to express yourself first. UML is the language you speak. To be an excellent java programmer, please learn at least these two UML diagrams:

  1. Class diagram
  2. Sequence diagram

2. Clean Code

I think keeping the code concise and readable is the most basic guarantee of the code. If you reduce these two points one day for the efficiency of the program, I think it is understandable. In addition, there is no reason for you to waste your code at will.

  1. Readers can take a look at the book Clean Code published by Robert C. Martin
  2. You can refer to meituan article: "talk about Clean Code"
  3. You can also take a look at Alibaba's Java coding specification

Anyway, please keep your code clean and tidy.

3. Linux basic commands

In fact, this has nothing to do with being able to write Java, but Linux does host the container running Java many times. Please learn the basic commands of Linux well.

5, Summary

Java is a large system. Today's discussion does not involve the knowledge of framework and architecture, but just how to write good code.

From the small aspect of writing Java program to the large aspect, this paper expounds how to write m/mazi Java program well, and tells readers how to improve their coding level. I hope all of you who read this article can be an excellent java programmer.

Author: Sigma
Link: http://lrwinx.github.io/2017/03/04/%E7%BB%86%E6%80%9D%E6%9E%81%E6%81%90-%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BC%9A%E5%86%99java%E5%90%97/
Source: sigma blog

Tags: Java Programming Programmer IT

Posted by Zeceer on Wed, 25 May 2022 12:03:47 +0300