3

我有一个通用Person类和两种类型的人StudentTeacher并且扩展了Person该类。我也有教学课程,其中将存储学生列表和将在该课程中的教师列表。

class Person {}
class Student extends Person {}
class Teacher extends Person {}

class Session {
  List<Student> students = new ArrayList();
  List<Teacher> teachers = new ArrayList();

  // add a person to the teaching session.
  // They should be placed into the appropriate lists
  public void enroll(Person p)
  {
    if (p instanceof Student)
      students.add((Student) p)
    else if (p instanceof Teacher)
      teachers.add((Teacher) p)
  }
}

这个想法是,一些其他代码将有一个人员列表并迭代列表以根据需要将他们注册到适当的会话中。但是,该enroll方法当前显式检查对象的类型,这对我来说是不可取的,并且似乎是糟糕的设计。

我尝试enroll过使用方法重载来编写这样的方法,看起来更干净

public void enroll(Student p)
{
    students.add(p)
}

public void enroll(Teacher p)
{
    teachers.add(p)
}

但似乎遍历Person对象列表的代码需要确定当前人是学生还是教师实例,并在将其传递给enroll方法之前进行适当的类型转换。

有没有办法让我设计这个,这样我就不需要instanceof在我自己的代码中的任何时间点调用?

4

5 回答 5

7
  1. Session需要有一个重载的注册方法,就像你的问题一样。
  2. abstract enroll将方法添加到作为参数的PersonSession

    public abstract void enroll (Session s);

  3. TeacherStudent每个覆盖enroll

    public void enroll (Session s) {
         s.enroll(this);
    }
    
于 2013-05-28T17:49:06.650 回答
1

最简单的方法是向isTeacher()person 类添加一个方法。

这是一个基本实现(您已经编写的部分省略了)

abstract class Person {
  public abstract boolean isTeacher();
}

class Teacher extends Person {
  public boolean isTeacher() { return true; }
}

class Student extends Person {
  public boolean isTeacher() {return false; }
}

abstract class Session {
  public void enroll(Person p) {
    if (p.isTeacher()) teachers.add((Teacher)p);
    else student.add((Student)p);
  }
}

如果您还想避免类型转换,请尝试此...

abstract class Person {
  public abstract Teacher asTeacher();
  public abstract Student asStudent();
}

class Student extends Person {
  public Student asStudent() { return this; }
  public Teacher asTeacher() { return null; }
}

class Teacher extends Person {
  public Student asStudent() { return null; }
  public Teacher asTeacher() { return this; }
}

class Session extends Person {
  public void enroll(Person p) {
    Teacher t = p.asTeacher();
    if (t != null) teachers.add(t);
    else students.add(p.asStudent());
  }
}

这个实现有一个小弱点,它假设教师和学生是唯一的人类型,但将其扩展到多种类型是相当简单的。

于 2013-05-28T17:34:45.363 回答
1

问题p instanceof Student在于您根据不是方法调用的内容更改行为,因此将来的更改将很困难(即,如果您添加另一个 Person 子类)。

有几种方法可以查看这个设计问题:

  • Why you need to create a hierarchy of Person? It's possible in your system that someone that is a student can be a teacher in another course? If that is the case, you have some information about a person, and what changes is the role of that person for a course. So is not convenient to use inheritance for that, use composition and create an object that represents the role of a Person in a course.

  • You can add a isTeacher method, or use a visitor pattern to split the collection. That will be a little bit better than the instanceof. But in my opinion the problem is still in the incorrect class hierarchy.

For example, using "roles":

enum Role { TEACHER, STUDENT; }

class Session {
   List<SessionEnrolment> enrollments = new ArrayList<>();

   public void enroll(Person p, Role role)
   {
       enrollments.add(new SessionEnrollment(p, role));
   }

   public List<Person> getTeachers() {
      List<Person> result = new ArrayList<>();
      for (SessionEnrolment e : enrollments) {
            if (e.isTeacher()) { result.add(e.getPerson()); }
            return result;
      }
   }
}

Using a isTeacher method:

public void enroll(Person p)
{
    if (p.isTeacher()) { teachers.add(p); } else { students.add(p); }
}

As you can see having a isTeacher method for all persons looks awkward. And the role solution reflects better the temporal property of being a student or teacher.

You can use a visitor, and it will save you from the ugly isTeacher in Person; but to me that solution is overcomplicated, and you'll translate the "uglyness" to the Visitor interface.

于 2013-05-28T17:57:45.260 回答
0

您可以使用每个具体实现的重载方法来执行您建议的操作Person,此外还可以将类型添加到列表中:

List<Student> students = new ArrayList<>();
List<Teacher> teachers = new ArrayList<>();

然后在你的迭代逻辑中你知道类型。

于 2013-05-28T16:53:20.107 回答
0

你可以把它转过来:

class Session {
  StudentList students = new StudentList();
  TeacherList teachers = new TeacherList();

  // add a person to the teaching session.
  // They should be placed into the appropriate lists
  public void enroll(Person p)
  {
     p.addMe(students, teachers);
  }
}

public class StudentList extends ArrayList<Student> {
}

public class TeacherList extends ArrayList<Teacher> {
}

public abstract class Person {
    public abstract void addMe(StudentList sList, TeacherList tList);
}

public class Student extends Person {
    public void addMe(StudentList sList, TeacherList tList) {
        sList.add(this);
    }
}

public class Teacher extends Person {
    public void addMe(StudentList sList, TeacherList tList) {
        tList.add(this);
    }
}
于 2013-05-28T16:54:21.663 回答