Коли слідкуйте за SRP, як я маю поводитися з підтвердженням та збереженням об'єктів?


10

Останнім часом я читав чистий код та різні статті в Інтернеті про SOLID, і чим більше я читаю про нього, тим більше відчуваю, що нічого не знаю.

Скажімо, я будую веб-додаток за допомогою ASP.NET MVC 3. Скажімо, у мене є UsersControllerтака Createдія:

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);
        }
    }
}

Це має певний сенс для мене, але я зовсім не впевнений, що це правильний спосіб поводження з такими речами. Це, наприклад , цілком можливо передати неприпустимий екземпляр CreateUserViewModelв IUserServiceкласі. Я знаю, що міг би використовувати вбудовані в 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

Відповіді:


4

Я дійсно не думаю, що ви порушуєте Принцип єдиної відповідальності у своєму другому прикладі.

  • UserServiceКлас має тільки один привід для зміни: якщо є необхідність змінити спосіб збереження користувача.

  • ICreateUserValidatorКлас має тільки одну причину , щоб змінити: якщо є необхідність змінити спосіб перевірки користувача.

Треба визнати, що ваша перша реалізація більш інтуїтивна. Однак перевірку має здійснювати суб'єкт, який створює користувача. Сам творець не повинен відповідати за перевірку; він повинен скоріше делегувати відповідальність класу валідатора (як у вашій другій реалізації). Отже, я не думаю, що другому дизайну не вистачає SRP.


1
Одиночна відповідальність-шаблон? Принцип, можливо?
янніс

Так, звичайно :) Виправлено!
Гувен

0

Мені здається, що перший приклад «ближче» до справжнього СРП; це обов'язок Контролера у вашому випадку знати, як з'єднати речі та як пройти вздовж ViewModel.

Хіба не було б більше сенсу, щоб усі IsValid / ValidationMessages були частиною самого ViewModel? Я не посперечався з MVVM, але це здається старим шаблоном Model-View-Presenter, де презентатор міг нормально обробляти такі речі, оскільки це було безпосередньо пов'язане з презентацією. Якщо ваш ViewModel може перевірити свою справжність, немає жодного шансу передати недійсний метод створення UserService.


Я завжди вважав, що ViewModels повинні бути простими DTO без занадто багато логіки в них. Я не впевнений, чи варто мені поставити щось подібне до перевірки бази даних у ViewModel ...
Kristof Claes

Я думаю, це залежало б від того, що тягне за собою валідація; якщо ViewModel просто виставляє IsValidбулевий ValidationMessagesмасив і масив, він все ще може викликати клас Validator і не повинен турбуватися про те, як здійснюється перевірка. Контролер може спочатку перевірити, що, наприклад, if (userViewModel.IsValid) { userService.Create(userViewModel); }ViewModel повинен знати, чи він дійсний, але не те, як він знає, що він дійсний.
Уейн Моліна
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.