Has*_*hin 3 java constructor design-patterns
我的问题是:使用通过初始化的字段编写Java类构造函数的最佳方法是什么stdin?
例如,假设我有一个Employee类似于以下的类:
Public class Employee {
private int empID;
private String empName;
private List<Role> empRoles;
{....}
}
Run Code Online (Sandbox Code Playgroud)
我可以为这堂课写下所有的设定者和吸气剂.当然,Role该类将拥有自己的文件.
还假设我为前两个字段创建了我的setter,如下所示,以便让最终用户初始化字段:
public void setEmpID() {
System.out.println("Please enter the employee ID");
Scanner s = new Scanner (System.in);
this.empID = s.nextInt();
public void setEmpName() {
System.out.println("Please enter the employee name");
Scanner s = new Scanner (System.in);
this.empName = s.next();
}
Run Code Online (Sandbox Code Playgroud)
然后:
Scanner我在每个setter中创建的对象移动到构造函数并将其作为setter的参数更好例如:
public void setEmpName(Scanner s) {
...
this.empName = s.next();
}
Run Code Online (Sandbox Code Playgroud)
如您所见,这可能是一个设计问题,而不仅仅是"编码".
非常感谢您的帮助.
我认为您可能会将用户输入/输出与程序模型混淆.这里的关键是你应该把两者完全分开.Employee类应该完全不知道使用它的UI或I/O的类型,因为这样可以在GUI,控制台程序或其他任何需要的地方使用它.
因此,您的Employee构造函数应该只接收创建Employee对象所需的数据,无论其来源如何,并且对于您的字段getter也是如此.
因此,你的getter看起来就像你发布的一样,而是更加简单,更加"愚蠢"或"无知"的用户I/O(Scanner,System.in等)
public void getEmpID (int empID) {
this.empID = empID;
}
Run Code Online (Sandbox Code Playgroud)
其他领域也一样.
所有的I/O东西 - Scanner类等都在驱动程序类的其他地方.
旁注:当您使用基于System.in程序的扫描程序时,应创建一个且只有一个这样的野兽,在需要时创建它,然后仅在程序完全使用它时关闭并处理它.否则,您可能会因过早关闭连接而破坏系统输入.这是在您创建多个Scanner对象时不使用您提议的代码的另一个原因.
例如....
import java.util.ArrayList;
import java.util.List;
public class Employee {
private int empID;
private String empName;
private List<Role> empRoles;
public Employee(int empID, String empName) {
super();
this.empID = empID;
this.empName = empName;
empRoles = new ArrayList<>();
}
public int getEmpID() {
return empID;
}
public void setEmpID(int empID) {
this.empID = empID;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public List<Role> getEmpRoles() {
return empRoles;
}
public boolean addEmpRole(Role role) {
return empRoles.add(role);
}
public boolean removeEmpRole(Role role) {
return empRoles.remove(role);
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以在其他地方使用它:
import java.util.Scanner;
public class TestEmployee {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("Enter employee ID: ");
int empID = scan.nextInt();
scan.nextLine(); // handle dangling end of line token
System.out.print("Enter employee Name: ");
String empName = scan.nextLine();
Employee employee = new Employee(empID, empName);
// if we are **totally** done with the Scanner, now we may close it
scan.close();
}
}
Run Code Online (Sandbox Code Playgroud)
实际上,您不依赖于使用特定构造函数填充对象字段的方法,而是依赖于无参数构造函数.
您确实选择了一个setter方法来Employee在调用后填充实例的字段new Employe().
但是这种setter方法很复杂,因为你混合了太多的责任:获取用户输入和设置对象的状态.
我可以在覆盖默认构造函数的构造函数中使用此类setter.
不,这没有任何意义:构造函数和setter是两种不同的方式,你不能用另一种方式覆盖它们.
但是,您可以通过依赖Scanner实例来接受用户输入来调用构造函数中的setter,但与实际的setter方法类似,这似乎是一种尴尬的方法,因为它给构造函数带来了太多的责任.
这是编写此类构造函数的最佳方法吗?
使用填充所有字段的构造函数,即:
Employee emp = new Employe(id, name, roles)
Run Code Online (Sandbox Code Playgroud)
如果您的对象在创建后被设计为不可变的,那么这是有意义的.
在您的实际情况中,如果您的对象不是使用构造函数设置为不可变的,或者setter是有效的,但无论如何,您应该提供setter.
因此,要回答您的问题,您应该分离职责(获取用户输入和设置对象状态),并根据您对以下实例的要求使用setter或构造函数方法Employee:
Employee emp = new Employe(id, name, roles)
Run Code Online (Sandbox Code Playgroud)
要么
Employee emp = new Employe();
emp.setId(...);
emp.setName(...);
emp.setRoles(...);
Run Code Online (Sandbox Code Playgroud)