如何纠正我的设计以避免循环依赖

时间:2017-06-16 12:19:59

标签: php laravel architecture solid-principles

我正在敲打我的头来找到解决方案,但我仍然无法解决,我正在寻找一个设计副解决方案而不是解决问题的黑客。

我有以下课程

class CourseService{

     public function getCourse($courceId){
         $course = $this->courseRepo->getCourse($courseId);
         $restrictions = $this->invoiceService->getRestrictions($course->courseid);
         $course->restrictions = [];
         if($restrictions != null){
           $course->restrictions = $restrictions;
         }
     }
}
  1. 现在这个课程服务是在StudentService的构造函数中注入的,因为当学生需要注册到cource时,我会在那里使用这个课程服务。
  2. 你也可以看到我使用CourseRepo来获取Course对象然后InvoiceService来说明哪些字段被限制更新,基本上restrictions属性给出了一个字符串数组来定义哪个字段不允许编辑,我希望UI开发人员将使用它来禁用这些字段,我不得不注入InvoiceService,因为有一些处理要对从{{1}获取的原始数据库记录进行处理所以发票回购被封装在invoiceService
  3. 现在让我们看一下InvoiceService

    InvoiceRepo
    1. 但我无法将StudentService注入此处,因为Class InvoiceService{ public function getAmountToPay($courseid, $studentid){ //now I need to inject StduentService inorder to get student info which needed for the calculation } } - > StudentService - > CourceService
    2. 我看到的选项和后果

      1. 我看到的一个选项是从InvoiceService中删除InvoiceService并在CourseService被调用的地方使用InvoiceService,然后修改结果,但问题是,getCourse()主要用于控制器,接下来的事情是getCourse()从许多控制器和服务调用,并期望限制在那里,所以如果我想摆脱{{1然后我会有很多地方添加删除行,它会重复代码重复。

      2. 我可以将getAmountToPay()移动到学生服务但是那个服务已经完成了许多学生相关的任务,我很乐意将发票部分提取到另一个服务,所以我有一个明确的地方可以看到我需要检查发票上的错误。

2 个答案:

答案 0 :(得分:0)

我会将您的invoiceService更改为不依赖于学生服务。将您需要的内容传递给invoiceService。如何处理这些学生详细信息的逻辑可以保留在invoiceService中,但内容可以传入。

答案 1 :(得分:0)

学生服务:

首先,您必须看到 - 实际上要确定 - 学生服务使用发票服务,而不是互惠。当我作为一名历史学生注册时,我先去注册/学生办公室。他们打电话给财务/发票办公室询问我应该支付多少钱。财务办公室检查数据库并返回有关我支付金额的回复。

课程服务:

......时间过去了。现在我是学生。我不再需要去登记处了。如果我必须了解我的课程,我会去秘书处/ 课程服务。他们会给我关于课程所需的所有信息。但是,如果我想参观一些特殊的考古课程,必须付费的话,课程服务会致电财务/ 发票服务来为我询问。反过来,他们将返回信息。如果课程服务想知道我应该具备的一些财务限制,则同样适用:他们称之为财务/发票服务。

发票服务 - 学生服务,发票服务 - 课程服务:

现在,如果发票服务需要有关学生或课程的信息,会发生什么?它应该拨打学生服务还是课程服务?答案是不。发票服务应该接收学生ID,课程ID,域对象学生或域对象课程作为构造函数/方法依赖项,但不是相应的服务。它将自己获取所需的信息。更多的是,发票服务应该使用其特定的发票/财务表,而不是课程表或学生详细信息表(除了他们的ID)。

<强>结论:

  • 注册学生是StudentService的工作。虽然 CourseService可以帮助注册过程。
  • StudentService通过致电验证学生支付的金额 InvoiceService。我知道你不想拥有getAmountToPay() 在StudentService中,但它是一个自然的工作流程。你可能会想 将StudentService分开的其他很多东西分开 对另一项服务负责。
  • CourseService负责查找课程 课程限制,它称之为InvoiceService。所以, CourseService将由InvoiceService协助。

我向你传递了我的愿景的PHP版本。我重命名了一些函数,以便为您提供更好的视角。

祝你好运!

P.S:我希望我理解正确,发明服务的意义&#34;是一个&#34;财务部门&#34;一。对不起,但我不是母语为英语的人,所以我无法了解所有的感官。

<?php

class StudentService {

    protected $courseService;
    protected $invoiceService;

    /**
     * Even if the course service uses the invoice service,
     * doesn't mean that the student service shouldn't use it too.
     * 
     * @param CourseService $courseService
     * @param InvoiceService $invoiceService
     */
    public function __construct(CourseService $courseService, InvoiceService $invoiceService) {
        $this->courseService = $courseService;
        $this->invoiceService = $invoiceService;
    }

    /**
     * Enroll a student to a course.
     * 
     * @param integer $studentId
     * @param integer $courseId
     * @return bool Enrolled or not.
     */
    public function enrollToCourse($studentId, $courseId) {
        //... Use here the CourseService too - for what you said regarding the enrollment.
        $enrolled = $this->studentRepo->enrollToCourse($studentId, $courseId);
        return $enrolled;
    }

    /**
     * Get the amount to be payed by a student on the enrollment moment.
     * 
     * @param integer $studentId
     * @param integer $courseid
     * @return integer Amount to be payed.
     */
    public function getAmountToPayOnEnrollment($studentId, $courseid) {
        $amount = $this->invoiceService->getAmountToPayOnEnrollment($studentId, $courseid);
        return $amount;
    }

}

class CourseService {

    protected $invoiceService;

    /**
     * Invoice service is used to get the (financial) restrictions for a course.
     * 
     * @param InvoiceService $invoiceService
     */
    public function __construct(InvoiceService $invoiceService) {
        $this->invoiceService = $invoiceService;
    }

    /**
     * Get a course and its corresponding (financial) restrictions list.
     *  
     * @param integer $courseId
     * @return Course Course domain object.
     */
    public function getCourse($courseId) {
        $course = $this->courseRepo->getCourse($courseId);
        $course->restrictions = $this->getRestrictionsForCourse($course->courseId);
        return $course;
    }

    /**
     * Get the (financial) restrictions for a specified course.
     * 
     * @param integer $courseId
     * @return array Restrictions list.
     */
    public function getRestrictionsForCourse($courseId) {
        $restrictions = $this->invoiceService->getRestrictionsForCourse($courseId);
        return $restrictions;
    }

}

Class InvoiceService {

    /**
     * No student service needed!
     */
    public function __construct() {
        //...
    }

    /**
     * Again, no student service needed: the invoice service
     * fetches by itself the needed infos from the database.
     * 
     * Get the amount to be payed by a student on the enrollment moment.
     * 
     * @param integer $studentId
     * @param integer $courseid
     * @return integer Amount to be payed.
     */
    public function getAmountToPayOnEnrollment($studentId, $courseid) {
        $amount = $this->invoiceRepo->getAmountToPayOnEnrollment($studentId, $courseid);
        return $amount;
    }

    /**
     * Get the (financial) restrictions for a course.
     * 
     * @param integer $studentId
     * @param integer $courseid
     * @return array Restrictions list.
     */
    public function getRestrictionsForCourse($courseid) {
        $restrictions = $this->invoiceRepo->getRestrictionsForCourse($courseid);
        return isset($restrictions) ? $restrictions : [];
    }

    /*
     * Quote: "Some processing to do to the raw 
     * db records that are fetched from the InvoiceRepo".
     */
    //...
}