【问题标题】:Constructing linked Java objects from constructors从构造函数构造链接的 Java 对象
【发布时间】:2015-07-24 08:51:51
【问题描述】:

我一直在试图解决 OOD 问题。我正在尝试构建一个具有三个类的程序 - 一个称为 Person ,它具有一个由个人信息组成的构造函数。另一个名为 Doctor 的类继承了 Person 类,并简单地构造了一个带有额外字段的对象(大多数字段,即姓名,地址将指向人员对象)称为专业。我使用另一个类来构建 java 应用程序来创建、查看和修改人员和医生对象中的字段。

我已经构建了 person 和 Doctor 类,你可以在这篇文章的底部找到它们的相关代码。当我尝试创建医生对象时会出现问题,因为我的代码似乎正在创建一个与人员对象没有任何关系的全新医生对象。我尝试编辑人员对象的 firstName 字符串,但没有更新医生对象以反映这一点。它似乎已经创建了自己的永久名称,而不是简单地指向人员对象中定义的名称。

我通过用户输入创建人员对象并使用以下行存储它们。

        persons[amountPersons] = new Person (firstName, lastName,   homeAddress, phoneNumber);
        amountPersons++;

一旦创建了person对象,用户可以输入personNumber并像这样创建一个医生对象

    person = getPersonID(personID);

        int personNumber;
        String firstName = null; 
        String lastName = null;
        String homeAddress = null;
        String phoneNumber = null;


        firstName = person.firstName;
        lastName = person.lastName;
        homeAddress = person.homeAddress;
        phoneNumber = person.phoneNumber;
        personNumber = person.personNumber;

        System.out.print ("Enter speciality of the doctor: ");  
        String speciality = input.nextLine();

        doctors[amountDoctors] = new Doctor (firstName, lastName, homeAddress, phoneNumber, speciality);
        amountDoctors++;

我到底做错了什么?为大量代码道歉 - 我已尽我所能精简它,但我不确定问题出在哪里,所以我只能做这么多。

人物类

public class Person {

//Instance Variables 
protected String firstName;
protected String lastName;
protected String homeAddress; 
protected String phoneNumber; 
protected int personNumber;

private static int NumberSystem = 1;



public Person()
{
firstName = " ";
lastName = " ";
homeAddress = " ";
phoneNumber = " ";
personNumber = 0;
}


public Person (String firstName, String lastName, String homeAddress, String phoneNumber)
{
// Initialize instance variables
this.firstName = firstName;
this.lastName = lastName;
this.homeAddress = homeAddress;
this.phoneNumber = phoneNumber;
personNumber = NumberSystem;
NumberSystem = NumberSystem + 1;
}


public String toString()
{
    String p;
    p = "=================================================" +"\n" +
        "Identification Number: " + personNumber +"\n" +
        "Name: " + firstName +"\n" +
        "Surname: " + lastName +"\n" +
        "Address: " + homeAddress +"\n" +
        "Mobile/Telephone: " + phoneNumber +"\n";
return p;

}

医生

public class Doctor extends Person{

// the aim of Doctor subclass is to simply add a speciality field
private String speciality;
int doctorID;

public Doctor() {
    doctorID = 0;
    speciality = "none";
}


public Doctor(String firstName, String lastName, String homeAddress, String phoneNumber, String speciality) {
    super(firstName, lastName, homeAddress, phoneNumber);
    this.speciality = speciality;
    }


public String toString()
{
    String d;
    d = "=================================================" +"\n" +
        "Doctor Number: " + doctorID +"\n" +
        "Person Number: " + personNumber +"\n" +
        "Name: " + firstName +"\n" +
        "Surname: " + lastName +"\n" +
        "Address: " + homeAddress +"\n" +
        "Mobile/Telephone: " + phoneNumber +"\n" +
        "Speciality: " + speciality +"\n";
return d;

}


}

【问题讨论】:

  • 听起来你不是在寻找继承,而是在寻找组合。目前,当您拥有Doctor 时,它也是Person,这意味着您可以在任何可以使用Person 的地方使用它。不过,这不会将其链接到任何其他 Person 对象。为此,您需要使 Doctor 具有 Person 字段,而不是使其成为子类。

标签: java arrays object inheritance polymorphism


【解决方案1】:

在设计两个相互关联的对象时,有两种常用的模式。

  • 继承。继承是一个强大的工具,但它不是灵丹妙药,它有自己的问题和局限性。仅当 B 类与 A 类具有 is a 关系时,才应使用继承。例如,山地自行车 IS A 自行车,因此使用 MountainBikeextend 是明智的。自行车'。如果你不能说存在“是”关系,那么就不要使用继承。

  • 作文。组合也是一个强大的工具,可以说它比继承更健壮、更不脆弱。当 B 类是 A 类的一个属性时(或当 A 类有一个 B 类)时,使用组合。在这种情况下,您将 B 类设置为 A 类的实例字段。

在您描述的模型中,医生是一个人,因此使用继承似乎很明智。由于我们只能看到您的领域模型是您展示的两个类,因此我也会选择让 Doctor 扩展 Person。

就风格而言,您应该养成使用@Override 注释的习惯,只要您打算覆盖基类中的方法。当你这样做时,编译器会提醒你,否则很难发现错误。您应该使用@Override 注释toString() 方法(继承自Object)。

当我尝试创建医生对象时出现问题,因为我的代码看起来 创建一个没有关系的全新的医生对象 任何人对象。

当你创建一个 Doctor 实例时

Doctor doc = new Doctor(...);

你得到的对象既是它自己的类型,也是它的父类型,例如。医生医生,医生人。以下是合法的:

Person person = new Doctor(...);

您可以将子类中的对象分配给其超类(或父类)的变量。没有创建新的 Person 对象,只有 Doctor 对象(可以将其分配给 Person 变量,因为医生是人)。

这是不允许的:

Doctor doc = new Person(...);

所有的医生都是人,但是……不是所有的人都是医生,所以这个任务会有问题。编译器不会让你这样做。

【讨论】:

