【问题标题】:OOP - How does one class become another?OOP - 一个类如何变成另一个类?
【发布时间】:2016-02-11 15:45:34
【问题描述】:

一个人有一个ID。 学生有学生证。 司机有驾照。

  • 一个人上学并成为学生。
  • 一个学生毕业,成为一个普通人。
  • 一个人拿到驾照就成了司机。
  • 一名学生获得驾照并成为一名司机。

这些东西只是像下面的例子那样改变状态吗:

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 {
    //
}

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

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

【问题讨论】:

  • 继承才是正道。另一方面,在您的第二个问题中,第一种情况和第二种情况可能是兼容的:在getADriversLicense 内部调用new Driver(this) 这是第二种情况的构造函数。
  • 是否编译:class DriverStudent extends Person, Driver {?
  • 你总是可以使用decorator pattern从一个类转换到另一个类。
  • @HovercraftFullOfEels 当然不会编译。但是,如果有一种方法需要一个既是学生又是司机的人呢?
  • @ctst:我很确定这是讽刺/假设......让 OP 思考。

标签: java oop domain-driven-design


【解决方案1】:

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

如果一个人可以是司机和学生,也可以是员工的场景呢?

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

编辑:进一步全面回答您的问题,它很容易解决 Person 获得或失去角色的场景,即添加或删除角色。正如评论中所指出的,这种关系最好通过 Has 关系然后是 IS - 一种关系来表示。

编辑 1 相比之下,当您使用装饰器模式时,您的源会被包装,过多的装饰器会创建一个聚合链,而 IMO 不是一个理想的场景,或者它会导致装饰器继承链这不符合我的喜好。

话虽如此,根据特定情况,一种特定模式可能比另一种更适合,但在您给出的示例中,我认为简单的角色聚合是最好的。

【讨论】:

  • 正是我的想法。 OP 的情况最好通过 HAS-A 关系而不是 IS-A 关系来建模。
  • 由于这与 Spring Security 等安全框架非常吻合(一个有角色的人可以做一些特定的事情),我更喜欢这个。让我调查一下,看看是否有这样的情况还不够。
  • @Reek 我经常使用 Guice 而不是 Spring,但总的来说,我描述的模式非常适合粒度配置。特定角色的细节也可以通过角色实现类轻松实现。而且我对 Spring 充满信心,您可以围绕它编织方面以实现对它的精细控制
  • 谁拥有DriverLicense 字段并以这种方式实现DriverRole?具体的数据和行为在哪里实现?
  • @Reek 我最近遇到了一个与您的观点相关的有趣场景。由于我们非常依赖代码检测,因此我们开发了一个通用的对象引用机制来解决这个问题,并使用在引用更改时触发的自定义规则。无法真正分享代码,但我希望你能大致了解一下。
【解决方案2】:

继承是考虑这些关系的一种方式,但只有在合理地列举一个人可能拥有的状态组合的数量时,这实际上才有意义。

在不通过继承限制自己的情况下考虑这一点的另一种方法是将每个人都视为具有不同证书的人。

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

【讨论】:

    【解决方案3】:

    正如您所提到的,考虑使用继承来解决这个问题。每个学生和每个司机都被认为是一个。因此,StudentDriver 都应该从父类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");
        }
    }
    

    【讨论】:

      【解决方案4】:

      我不太喜欢继承,因为 Person 可以同时是 Driver 和 Student。 虽然我不完全记得装饰器模式(由 Poliwhirl 先生建议)是如何工作的,但这可能是正确的方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-09-02
        • 1970-01-01
        • 2013-05-17
        • 1970-01-01
        • 1970-01-01
        • 2015-08-26
        • 2011-06-13
        • 1970-01-01
        相关资源
        最近更新 更多