我有一个通用的Person
类,以及两种类型的人Student
和Teacher
,它们扩展了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
了?
答案 0 :(得分:7)
Session
需要像您的问题一样重载注册方法。将abstract enroll
方法添加到以Person
作为参数的Session
类
public abstract void enroll (Session s);
Teacher
和Student
各自覆盖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);
}
}