OOP - 一个班级如何成为另一个班级?

时间:2016-02-11 15:45:35

标签: java oop domain-driven-design

一个人有身份证。 学生有学生证。 司机有驾驶执照。

  • 一个人上学并成为学生。
  • 一名学生毕业并成为普通人。
  • 一个人获得驾驶执照并成为驾驶员。
  • 学生获得驾驶执照并成为驾驶员。

这些事情是否只是状态改变,如下例所示:

class Person {
    ID id;
    StudentID stId;
    DriverLicense license;

    void drive() {
        if(license == null) //illegal state exception
        //drive
    }

    //bla bla
}

还是继承?由于对象是数据+行为,新数据和能够做新事物应该保证新对象

class Student extends Person {
    //
}

class Driver extends Person {
    //
}

//things get messy here, in Java you can't extend multiple class
//what if there's a rule that, student drivers can request/get a tax reduction?
class DriverStudent extends Person, Driver {
    //
}

PLUS,更重要的是,如何成为另一个?通过方法或构造函数或第三类(如服务或聚合)?

class Person {
    Driver getADriversLicense() {
        //create and return a Driver
        //this person still exists but now there's a driver with this person's data
    }
}

or:

class Driver extends Person {
    public Driver(Person p) {
        //constructor
    }
}

or:

class Aggregate {
    Driver giveDriversLicense(Person p) {
        // access internal state of both objects(ditch encapsulation) and return a driver?
        // put aggregate in same package with Driver and Person and use package private methods to provide encapsulation?
    }
}

4 个答案:

答案 0 :(得分:5)

看到这种关系的更好方法是通过角色,即一个人可以扮演多个角色。

一个人可以是司机和学生,也可能是员工的场景怎么样?

所以IMO最好代表如下 -

class Person{

     List<Role> currentRoles ; 

     List<Role> getCurrentRoles(){ 
         return currentRoles ; 
     }

     public void addRole(Role role){
          currentRoles.add(role) ; // so on
     }  
}

使用泛型和类型安全转换,您可以轻松检索特定角色并在其上调用相关操作。

public interface DriverRole implements Role {

    License getDriversLicense() ;
} 

编辑:进一步完全回答您的问题,它可以轻松解决人员获得或失去角色的情况,即添加或删除角色。正如评论中所指出的,这种关系最好通过Has&lt; 0..M&gt;来表示。那么关系就是那种关系。

编辑1 相比之下,当您使用装饰器模式时,您的原点被包装,并且太多装饰器可以创建聚合链,而IMO不是理想的场景,或者它将导致装饰器继承链这不是我喜欢的。

已经说过,根据具体情况,一个特定的模式可能比另一个特定模式更合适,但在您给出的示例中,我认为角色的简单聚合是最好的。

答案 1 :(得分:3)

继承是思考这些关系的一种方式,但只有在枚举此人可能拥有的状态组合的数量时,这才有意义。

另一个想到这一点而不是通过继承限制自己的人是将每个人都视为具有不同凭据的人。

不是一个人是司机,而是将每个人都视为具有一组凭据的通用人员。例如。驾驶执照,还有学生证。然后,您可以通过Credentials类(Driver,Student等)扩展来表示一个人可能具有的所有这些不同凭据。一个人可能有一个List,您可以使用它来执行您可能需要的任何基于案例的逻辑。

答案 2 :(得分:0)

正如您所提到的,请考虑使用继承来解决此问题。每个学生和每个驱动程序都被视为 Person 。因此, Student Driver 都应该从父类 Person 继承功能。至于某人是学生,司机,两者,还是只是一个“普通人”应该只是存储在一个变量中。

public class Person
{
    private int id;
    private String type;

    public Person(int id, String type)
    {
        this.id = id;
        this.type = type;
    }

    public int GetID()
    {
        // Using *this* keyword for consistency, but not necessary here.
        return this.id;
    }

    public void SetID(int id)
    {
        this.id = id;
    }

    public String GetType()
    {
        // Using *this* keyword for consistency, but not necessary here.
        return this.type;
    }

    public void SetType(String type)
    {
        this.type = type;
    }
}

public class Student extends Person
{
    super(int id, String type);
}

public class Driver extends Person
{
    super(int id, String type);
}

public class Main
{
    public static void main(String [] args)
    {
        Student sam = new Student(342918293, "student"); 
        Driver rosa = new Driver(147284, "driver");
    }
}

答案 3 :(得分:-1)

我并不太喜欢这种继承,因为一个人可以同时成为一名司机和一名学生。 虽然我不完全记得装饰者模式(Poliwhirl先生的模糊)是如何工作的,但这可能是正确的方法。