处理某些抽象类的子类的重载方法

时间:2013-05-28 16:45:11

标签: java

我有一个通用的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对象列表的代码需要确定当前人是学生还是教师实例,并在将其传递给{{{}之前进行适当的类型转换。 1}}方法。

我有没有办法设计这个,这样我就不需要在自己的代码中随时调用enroll了?

5 个答案:

答案 0 :(得分:7)

  1. Session需要像您的问题一样重载注册方法。
  2. abstract enroll方法添加到以Person作为参数的Session

    public abstract void enroll (Session s);

  3. TeacherStudent各自覆盖enroll

    public void enroll (Session s) {
         s.enroll(this);
    }
    

答案 1 :(得分:1)

执行此操作的最简单方法是向person类添加isTeacher()方法。

这是一个基本的实现(你已经写过的部分遗漏了)

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());
  }
}

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

答案 2 :(得分:1)

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

有几种方法可以解决这个设计问题:

  • 为什么需要创建Person的层次结构?在您的系统中,作为学生的人可以成为另一门课程的老师吗?如果是这种情况,您可以获得有关某人的一些信息,以及该人员对课程的作用有何变化。因此使用继承不方便,使用组合并创建一个表示课程中Person角色的对象。

  • 您可以添加isTeacher方法,或使用访问者模式拆分集合。这比instanceof好一点。但在我看来,问题仍然存在于错误的类层次结构中。

例如,使用“角色”:

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;
      }
   }
}

使用isTeacher方法:

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

正如你所看到的,对所有人都采用isTeacher方法看起来很尴尬。角色解决方案更好地反映了作为学生或教师的时间属性。

您可以使用访问者,它可以帮助您避免isTeacher中丑陋的Person;但对我来说,解决方案过于复杂,你会将“丑陋”翻译成Visitor界面。

答案 3 :(得分:0)

您可以根据具体Person实现的重载方法执行您的建议,除此之外,还要向List添加一个Type:

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

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

答案 4 :(得分: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);
    }
}