위 게시글에서도 한차례 정적 팩토리 메서드 활용방안을 정리해봤는데,
레벨1을 마무리하며 미션 내 정적 팩토리 메서드를 활용한 사례들을 총 정리하려고 한다.
🪜 사다리 미션
일급 컬렉션
Player의 일급 컬렉션 Players에서 정적 팩토리 메서드를 활용해 플레이어의 이름으로 Player 객체를 생성했다.
일급 컬렉션에 정적 팩토리 메서드를 도입함으로써
생성자는 필드값을 검증하고, 필드를 초기화하는 역할만 가지도록 하고,
Player의 생성 책임을 Players가 가지도록 했다.
public class Players {
private static final int PLAYER_MIN_SIZE = 1;
private static final int PLAYER_MAX_SIZE = 20;
private final List<Player> players;
private Players(final List<Player> players) {
validate(players);
this.players = new ArrayList<>(players);
}
public static Players valueOf(final List<String> playerNames) {
List<Player> players = playerNames.stream()
.map(Player::new)
.collect(Collectors.toUnmodifiableList());
return new Players(players);
}
/***/
}
enum 클래스에 전략 패턴 도입
나는 Point라는 enum 클래스에 Point 생성 전략을 주입하여 적합한 Point 객체를 생성하고싶었다.
하지만, enum의 생성자는 private만 허용되기 때문에 외부에서 생성자를 호출할 수 없다.
enum클래스에 전략을 주입할 방법으로 정적 팩토리 메서드를 택했다.
아래는 해당 코드이다.
public enum Point {
EXIST(true),
NOT_EXIST(false);
private final boolean isExist;
Point(boolean isExist) {
this.isExist = isExist;
}
public static Point choosePoint(final Point previousPoint, final PointGenerator pointGenerator) {
if (previousPoint == EXIST) {
return NOT_EXIST;
}
return choosePoint(pointGenerator);
}
public static Point choosePoint(final PointGenerator pointGenerator) {
return pointGenerator.generate();
}
/***/
}
🃏 블랙잭
상태 패턴 - 최초 상태 객체 생성
블랙잭에 상태 패턴을 활용했는데,
State라는 추상 클래스에서 정적 팩토리 메서드 create를 호출할 시 최초 상태인 Ready 객체를 생성하도록 했다.
하위 타입을 반환할 수 있다는 정적 팩토리 메서드의 장점을 경험한 순간이었다.
public abstract class State {
protected Cards cards;
/***/
public static State create() {
return new Ready();
}
/***/
}
카드 객체 캐싱
Card 객체에서 정적 팩토리 메서드를 통해 캐싱된 Card객체를 가져오도록 했다.
Card객체를 매번 생성하지 않고 재활용할 수 있는 장점을 얻을 수 있었다.
아래 코드의 of 메서드가, 캐싱한 Card 객체를 가져오는 정적 팩토리 메서드이다.
public class Card {
public static final Map<Denomination, List<Card>> CARDS;
private final Denomination denomination;
private final Suits suit;
static {
CARDS = new LinkedHashMap<>();
for (Denomination denomination : Denomination.values()) {
List<Card> cards = createCards(denomination);
CARDS.put(denomination, cards);
}
}
private static List<Card> createCards(final Denomination denomination) {
int suitLength = Suits.values().length;
List<Card> cards = new ArrayList<>();
for (int i = 0; i < suitLength; i++) {
cards.add(new Card(denomination, Suits.values()[i]));
}
return cards;
}
private Card(Denomination denomination, Suits suit) {
this.denomination = denomination;
this.suit = suit;
}
public static Card of(final Denomination denomination, final Suits suit) {
return CARDS.get(denomination).stream()
.filter(card -> card.getSuit() == suit)
.findAny()
.orElseThrow(() -> new IllegalStateException("카드가 초기화되지 않았습니다."));
}
/***/
}
♟ 체스
기존 게임 정보 불러오기
Game객체에서, 기존 게임의 정보를 불러와 Game 객체를 초기화시킬 때 정적 팩토리 메서드를 활용했다.
새로운 게임의 경우, Game 생성자를 통해 생성하고
진행중이던 게임의 경우, 정적 팩토리 메서드를 호출하여 Game 객체를 생성하도록 했다.
기보에 따른 기물 이동이라는 복잡한 로직을 생성자에서 분리할 수 있었다.
public class Game {
private final long roomId;
private Color turn;
private final Board board;
public Game(long roomId) {
this.roomId = roomId;
this.turn = Color.WHITE;
this.board = BoardFactory.createBoard();
}
public static Game from(long roomId, List<Move> moves) {
Game game = new Game(roomId);
Board board = game.board;
for (Move move : moves) {
board.move(move.getSource(), move.getTarget());
}
game.turn = Color.calculateTurn(moves.size());
return game;
}
/***/
}
🚗 자동차(웹)
도메인 → DTO
도메인을 DTO 객체로 전환시킬 때, 정적 팩토리 메서드를 활용했다.
내부 구조를 캡슐화 한다는 장점을 가진다. (Car의 어떠한 속성으로 Response가 구성되는지 캡슐화한다.)
// 정적 팩토리 메서드 X
CarResponse carResponse = new CarResponse(car.getName(), car.getPosition());
// 정적 팩토리 메소드
CarResponse carResponse = CarResponse.from(car);
public class CarResponse {
private final String name;
private final int position;
private CarResponse(String name, int position) {
this.name = name;
this.position = position;
}
public static CarResponse from(Car car) {
String name = car.getName();
int position = car.getPosition();
return new CarResponse(name, position);
}
public String getName() {
return name;
}
public int getPosition() {
return position;
}
}
추가적으로, 정적 팩토리 메서드를 활용하면 생성자의 접근 제어자를 private으로 지정할 수 있다는 장점을 얻을 수 있다.
그렇게 되면 외부에서 해당 클래스의 생성자를 호출할 수 없기 때문에, 해당 클래스를 상속받지 못하도록 막을 수 있다.
(final class로 선언하는 것과 동일한 효과를 낸다.)
'우테코 5기 > 레벨1' 카테고리의 다른 글
[체스 미션] 클래스 abstract / final로 만들기 (6) | 2023.03.29 |
---|---|
[체스 미션] 상속 대신 전략패턴 도입하기 (4) | 2023.03.28 |
정적 팩토리 메서드로 생성자 다이어트 시키기 (4) | 2023.03.05 |
전략 패턴을 통한 테스트 (2) | 2023.02.17 |
View단에서 도메인 객체를 생성해 return하는게 좋을까, 아니면 입력값을 그대로 return하는게 좋을까? (3) | 2023.02.17 |