This article uses examples to discuss a design scenario of using default constructor with caution. -- Take Java as an example.
To facilitate the discussion, we assume that a student needs to be modeled, including two attributes: name and place of birth. We can see a lot of the following code:
public class Student {private String name = null;private String birthPlace = null;public Student() {}public void setName(String name) {this.name = name;}public void setBirthPlace(String birthPlace) {this.birthPlace = birthPlace;}public String getName() {return name;}public String getBirthPlace() {return birthPlace;}}
Example:
import org.junit.Test;public class StudentTest { @Test public void test() { final String NAME = "John"; final String BIRTH_PLACE = "Hawaii"; Student student = new Student(); student.setName(NAME); student.setBirthPlace(BIRTH_PLACE); assert(NAME.equals(student.getName())); assert(BIRTH_PLACE.equals(student.getBirthPlace())); }}
The common method is to use the default constructor to create an object and then call the setter method of each attribute.
The proper semantics of the above design method is:
- Each property of an object can have a default value, and the default value is meaningful;
- The object property value is variable, that is, you can use setter to modify the current property value.
There are many objects that conform to this semantics. For example, a point point2d on a (two-dimensional) plane is suitable for the above design method.
This article discusses another situation, that is, the following semantic requirements:
- Each attribute does not have an appropriate default value;
- Once an object is created, its attribute values are also clear;
- The attribute value cannot be changed.
For the student example we mentioned earlier, we assume that the name cannot be modified. If there is a new name, we need to add an attribute (such as aliasname), and the place of birth is also fixed. In this case, although the Code provided above can work well, it is not appropriate in terms of design semantics.
In this case, we recommend the following:
- No default constructor is provided (a private default constructor must be defined or declared in C ++ );
- Provides constructors with parameters. The parameter list includes attribute values that must be specified when an object is created;
- The getter method cannot be provided for data members whose attribute values cannot be modified.
Therefore, the previous class student must re-construct the following code:
public class NewStudent {private String name = null;private String birthPlace = null;public NewStudent(String name, String birthPlace) {this.name = name;this.birthPlace = birthPlace;}public String getName() {return name;}public String getBirthPlace() {return birthPlace;}}
Example:
@Testpublic void testNewStudent() {final String NAME = "John";final String BIRTH_PLACE = "Hawaii";NewStudent student = new NewStudent(NAME, BIRTH_PLACE);assert(NAME.equals(student.getName()));assert(BIRTH_PLACE.equals(student.getBirthPlace()));}
Additional benefits: Through design optimization, three statements are converted into one statement when we create an object. In this way, the brain's thinking costs are significantly reduced.
-- You must know that a project usually has a large amount of code. It is very common to have thousands of codes. When there is a variety of low-quality semantic code everywhere, the entire project will produce a lot of invalid code, which is a terrible thing for project maintenance.
Every line of code we write is to express a design idea to readers (including yourself. The code that can work is not necessarily good quality code. Obviously, we are pursuing the latter.