Является ли группировка нескольких классов @Service и @Repository в оболочках антипаттерном? - PullRequest
0 голосов
/ 05 июня 2018

Вопрос немного риторический.У меня был спор с моими коллегами по поводу следующего шаблона:

@Component
public class MetaService {

    public static UserService userService;
    public static GroupService groupService;   
    public static PermissionService permissionService;            
    // ...more fields

    @Autowired
    public MetaService(UserService userService,
                       GroupService groupService
                       PermissionService permissionService) {

        MetaService.userService = userService;
        MetaService.groupService = groupService;
        MetaService.permissionService = permissionService;           
    }
}

По сути, MetaService является точкой входа / оболочкой для нескольких классов Spring bean @Service.И есть еще одна похожая MetaDao оболочка для @Repository класса бобов:

@Component
public class MetaDao {

    public static UserDao userDao;
    public static GroupDao groupDao;
    public static PermissionDao permissionDao;
    // ...more fields   

    @Autowired
    public MetaDao(UserDao userDao,
                   GroupDao groupDao,
                   PermissionDao permissionDao) {

        MetaDao.userDao = userDao;
        MetaDao.groupDao = groupDao;
        MetaDao.permissionDao = permissionDao;
    }
}

Идея такого мета-класса состоит в том, чтобы уменьшить базу кода в классах @RestController и @Service,предоставление краткого API для вызова @Service и @Repository методов, например:

@RestController
public class UserController {

    @PostMapping
    public UserCreateResponse createUser(UserCreateRequest request) {
        MetaService.userService.createAndReturn(userEntity);
        // other service calls
    }
}

Другими словами, это альтернатива наличию @Autowired полей / конструкторов для каждого @Service в каждом @Controller и т. Д. На мой взгляд, этот подход значительно сокращает объем повторяющегося кода.

Мои коллеги говорят, что такой подход затрудняет понимание / сопровождение кода, поскольку объединение @Service компонентов в одномместо скрывает модель зависимости других компонентов, которые их используют.Другими словами, если бы были поля, объявленные для UserController класса.Было бы проще быстро выяснить, от каких компонентов зависит UserController.Кроме того, сложнее издеваться над такими зависимостями, выполняя модульное тестирование.И самое главное, что такой подход супер-анти-паттерн .В то время как я могу согласиться с насмешливой частью, все остальное кажется мне нелепым.

Вопрос в том, действительно ли это как-то против паттерна?

Ответы [ 3 ]

0 голосов
/ 05 июня 2018

Это явно анти-паттерн для меня.

  1. Почему в этом даже есть необходимость?UserController нужен UserService, почему он включает MetaService, содержащий GroupService, в котором он не нуждается?

  2. Не уверен, что предполагается, что ваш MetaDAOделать, связывать воедино объекты, которые находятся в определенном отношении?Я очень сомневаюсь, что это правильная реализация для большинства объектов (может ли пользователь быть во многих группах? Может ли он иметь несколько разрешений?). Какую релевантность домена он должен обслуживать?Или они должны быть @Repository с?

  3. Статические члены public static, но не окончательные - нарушение инкапсуляции.В основном это плохо реализованный шаблон Singleton.

  4. MetaService может также реализовать все три интерфейса и поддерживать связь с репозиториями.Нет реальной необходимости размещать два уровня служб между контроллером и хранилищем, если фактический домен правильно представлен одним сервисом.

Кроме того, я думаю, что этот вопрос относится к https://softwareengineering.stackexchange.com.

0 голосов
/ 05 июня 2018

Вы вводите сервисный локатор под названием MetaService, от которого зависят все другие сервисы, а не напрямую зависящие от их зависимостей.Это также означает, что вы вводите скрытые зависимости, см. Здесь:

MetaService.userService.createAndReturn(userEntity);
           _           _
          /|\         /|\
           |           |

Вы больше не знаете, исходя из аргументов вашего конструктора, каковы ваши зависимости на самом деле.

Если выу вас уже есть система внедрения зависимостей, вам обычно не нужен локатор службы.

0 голосов
/ 05 июня 2018

Что касается принципов ООП, я считаю выставление сгруппированных услуг через атрибуты public static даже с ключевым словом final плохим принципом.Я думаю, что этот подход увеличивает сложность: что, если будет добавлено больше DAO / сервисов?- Тогда вам придется увеличить классы MetaDao и MetaSerice.

Предоставление этих сервисов через этот фасад , другой ненужный слой позволит использовать UserController и touch услуги, которые действительно не нужны - это нужно?Это нарушает инкапсуляцию.Используйте этот широко используемый подход:

@RestController
public class UserController {

    @Autowired
    private final UserService userService;

    // ...
}
...