0

我有一点 OOD 问题。

我有服务:

namespace Front\Service\Course;

use Front\ORM\EntityManagerAwareInterface;
use Zend\Http\Request;
use Zend\InputFilter\InputFilter;
use Front\InputFilter\Course\CreateFilter;

class Create implements EntityManagerAwareInterface
{
/**
 * @var \Doctrine\Orm\EntityManager
 */
protected $entityManager = null;

public function create(CreateFilter $createFilter)
{
    if (!$createFilter->isValid()) return false;

    /* @var $courseRepository \Front\Repositories\CourseRepository */
    $courseRepository = $this->getEntityManager()->getRepository('Front\Entities\Course');
    $course = $courseRepository->findByName($createFilter->getCourse());
}

/* (non-PHPdoc)
 * @see \Front\ORM\EntityManagerAwareInterface::getEntityManager()
 */
public function getEntityManager()
{
    return $this->entityManager;
}

/* (non-PHPdoc)
 * @see \Front\ORM\EntityManagerAwareInterface::setEntityManager()
 */
public function setEntityManager(\Doctrine\ORM\EntityManager $entityManager)
{
    $this->entityManager = $entityManager;
    return $this;
}
}

和控制器:

class CreateController extends \Zend\Mvc\Controller\AbstractController
{

    public function onDispatch(MvcEvent $e)
    {
        $jsonModel = new JsonModel();

        /* @var $courseCreateService \Front\Service\Course\Create */
        $courseCreateService = $this->getServiceLocator()->get('Front\Service\Course\Create');

        $courseCreateFilter = new CreateFilter();

        $courseCreateFilter->setData($this->params()->fromPost());

        if (!$courseCreateFilter->isValid()) {
            $jsonModel->setVariable('status', 0);
            $jsonModel->setVariable('message', $courseCreateFilter->getMessages());

            return;
        }

        $courseCreateService->create($courseCreateFilter);

        }
 }

通过服务方法声明:

    public function create(CreateFilter $createFilter)

我强制服务用户每次想要创建新课程时使用从 Zend/InputFilter 派生的 CreateFilter 容器。

我的问题是:当我发送到服务层而不是 Typed 对象而是简单值时会不会更好?在我的例子中,它可能看起来像:

public function create($courseName) 

我的 CreateFilter 看起来像:

class CreateFilter extends InputFilter
{
    public function __construct()
    {
       $input = new Input('name');

    $validatorChain = new ValidatorChain();

    $validatorChain->addValidator(new StringLength(array('max'=>60)))
                   ->addValidator(new NotEmpty());

    $input->setRequired(true)->setValidatorChain($validatorChain);

    $this->add($input);
}

/**
 * @return string | null
 */
public function getCourse()
{
    return $this->getValue('name');
}
}
4

3 回答 3

0

如果您像现在这样提供一个具体的类名,那么您将永远与该类的具体实现或从它派生的实现联系在一起。如果您稍后决定要完全使用不同的类,则必须重构您的服务类代码,而使用接口,您只需在新类中实现它,您的服务将继续工作而无需任何更改。

根本没有任何接口,您的服务类将不得不进行额外的检查,以首先查看它是否是一个对象,然后它是否实现了您期望的方法,然后才能开始执行它的工作。通过要求接口,您消除了不确定性,并否定了检查的需要。

通过提供接口,您可以在方法和它们期望作为参数的类之间创建契约,而不限制哪些类可以进入契约。总而言之,接口契约比类名契约更可取,但两者都比没有契约更可取。

于 2013-04-26T17:27:47.597 回答
0

我通常将我的实体绑定到我的表单,因此它们填充了表单中的数据。这样,您将实体注入您的服务,恕我直言,这更清洁。该服务不应该知道您是如何获取数据的。

我的实体 Bar 的“管理员”控制器通常注入三个对象:存储库(用于查询对象)、服务(用于持久/更新/删除对象)和表单(用于为用户修改对象)。然后,标准控制器非常基于 CRUD,并且仅将实体推送到服务层:

<?php

namespace Foo\Controller;

use Foo\Repository\Bar as Repository;
use Foo\Form\Bar       as Form;
use Foo\Service\Bar    as Service;
use Foo\Entity\Bar     as Entity;
use Foo\Options\ModuleOptions;

