【问题标题】:OOP Class ResponsibilityOOP 类责任
【发布时间】:2017-10-30 21:45:13
【问题描述】:

我正在开发一个爱好项目,以正确理解封装、可以负责的类和规则。我在另一个forum 中要求进行代码审查和帮助,但我不同意给出的方法。

我有以下要求:

  • 国际学生需要文件来完成注册过程,但国内学生不需要。

学生状态界面:

public interface StudentStatus {
    Collection<String> retrieveDocuments();
    StudentType retrieveStatus();
}

 public final class Domestic implements StudentStatus {

       private final StudentType type;
       private final Collection<String> documents;

       public Domestic() {
           this.type = StudentType.Domestic;
           this.documents = Collections.emptyList();
       }

       @Override
       public Collection<String> retrieveDocuments() {
           return this.documents;
       }

       @Override
       public StudentType retrieveStatus() {
           return type;
       }
   }

public final class International implements StudentStatus {

   private final StudentType type;
   private Collection<String> documents;


   public International(Collection<String> documents) {

       this.type = StudentType.International;
       this.documents = Collections.unmodifiableCollection(documents);
   }

   @Override
   public Collection<String> retrieveDocuments() {
       return Collections.unmodifiableCollection(documents);
   }

   @Override
   public StudentType retrieveStatus() {
      return type;
   }
}

学生班:

public final class Student {

     //left out constructor and getters for other attributes. 

     public Collection<String> retrieveDocuments() {
           return status.retrieveDocuments();
     }

     public StudentType retrieveStatus() {
          return status.retrieveStatus();
     }

     public boolean isVerified(StudentType type) {
         return this.retrieveStatus() == type;
     }
}

大学班:

public class University {

    private final Map<Student,Collection<String>> registeredStudents;
    private final StudentType type;

    public University()
    {
        registeredStudents = new HashMap<Student,Collection<String>>();
        type = StudentType.International;
    }

    public void add(Student student){
        if (student.isVerified(type)){
            registeredStudents.put(student, student.retrieveDocuments());
        }else {
            //throw an exception or handle error accordingly 
        }
    }
}

在继续之前,我了解这是一个真的过于简化的申请流程。在现实世界中,在学生注册之前还需要做很多事情。学生可能必须通过入学考试,并在注册开始前付款。此外,在现实环境中,这些信息可能会存储在校园员工可以访问的数据库中。

在另一个论坛中,讨论的内容是提供了哪些信息,并给出了方法。

  • 有一个规则类,它接受 Student 对象并验证它 实际上是国际化的并且有文件。

我遇到的问题是,您仍然需要使用retriveStatus()isVerified() 询问学生他/她的状态,我真的不知道该怎么做.

  • 分别传递学生和文档集合以添加到地图中。

在现实世界中,大学如上所述制定规则,其职责是检查国际学生是否有文件。

当我使用add(Student student) 建议上述方法时,他们表示这不是一个好主意,因为规则可能会改变,您必须更改 Student 类以及 University 类。

但是,在现实世界中,学生很清楚他/她的身份,以及他/她是国内还是国际,并且拥有可以提供给学校的文件。

鉴于上述方法,以这种方式编写 add 方法是个好主意吗?有没有比 add 方法更好的方法?

tl;dr - 如果学生必须遵守大学制定的规则,那么学生对象将如何与大学通信以获取数据,以便大学可以确保学生对象在不破坏封装的情况下遵守规则?

【问题讨论】:

  • 我认为您需要对模型进行一些更改。考虑每个类中应该存在哪些依赖项。例如,学生对象应该知道状态吗?这是我的建议:为学生、状态、文档、大学和注册策略创建抽象。您可以将抽象工厂用于各种注册过程,也可以使用策略。
  • 一所大学招收一种类型的学生吗?
  • @AndrewTobilko - 不,但我想专注于国际,因为那是一套规则我无法理解和实施的规则。
  • @amitmah - 从现实世界的角度来看,学生知道身份,您知道自己是国内还是国际,并且有文件证明您的身份。我不明白为什么学生不会意识到这一点。
  • isVerified 不应该在Student 中。学生无法确认他的验证。此验证可能涉及一些服务。您会将实体绑定到这些服务吗?但是学生可以将文件交给负责决定这些文件是否符合要求的服务机构。学生对要求以及他拥有的文件的结构一无所知。

标签: java oop


【解决方案1】:

上一篇文章中的对话可能将您引向了一个总体上不错的方向。最适用的原则是开放/封闭原则。 https://en.wikipedia.org/wiki/Open/closed_principle.

