遵循SRP时,应如何处理验证和保存实体?


10

最近,我一直在阅读Clean Code和各种有关SOLID的在线文章,而我阅读的内容越多,我感觉自己一无所知。

假设我正在使用ASP.NET MVC 3构建Web应用程序。假设我有一个类似这样UsersControllerCreate操作:

public class UsersController : Controller
{
    public ActionResult Create(CreateUserViewModel viewModel)
    {

    }
}

在这种操作方法中,如果输入的数据有效,我想将用户保存到数据库中。

现在,根据“单一责任原则”,对象应具有单一责任,并且该责任应由类完全封装。它的所有服务都应严格地与这一责任保持一致。由于验证和保存到数据库是两个单独的职责,我想我应该创建一个单独的类来像这样处理它们:

public class UsersController : Controller
{
    private ICreateUserValidator validator;
    private IUserService service;

    public UsersController(ICreateUserValidator validator, IUserService service)
    {
        this.validator = validator;
        this.service= service;
    }

    public ActionResult Create(CreateUserViewModel viewModel)
    {
        ValidationResult result = validator.IsValid(viewModel);

        if (result.IsValid)
        {
            service.CreateUser(viewModel);
            return RedirectToAction("Index");
        }
        else
        {
            foreach (var errorMessage in result.ErrorMessages)
            {
                ModelState.AddModelError(String.Empty, errorMessage);
            }
            return View(viewModel);
        }
    }
}

这对我来说有些道理,但我完全不确定这是处理此类问题的正确方法。例如,完全有可能将无效的实例传递CreateUserViewModelIUserService该类。我知道我可以使用内置的DataAnnotations,但是当它们不够用时该怎么办?我ICreateUserValidator检查数据库以查看是否已经存在另一个具有相同名称的用户的图像...

另一种选择是让IUserService验证这样进行:

public class UserService : IUserService
{
    private ICreateUserValidator validator;

    public UserService(ICreateUserValidator validator)
    {
        this.validator = validator;
    }

    public ValidationResult CreateUser(CreateUserViewModel viewModel)
    {
        var result = validator.IsValid(viewModel);

        if (result.IsValid)
        {
            // Save the user
        }

        return result;
    }
}

但是我觉得这里违反了单一责任原则。

我应该如何处理这样的事情?


user该类不应该处理验证吗?是否使用SRP,我不明白为什么user实例不应该知道何时有效,而是依靠其他方法来确定它。班级还有什么其他职责?另外,当user更改时,验证可能会更改,因此将其外包给其他类将仅创建紧密耦合的类。
sebastiangeiger

Answers:


4

我真的不认为您在第二个示例中违反了“单一责任原则”。

  • UserService类只有一个理由改变:如果有必要改变你保存用户的方式。

  • ICreateUserValidator类只有一个理由改变:如果有必要改变你的验证用户的方式。

我必须承认,您的第一个实现更加直观。但是,验证应由创建用户的实体完成。创建者本身不应对验证负责。它应该将责任委托给验证者类(如您的第二种实现)。因此,我认为第二种设计没有缺少SRP。


1
单一责任模式?原则,也许?
yannis 2011年

是的,当然:)更正了!
Guven

0

在我看来,第一个例子更接近于真正的SRP。在您的情况下,Controller的责任是了解如何进行连接以及如何传递ViewModel。

将整个IsValid / ValidationMessages包含在ViewModel本身中会更有意义吗?我还没有涉足MVVM,但似乎就像旧的Model-View-Presenter模式,Presenter可以处理类似的事情,因为它与表示直接相关。如果您的ViewModel可以检查自身的有效性,则没有机会将无效的参数传递给UserService的Create方法。


我一直认为ViewModels应该是简单的DTO,而没有太多逻辑。我不确定是否应该在ViewModel中放置类似检查数据库的内容...
Kristof Claes

我想这将取决于您的验证需要什么。如果ViewModel仅公开IsValidboolean和ValidationMessagesarray,它仍然可以调用Validator类,而不必担心如何实现验证。该控制器可以检查,首先如if (userViewModel.IsValid) { userService.Create(userViewModel); }基本视图模型应该知道,如果它是有效的,但不是如何它知道它是有效的。
韦恩·莫利纳
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.