用作子类型时可用的超类型的单一"从数据存储加载"方法



我有两个类,PersonEmployeeEmployee扩展Person .

我有一个从存储中读取Person的方法,并且正在编写读取Employee的方法。

我想重用我为Person读取与Employee相同的属性的方法,而无需复制粘贴代码,但似乎找不到一种方法。

public Person getPersonFromStorage() {
    Person person = new Person();
    // ... logic
    return person;
}
public Employee getEmployeeFromStorage() {
    Employee employee = new Employee();
    // ... logic for employee-specific fields
    // I want to read the fields inherited from Person here
    return employee;
}

我无法从getPersonFromStorage中转换检索到的Person,因为它不是Employee。可能是,因为它也不是另一种亚型,但事实并非如此。

我可以执行以下操作:

public Person getPersonFromStorage(Person person) {
    if(person==null) { person = new Person(); }
    // ... logic
    return person;
}
public Employee getEmployeeFromStorage() {
    Employee employee = (Employee) getPersonFromStorage(new Employee());
    // ... logic for employee-specific fields
    return employee;
}

但如果可以的话,我想避免这种复杂性。我有一种感觉,我忽略了一些基本的东西。有没有更好的方法来解决这个问题?

只是提供我通常在这种情况下使用的不同架构。如果你谈论的是"来自存储",对我来说,这意味着某种持久的结构。文本文件、数据库等对于以下示例,假设您的值位于文本文件中。

假设文件employees.txt ,其中包含一个员工:

// Dave's person details.
Dave
Manchester
32
// Dave's employee details
Assassin
Mostly North Korea.

然后你有一个类Person,看起来有点像这样:

public class Person
{
    private String name;
    private String location;
    private int age;
    public Person(String name, String location, int age)
    {
       // blah blah blah.
    }
}

Employee如下所示:

public class Employee extends Person
{
    private String jobTitle;
    private String area;
    public Employee() { 
       // etc.
    }
}

Person 类中,可以创建一个构造函数,旨在读取Person的参数。像这样:

public Person(Scanner file)
{
    this.name = file.nextLine();
    this.location = file.nextLine();
    this.age = file.nextInt();
    file.nextLine(); // Make sure you're pointing at the new line!
}

Employee 类中,您可以创建一个构造函数,旨在读取员工的参数,同时调用它的超类来处理其他值。

public Employee(Scanner file)
{
    super(file);
    this.jobTitle = scanner.nextLine();
    this.area = scanner.nextLine();
}

然后你所要做的就是调用它:

Scanner s = new Scanner("employees.txt");
Person p = new Employee(s);

或者使其更紧凑:

Person p = new Employee(new Scanner("employees.txt"));

这将去解析文件,并返回一个对象,同时在需要数据的类中包装实际读取文件的所有逻辑。

不是文本文件?

好吧,这并不重要。重要的是将对象传递到调用链上,这些方法正在执行该特定类需要执行的操作,然后传递该对象。如果是database row,原理完全一样。

你的第二个代码示例是要走的路,除了你甚至不需要null检查行。只需传入您在其他地方实例化的非空Person即可。

为了获得更好的抽象,请查看是否可以将Person变成abstract类。

更优雅的方法是重载Employee构造函数,以便能够从父实例Person创建Employee实例。

public Employee getEmployeeFromStorage() {
    Employee employee = new Employee(getPersonFromStorage());
    // ... logic for employee-specific fields
    return employee;
}

您可以使用受保护的工厂方法。

protected Person createNewInstance() { return new Person(); }

并在 getPersonFromStorage(( 方法中使用它。然后,子类将重写此方法,从而将返回类型更改为 Employee,然后可以像第二个示例中一样使用它。

public class Person {
    public Person getPersonFromStorage() {
        Person person = createNewInstance();
        // ... logic
        return person;
    }
    protected Person createNewInstance() {
        return new Person();
    }
}
public class Employee extends Person {
    public Employee getEmployeeFromStorage() {
        Employee employee = (Employee) getPersonFromStorage();
        // ... logic for employee-specific fields
        return employee;
    }
    protected Person createNewInstance() {
        return new Employee();
    }
}

或者,您也可以基于人员创建员工构造函数

public class Employee extends Person {
    public Employee(Person person) {
        super();
        // copy all fields from person 
    }
    public static Employee getEmployeeFromStorage() {
        Employee employee = new Employee(getPersonFromStorage());
        // ... logic for employee-specific fields
        return employee;
    }
}

我还向方法添加了静态,假设您打算创建与现有对象没有直接关系的新实例。这不适用于第一个变体。

相关内容

最新更新