不要让自己不得不不断地修改一个特定的类或一组类(至少在 OO 世界中),在一个你知道将是频繁变化的领域。该原则同样适用于功能世界,但您的示例使用的是 OOPL。

手工构建的小规则引擎是解决您提出的问题的一个很好的解决方案。特别是如果你知道规则在相当固定的输入上流动——比如大学和学生。 DocumentsRequiredForInternationalStudents 是该架构中的一个规则类 - 仅当该规则本身发生变化时才需要更改。新规则,这将发生很多 = 添加新类,而不是修改现有类。

有时你不知道变化的向量,更难做出决定,但如果很明显,不要构建一个系统,因为已知的变化向量你将不得不不断地违反打开/关闭。

有不同的方法来实现小规则引擎。一种选择(这是蹩脚的伪代码,因此占用的空间更少)

interface RegistrationRule
 boolean isRegistrationValid(Student student)  //might need university too for some rules.

class DocumentsNeededForInternationalStudents implements RegistrationRule
 boolean isRegistrationValid(Student student) 
 // return student.status is international and student has documents, or student status is domestic 
// (this rule passes through as valid any domestic students).

class RegistrationRules 
// (holds all the rules you will use - kind of a factory)
constructor -> add to static list of rules an instance of all your rules
boolean runRulesForStudent(Student)
  //iterate through all rules, call isRegistrationValid, short circuit and return false if one of them false


class University
addStudent(Student student)
  if (RegistrationRules.runRules(student).... else

这只是将其组合在一起的一种方式,但您可以看到它实际上并没有很多代码。您有一个接口,每个规则的实现类,以及一个为您应用每个规则的小规则引擎类。新规则 = 新类,修改规则引擎中的构造函数以添加该类的实例,完成。

当规则中需要的属性和行为非常多样化且不集中在同一小类集合中时,这种模式开始有点困难。

【讨论】:

  • 如果我遵循您的方法,如果我看到一个示例可能会有所帮助,但是如果新规则意味着新课程,这意味着,我正在要求学生提供信息以验证是否他/她遵守规则。因此,如果我有一个规则调用DocumentsRequiredForInternationalStudents,该方法会是什么样子?我应该从学生那里拨打getters 还是在学生内部提供methods 来完成这项工作?我的问题是学生如何在不破坏封装的情况下与正在检查它是否遵守大学规则的班级进行交流?这才是我真正想知道的。
  • 已在上面编辑。您可以调用吸气剂。封装是一件好事,但有时您确实需要访问类的属性,因为做某事确实不应该是该类的责任。注册就是一个很好的例子。学生应该了解个别大学的注册规则吗?可能不是。这意味着其他一些代码单元需要有关学生的信息来确定注册有效性。封装和单一职责原则经常相互矛盾 - 您必须根据最适合您的问题来决定。
  • 这几乎是我抽象出StudentStatus的方式(参见上面的更新代码)。鉴于状态有自己的一套规则来执行,创建一个类来执行这些规则是有意义的,而不是让学生提防所有StudentTypes
  • 是的,您的模型中的 StudentStatus 可能就是您需要传递给规则来运行它的全部内容,而不是整个学生对象。
【解决方案2】:

你说:

在现实世界中,大学设定了上述规则,并且 它的责任是检查国际学生是否有 文档。

这意味着验证责任在于大学(每个大学可能不同)而不是学生。学生所能做的就是提供必要的信息供大学验证。

add 方法应该从retrieveDocuments 获取文档并运行其规则以确定是否允许学生被接受。

【讨论】:

  • 我的 add 方法是否足以满足我的要求?
  • 没有。它需要重构以在其中包含“isVerified”和retrieveStatus 逻辑。学生不应该有isVerified,因为该方法是大学特定的。
  • 如果isVerified 是大学方法(我明白为什么),这意味着我必须向学生询问信息,我是通过吸气剂还是其他方法来做到这一点?如果我使用吸气剂,这会破坏封装吗?
  • 这取决于什么样的信息,但一般不会破坏封装。例如,为了检查学生是否是外国学生,您可以对学生使用“getCountryOfResidence”方法,美国的大学可以检查它是否是美国,或者有单独的 StudentRegistry,让学生返回该学生的居住国家/地区。跨度>
  • 这都是关于实用性和理论性的。纯粹主义者可能会说getters 很糟糕,但如果没有getters,就无法设计可用的应用程序。一切都是为了平衡和保持责任。
猜你喜欢
  • 2015-09-16
  • 1970-01-01
  • 1970-01-01
  • 2011-11-16
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 2020-02-08
相关资源
最近更新 更多