전략 패턴의 배경
예시 입니다 :)
인프런에 비즈니스 로그인(Account)이 있고 일반 로그인(User)이 있다.
public interface Login {
public void login();
}
public class User implements Login {
public void login() {
System.out.println("일반 유저가 로그인 한다.")
}
}
public class Account implements Login {
public void login() {
System.out.println("비즈니스 유저가 로그인 한다.")
}
}
예시 만들기 어렵네..(좀 어거지임)
근데 여기서 login 메서드를 ‘일반 유저가 또 로그인을 한다.’ 라고 하고 싶을 경우 메서드를 직접 바꿔야함
⇒ 이것은 OCP 원칙을 어기는 것입니다..
⇒ 또한 wake() 메서드가 필요한 클래스들을 계속 생성해야해서 서비스가 커진다면 힘들어진다.
전략 패턴의 예시
그래서 전략 패턴은 위와 같은 login에 전략을 세워서 accountLogin, userLogin 등의 전략을 만들어서 끼워준다.
userType
@Getter
public enum UserType {
USER("USER"), ACCOUNT("ACCOUNT"), ALL("ALL");
final String type;
UserType(String type) {
this.type = type;
}
}
controller
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserFactory userFactory;
@PostMapping("api/v1/user")
public void createUser(@RequestBody @Valid CreateUserRequest request) {
UserFactoryService userService = userFactory.getUserService(request.getUserType());
userService.createUser(request);
}
}
- request에서 userType을 받는다.
- 받은 type을 통해서 userFactory.getUserService를 받아서 어떤 전략을 실행 시켜줄 지 정한다.
userFactoryService
public interface UserFactoryService {
void createUser(CreateUserRequest request);
}
- 비즈니스 유저, 일반 유저가 할 행동을 인터페이스로 적어줍니다.
- 이 인터페이스를 implements 해서 구현해줍니다.
- 만약 일반 유저, 비즈니스 유저 말고 또 다른 유저가 생기거나, 다른 전략이 생긴다면 기존 메서드를 변경하는 것이 아니라 인터페이스를 받아서 생성해준 후 바꿔 끼워주는 형태입니다.
userFactory
@Component
@RequiredArgsConstructor
public class UserFactory {
private final UserService userService;
private final AccountService accountService;
private final Map<UserType, UserFactoryService> userServiceMap = new EnumMap<>(UserType.class);
@PostConstruct
void userServiceMap() {
userServiceMap.put(UserType.USER, userService);
userServiceMap.put(UserType.ACCOUNT, accountService);
}
public UserFactoryService getUserService(UserType userType) {
UserFactoryService userFactoryService = userServiceMap.get(userType);
if (userFactoryService == null) {
throw new NotFoundException(String.format("존재하지 않는 %s 입니다.", userType));
}
return userFactoryService;
}
}
UserService, accountService
@Service
@RequiredArgsConstructor
public class UserService implements UserFactoryService {
@Override
public void createUser(CreateUserRequest request) {
// user 로직 수행
}
}
@Service
@RequiredArgsConstructor
public class AccountService implements UserFactoryService {
@Override
public void createUser(CreateUserRequest request) {
// account 로직 수행
}
}
처음에는
public UserFactoryService getUserService(UserType userType) {
UserFactoryService userFactoryService = userServiceMap.get(userType);
if (userFactoryService == null) {
throw new NotFoundException(String.format("존재하지 않는 %s 입니다.", userType));
}
return userFactoryService;
}
이 부분을 처음에는 swich case 문으로 했었는데 나쁘지 않았지만 계속 추가해줘야 한다. 하지만 인프런 강의를 들으면서 Map을 사용하면 편리하다고 듣고 해봤는데
userServiceMap.put(UserType.ACCOUNT, accountService);
이 부분만 추가해주는 것이 나쁘지 않은것 같다.
'spring boot > 기술 적용' 카테고리의 다른 글
Spring boot에 Facade 패턴 적용 (0) | 2022.10.10 |
---|---|
webClient 와 feignClient (0) | 2022.10.10 |
jpa comment를 custom annotation으로 만들기 (0) | 2022.07.20 |
stub, mock, spy 비교 (0) | 2022.07.02 |
converter를 만들지 않고 jpa mysql에서 json 사용하기 (0) | 2022.06.28 |