use Zend\Mvc\Controller\AbstractActionController;

class BarController extends AbstractActionController
{
    /**
     * @var Repository
     */
    protected $repository;

    /**
     * @var Service
     */
    protected $service;

    /**
     * @var Form
     */
    protected $form;

    /**
     * @var ModuleOptions
     */
    protected $options;

    public function __construct(Repository $repository, Service $service, Form $form, ModuleOptions $options = null)
    {
        $this->repository = $repository;
        $this->service    = $service;
        $this->form       = $form;

        if (null !== $options) {
            $this->options = $options;
        }
    }

    public function getService()
    {
        return $this->service;
    }

    public function getRepository()
    {
        return $this->repository;
    }

    public function getForm()
    {
        return $this->form;
    }

    public function getOptions()
    {
        if (null === $this->options) {
            $this->options = new ModuleOptions;
        }

        return $this->options;
    }

    public function indexAction()
    {
        $bars = $this->getRepository()->findAll();

        return array(
            'bars'     => $bars,
        );
    }

    public function viewAction()
    {
        $bar = $this->getBar();

        return array(
            'bar' => $bar,
        );
    }

    public function createAction()
    {
        $bar  = $this->getBar(true);
        $form = $this->getForm();
        $form->bind($bar);

        if ($this->getRequest()->isPost()) {
            $data = $this->getRequest()->getPost();
            $form->setData($data);

            if ($form->isValid()) {
                // Bar is populated with form data
                $this->getService()->create($bar);

                return $this->redirect()->toRoute('bar/view', array(
                    'bar' => $bar->getId(),
                ));
            }
        }

        return array(
            'form' => $form,
        );
    }

    public function updateAction()
    {
        $bar  = $this->getBar();
        $form = $this->getForm();
        $form->bind($bar);

        if ($this->getRequest()->isPost()) {
            $data = $this->getRequest()->getPost();
            $form->setData($data);

            if ($form->isValid()) {
                $this->getService()->update($bar);

                return $this->redirect()->toRoute('bar/view', array(
                    'bar' => $bar->getId(),
                ));
            }
        }

        return array(
            'bar'  => $bar,
            'form' => $form,
        );
    }

    public function deleteAction()
    {
        if (!$this->getRequest()->isPost()) {
            $this->getRequest()->setStatusCode(404);
            return;
        }

        $bar = $this->getBar();
        $this->getService()->delete($bar);

        return $this->redirect()->toRoute('bar');
    }

    protected function getBar($create = false)
    {
        if (true === $create) {
            $bar = new Entity;
            return $bar;
        }

        $id  = $this->params('bar');
        $bar = $this->getRepository()->find($id);

        if (null === $bar) {
            throw new Exception\BarNotFoundException(sprintf(
                'Bar with id "%s" not found', $id
            ));
        }

        return $bar;
    }
}

我在 Github 上用这个完整的代码(可读性更好)和服务制作了一个 gist 文件。该服务依赖于接口,因此您甚至可以将实体对象换成另一个具有相同接口的对象。

在此处查看完整内容:https ://gist.github.com/juriansluiman/5472787

于 2013-04-27T11:35:27.920 回答
0

感谢大家的回答,由于回答和分析,我得出了最适合我情况的结论。我同意在我的情况下 Service 不应该等待具体对象,它应该等待带有 getCourse 方法的抽象。
我完全同意“脆”的回答:

总而言之,接口契约比类名契约更可取,但两者都比没有契约更可取。

所以我需要用一种方法 getCourse 或 getName 提取接口,然后删除

if (!$createFilter->isValid()) return false;

所以界面:

interface CourseInterface
{
  /**
   * @return String
   **/
  public function getName();
}

和服务:

class Create implements EntityManagerAwareInterface
{
/**
 * @var \Doctrine\Orm\EntityManager
 */
protected $entityManager = null;

/**
 * @param CourseInterface $course
 * @param UserInterface $creator
 */
public function create(CourseInterface $course)
{
    $courseEntity = new Course();

    $courseEntity->setName($course->getName());

    $this->entityManager->persist($courseEntity);
    $this->entityManager->flush();
.....

谢谢大家。

于 2013-04-28T10:33:20.840 回答