2 回答
TA贡献1804条经验 获得超2个赞
对象为自己负责
通常在面向对象编程 (OOP) 中,我们希望对象对自己负责。应该在内部处理有关其内部状态完整性的业务规则(或委托给构建者——见下文)。这个想法是 OOP 中正式称为封装的一部分。
所以在你的例子中,Professor班级不应该担心Student班级的规则,比如学生姓名的长度。该Student级应执行其自身的完整性。我们希望这些完整性规则的逻辑位于一个地方,而不是分散在整个应用程序中。
事实上,Professor类不应该实例化Student对象。在您的示例中暗示,必须有其他方将学生分配给教授。也许是一个Tutorial负责跟踪由教授监督的几个学生的作业和进度的对象。这Tutorial应该是实例化Student对象,或者传递Student从其他来源(例如数据库服务对象)接收的对象。
当Student对象到达 时Professor,它们应该是有效的。本Professor类不应该是什么让一个被关注Student的有效与否。该Professor只应是什么让一个有关Professor有效。
class Professor{
List< Student > students;
…
public void addStudent( Student student ){
Objects.requireNonNull( student , "Received NULL rather than a Student object. Message # 68a0ff63-8379-4e4c-850f-e4e06bd8378a." ) ; // Throw an exception if passed a null object.
Objects.requireNonNull( this.students , "Collection of Student objects is NULL. Message # c22d7b22-b450-4122-a4d6-61f92129569a." ) ; // Throw an exception if the `students` list is not established.
this.students.add( student ) ;
}
}
除了对象对自己负责的想法之外Professor,不实例化Student对象的另一个原因是方便测试。如果Student对象来自某个其他来源,则该来源可以使用尚未完成、禁用某些功能(例如数据库访问)或被设计用于测试场景的虚假数据替代的类或接口提供虚假对象Student。
建造者模式
如果您有多个需要验证才能实例化新对象的属性,您可能需要使用Builder 模式。您定义了一个额外的类,例如,StudentBuilder它具有为学生所需的每个部分的方法。
通常,这些方法都返回相同的StudentBuilder对象以方便调用链。
不同的人对建筑商有不同的风格。一种方法是提供一种有效性检查方法,或者一种提供阻止构建所需对象的问题列表的方法。
有些人使用 likewith而不是 accessor 方法set来表明,虽然我们暂时在构建器上设置一个属性,但真正的意图是在另一个类的对象上设置一个属性。
StudentBuilder sb = new StudentBuilder().withFirstName( "Alice" ).withLastName( "Coleman" ).withEmail( "x@y.com" );
if( sb.isValid() ) {
Student s = sb.build() ;
…
}
TA贡献1815条经验 获得超10个赞
在简单的程序中,没有太大关系。在复杂的应用中,有许多因素决定了这一点:
在某些情况下,具有无效值的对象是否仍然存在?(即使它们包含无效值,它们是否有意义?)
验证很贵吗?(是否需要计算、网络连接或数据库操作?)
可以验证吗?(我们是否已经拥有验证所需的所有信息?)
验证是否有自己的阶段,在这个阶段与其他对象一起验证或针对其他对象进行验证?
是否有现有的约定或要求?
等等...
因此,大多数情况下,这将由架构或设计约束或与更大应用程序相关的其他因素决定。在非常小的程序中,您可能找不到任何决定验证最佳位置的因素。
在上面显示的示例对象创建代码中,您通常不会默默地跳过超过 20 个字符的值,但在这种情况下通常会抛出异常。如果这是数据处理而不是有意过滤短于 20 个字符的记录,您不想默默地忽略不合适的记录。(想象一下,谁会手动检查为什么在 1000 条记录的集合中,有 5 条记录丢失了,并且没有错误消息表明出了什么问题。所以你可能会看到,上述方法无论如何都没有实际使用。)
添加回答
举报