우테코 2단계 미션 강의 내용 중 들은 것을 정리해 보았다
int p = 0 ;
위와 같은 코드가 있다고 해보자.
직관적으로 봤을 때, 다음 코드가 무엇을 의미하는지를 예상할 수 있는가?
// 위치
int p = 0 ;
위처럼 주석을 통해 의도를 전달할 수 있겠지만, 코드 그 자체로 설명되도록 코드를 작성해야 유지보수 비용이 줄어든다
int position = 0 ;
따라서 다음과 같이 네이밍으로 설명을 잘 해야할 필요성이 있다
class Position(){}
위처럼 아예 원시값으로 포장해주면 더 비즈니스에 종속적일 수 있다.
class Car {
private String name;
private int position;
public String getName()
{
return name;
}
public int position() {
return position;
}
public void Forward()
{
position += 1;
}
public void back() {
position--;
}
}
-> 종합적으로 개발자의 의도가 드러나지 않아, 코드를 읽는 사람 입장에서 뇌에 혼란을 준다
-> 따라서 일관된 코드 스타일을 가져가야 할 필요성이 있다
class Lotto {
// 우승 로또 번호와 비교하여 상금을 얻는다.
Money calculatePrize(final WinningLotto winningLotto) { }
}
-> 추상화 계층이 커지고, 테스트코드 작성이 어려워질 것이다
final var neo = new Crew(
"neo",
10_000,
List.of("쌈밥", "김치찌개", "탕수육", "비빔밥"),
List.of("나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스")
);
final var neo = new Crew(
name: "neo",
balance: 10_000,
likeMenus: List.of("쌈밥", "김치찌개", "탕수육", "비빔밥"),
dislikeMenus: List.of("나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스")
);
final var neo = new Crew(
/*name*/ "neo",
/*balance*/ 10_000,
/*likeMenus*/ List.of("쌈밥", "김치찌개", "탕수육", "비빔밥"),
/*dislikeMenus*/ List.of("나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스")
);
final var taste = Taste.builder()
.like("쌈밥", "김치찌개", "탕수육", "비빔밥")
.dislike("나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스")
.build();
final var neo = new Crew(
"neo",
new Balance(10_000),
taste
);
final var taste = Taste.builder()
.like("쌈밥", "김치찌개", "탕수육", "비빔밥")
.dislike("나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스")
.build();
final var neo = new Crew(
"neo",
new Balance(10_000),
taste
);
class Menus {
public Menus(final List<String> menus) {
for (int i = 0; i < menus.size(); i++) {
for (int j = 0; j < i; j++) {
if (menus.get(i).equals(menus.get(j))) {
throw new IllegalArgumentException("메뉴는 중복될 수 없습니다.");
}
}
}
}
}
class Menus {
public Menus(final List<String> menus) {
if (menus.size() != menus.stream().distinct().count()) {
throw new IllegalArgumentException("메뉴는 중복될 수 없습니다.");
}
}
}
class Menus {
public Menus(final Set<String> menus) {
}
}
Optional<String> value = map.entrySet()
.stream()
.filter(e -> e.getKey().equals(key))
.map(Entry::getValue)
.findFirst();
public class RacingGame {
private List<Car> participants;
Position averagePosition() {
final var average = participants.stream()
.mapToInt(Car::position)
.average()
.orElse(-1); // 게임 참여자가 없다.
return new Position(average);
}
}
public class RacingGame {
private List<Car> participants;
Optional<Position> averagePosition() {
return participants.stream()
.mapToInt(Car::position)
.average()
.stream()
.map(Position::new)
.findAny();
}
}
public class RacingGame {
private List<Car> participants;
Position averagePosition() {
final var average = participants.stream()
.mapToInt(Car::position)
.average()
.orElseThrow(() -> new IllegalStateException("게임 참여자가 없습니다."))
return new Position(average);
}
}
예외를 발생하여 명시적으로 처리할 수도 있다
개발을 하다보면 모든 코드에서 null 체크나 예외처리를 할 수는 없다. 그렇기에 네이밍을 통해 의도를 드러내는 것이 중요하다.
public class RacingGame {
private List<Car> participants;
Position averagePosition() {
return participants.stream()
.mapToInt(Car::position)
.average()
.stream()
.map(Position::new)
.findAny()
.orElseGet(Position::ready);
}
}
public class RacingGame {
private List<Car> participants;
Position averagePosition() {
return participants.stream()
.mapToInt(Car::position)
.average()
.stream()
.map(Position::new)
.findAny()
.orElseGet(Position::ready);
}
}
class Car {
private int position;
int move(final int power) {
if (power < 4) {
return position;
}
return position++;
}
}
// 조회
final var position = car.move(0);
public class CrewMenuRecommendation {
private final Crew crew;
private final List<String> recommendedMenus;
public boolean canRecommendMenu(String menu) {
if (crew.dislikeMenu(menu)) {
return false;
}
if (recommendedMenus.contains(menu)) {
return false;
}
return true;
}
public void recommendMenu(String menu) {
if (cannotRecommendMenu(menu)) {
return;
}
recommendedMenus.add(menu);
}
}
enum Command {
RETRY,
QUIT;
}
public void run(final Command command) {
if (command == RETRY) {
retry();
}
if (command == QUIT) {
quit();
}
}
enum Command {
RETRY,
QUIT,
STATUS;
}
public void run(final Command command) {
if (command == RETRY) {
retry();
return;
}
if (command == QUIT) {
quit();
return;
}
throw new UnsupportedOperationException();
}
@Test
void testRun_allCommand() {
for (final var command : Command.values()) {
run(command);
}
}
public void run(final Command command) {
switch (command) {
case RETRY -> {
retry();
return;
}
case QUIT -> {
quit();
return;
}
}
throw new UnsupportedOperationException();
}
public enum BridgeGameResult {
UP_RIGHT(true, true) {
@Override
public boolean match(Position position, String tile) {
return position == Position.UP && position.equalPosition(tile);
}
},
UP_WRONG(true, false) {
@Override
public boolean match(Position position, String tile) {
return position == Position.UP && position.notEqualPosition(tile);
}
},
DOWN_RIGHT(false, true) {
@Override
public boolean match(Position position, String tile) {
return position == Position.DOWN && position.equalPosition(tile);
}
},
DOWN_WRONG(false, false) {
@Override
public boolean match(Position position, String tile) {
return position == Position.DOWN && position.notEqualPosition(tile);
}
};
private final boolean isPositionUp;
private final boolean isRightMove;
BridgeGameResult(boolean isPositionUp, boolean isRightMove) {
this.isPositionUp = isPositionUp;
this.isRightMove = isRightMove;
}
protected abstract boolean match(Position position, String tile);
}
class Position {
private int value;
public void increase() {
value++;
}
public Position decrease() {
value--;
}
}
class Position {
private final int value;
public Position increase() {
return new Position(value + 1);
}
public Position decrease() {
return new Position(value - 1);
}
}
List<String> excludeDislikeMenus(final List<String> menus) {
menus.removeAll(dislikeMenus);
return menus;
}
List<String> excludeDislikeMenus(final List<String> menus) {
final var excluded = new ArrayList<>(menus);
excluded.removeAll(dislikeMenus);
return excluded;
}
List<String> dislikeMenus() {
return dislikeMenus;
}
final var crew = new Crew("나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스");
final List<String> dislikeMenus = crew.dislikeMenus(); // ["나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스"]
dislikeMenus.clear();
crew.dislikeMenus(); // []
List<String> dislikeMenus() {
return new ArrayList<>(dislikeMenus);
}
final var crew = new Crew("나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스");
final List<String> dislikeMenus = crew.dislikeMenus(); // ["나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스"]
dislikeMenus.clear(); // []
crew.dislikeMenus(); // ["나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스"]
List<String> dislikeMenus() {
return Collections.unmodifiableList(dislikeMenus);
}
final var crew = new Crew("나시고렝", "파인애플 볶음밥", "미소시루", "하이라이스");
final List<String> dislikeMenus = crew.dislikeMenus();
dislikeMenus.clear(); // 예외 발생
+) 검증이 필요하지만 값만 반환하는 경우도 원시값 포장이 필요한가?