  • 感谢两者之间的解释 - 我一定在某些时候对继承和组合感到困惑/困惑。我想我必须对这些主题进行一些阅读,并尝试刷新我的想法。但是,我有最后一个问题。让我们忽略我最初的问题,我将组合设计技术与继承混合在一起。你会说医生和人员类以及我初始化这些类的对象的方式是继承的好例子吗?
  • 您的对象模型没有问题。您应该始终尝试设计不可变的值类。我会将两个类中的实例字段设为最终字段(除非你不能;必须符合 JavaBeans 协定的值对象不能有最终字段)。最后,构造函数有很多参数。如果您打算长期维护这些类,它们看起来很适合 Builder 模式。
【解决方案2】:

您使用person = getPersonID(personID); 获得的人与您使用doctors[amountDoctors] = new Doctor (firstName, lastName, homeAddress, phoneNumber, speciality) 创建的医生完全不同。

仅仅因为它们具有相同的例如firstName 等不代表对象有任何关系。

您可以通过多种方式解决此问题。例如,您不能创建一个 Person 后跟一个 Doctor,而只能创建一个 Doctor。但是,这并不能反映现实生活,因此您可以将存储在数组中的 Person 替换为 Doctor。

Person[] people = // however you initialise it;
Doctor[] doctors // however you initialise it;

由于医生也是人,您可以简单地说people[i] = doctor。根据您上面的代码,它可能被包装在这样的方法调用中:

// I'm assuming personID is an int, but you may need to adapt this
private void replacePerson(int personID, 
                           Person person) {
    int index = // get index of personID from the array
    people[index] = person;
}

并用

调用它
Doctor doctor = new Doctor (firstName, lastName, homeAddress, phoneNumber, speciality);
doctors[amountDoctors] = doctor;
amountDoctors++;
replacePerson(personID, doctor);

【讨论】:

    【解决方案3】:

    一旦你创建了一个Doctor,它就会得到他自己的成员变量。这样想:

    你有一个Person,他的夹克上有一个铭牌、地址牌等。现在你交出这个Person他的盘子。之后你想把他提升为Doctor,但由于Doctor继承了Person,他也有自己的铭牌、地址牌等。 他们没有分享它。

    所以你要做的是使用Composition。 例如

    public class Doctor(){
      private Person person;
      private String speciality;
      public Doctor(Person person, speciality){
       this.person = person;
       this.speciality = speciality;
      }
    }
    

    现在,如果您更改 Persons 的名称,它还将更新所有指向该人的 Doctors 的名称。

    【讨论】:

      【解决方案4】:

      发生的就是应该发生的。医生是人,但人不是医生。

      现在,当您创建 Person 对象时,您会创建 Person 类的实例,并将其作为对象放置在堆上。

      然后,您从该对象中检索各种字段,并使用它们来初始化 Doctor 的新实例,并将这个新对象放入堆中。这是一个独特的 Doctor 类型的新对象,它也是 Person 类型,但它不是您创建的旧 Person 对象。这是继承。以您期望的方式工作将是灾难性的。

      组合不是解决问题的正确方法,因为医生是一个人而不是有一个人。

      你可以做的是使用 Decorator 模式并让 Doctor 成为 Person 可以或不可以拥有的属性。一个好的教程是this

      【讨论】:

        【解决方案5】:

        看起来您期望继承的行为不会发生。 Doctor 对象与单独的 Person 对象没有某种关系。它Doctor是一个Person,因此它包含Person中定义的所有字段,以及Doctor中定义的字段。

        1. 您的 person[] 数组可以存储 Person 和 Doctor 类。但如果您使用new Person() 创建成员,他们将永远不会是医生。
        2. 不清楚为什么你有一个 Person 数组和一个 Doctor 数组。
        3. 继承是一个编译时概念。听起来在运行时您希望医生同时拥有 Person 记录和 Doctor 记录。您将只有一个 Doctor 记录,由于继承,该记录将包含所有 Person 字段。

        可能有点离题,您应该重新考虑继承是否是正确的方法。 @Dragondraikk 的评论是准确的。在我看来,继承的“Is a”测试应该替换为“Is a for the life of the application”。成为医生是一个人所扮演的受时间限制的角色。这指向使用组合。这样一来,一个人一旦获得资格就可以成为一名医生,如果他们被淘汰,可以恢复为一个人。在对象的生命周期内更改其具体类型绝不是一个好主意。

        如果我误解了您的问题,请提前道歉。

        【讨论】:

          【解决方案6】:

          当Doctor对象存入内存后,分配就完成了 用于超类 (Person) 中的字段,然后是 Doctor 类中的字段。

          所以当医生对象存储在内存中时,它是一个完整的对象而不是 一半是人,一半是医生。

          JVM 对象知道连接到 Doctor 对象内存分配的结构是 Doctor 对象的唯一方法是通过 4 字节数量标记为“指向类信息的本机指针”。

          类型安全转换和 instanceof 运算符等使用此信息来确定此内存分配属于哪个类结构 到。

          所以是的,确实是前面提到的继承关系 答案,因为医生是一个人(如果医生有病人,那么它就是作文)

          Bill Venners 的这篇文章对该主题进行了更详细的解释。 http://www.artima.com/designtechniques/initializationP.html

          更重要的一点是,即使 Person 具有在 Doctor 中无法直接访问的私有变量,当内存分配给一个 医生对象为私有变量分配了内存 清楚地表明为超类和子类设置的整个变量 一起构成了子类。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-09-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-20
            • 2021-10-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多