프로그래밍/리팩터링

리팩터링 2nd 실습하기 좋은 항목

라이프노트 2025. 4. 7. 08:32

Ch06.08 Introduce Parameter Object 매개변수 객체 만들기

//[Before]
public class Station {
...

	public List<Temperature> readingsOutsideRange(int min, int max) {
		return readings.stream()
				.filter(t -> t.getTemperature() < min || t.getTemperature() > max)
				.toList();
	}
}

//[After-Step01]
public class Station {
...

	public List<Temperature> readingsOutsideRange(NumberRange range) {
		return readings.stream()
			.filter(t->t.getTemperature() < range.getMin() || t.getTemperature() > range.getMax())
			.toList();
	}

//[After-Step02]
public class Station {
...
	public List<Temperature> readingsOutsideRange(NumberRange range) {
		return readings.stream()
			.filter(t->!range.contains(t.getTemperature()))
			.toList();
	}
}

 

ch06.11 split phase 단계 쪼개기

 

//[Before]
public class ReadOrder {
    public static void main(String[] args) {
        try {
            if (args.length == 0) throw new RuntimeException("파일명을 입력하세요.");
            String filename = args[args.length - 1];
            File input = new ClassPathResource(filename).getFile();

            System.out.println(input.getAbsolutePath());

            ObjectMapper mapper = new ObjectMapper();
            Order[] orders = mapper.readValue(input, Order[].class);

            if (Stream.of(args).anyMatch(arg -> "-r".equals(arg)))
                System.out.println(Stream.of(orders)
                        .filter(o -> "ready".equals(o.getStatus()))
                        .count());
            else
                System.out.println(orders.length);

        } catch (Exception e) {
            System.err.println(e);
        }
    }
}

 

//[After- 방법1]
public class ReadOrder26 {

    public static long run(String[] args) throws IOException {
        return countOrders(new CommandLine(args));
    }

    private static long countOrders(CommandLine commandLine) throws IOException {
        File input = new ClassPathResource(commandLine.filename()).getFile();
        ObjectMapper mapper = new ObjectMapper();
        Order[] orders = mapper.readValue(input, Order[].class);

        if (commandLine.onlyCountReady())
            return Stream.of(orders)
                    .filter(Order::isReady)     //캡슐화 : 메서드 이동
                    .count();
        else
            return orders.length;
    }

    private static class CommandLine {
        String[] args;

        public CommandLine(String[] args) {
            if (args.length == 0) throw new RuntimeException("파일명을 입력하세요.");
            this.args = args;
        }

        String filename() {
            return args[args.length - 1];
        }

        boolean onlyCountReady() {
            return Stream.of(args).anyMatch(arg -> "-r".equals(arg));
        }
    }
}

 

//[After-방법2]
// Strategy & Factory 적용
public class ReadOrder31 {

    public static long run(String[] args) throws IOException {
        return countOrders(new CommandLine(args));
    }

    private static long countOrders(CommandLine commandLine) throws IOException {
        // 팩토리 클랫 생성
        CountOrders countOrders = CountOrderFactory.createCountOrders(commandLine.onlyCountReady());

        return countOrders.count(getOrders(commandLine));
    }

    private static Order[] getOrders(CommandLine commandLine) throws IOException {
        File input = new ClassPathResource(commandLine.filename()).getFile();
        ObjectMapper mapper = new ObjectMapper();
        Order[] orders = mapper.readValue(input, Order[].class);
        return orders;
    }

    private static class CommandLine {
        String[] args;

        public CommandLine(String[] args) {
            if (args.length == 0) throw new RuntimeException("파일명을 입력하세요.");
            this.args = args;
        }

        String filename() {
            return args[args.length - 1];
        }

        public boolean onlyCountReady() {
            return Stream.of(args).anyMatch(arg -> "-r".equals(arg));
        }
    }
}

public interface CountOrders {
    long count(Order[] orders);
}

public class CountOrdersAll implements CountOrders {
    @Override
    public long count(Order[] orders) {
        return orders.length;
    }
}

public class CountOrdersOnlyReady implements CountOrders {
    @Override
    public long count(Order[] orders) {
        return Arrays.stream(orders)
                .filter(Order::isReady)
                .count();
    }
}

public class CountOrderFactory {
    public static CountOrders createCountOrders(Boolean onlyCountReady) {
        if (onlyCountReady)
            return new CountOrdersOnlyReady();
        else
            return new CountOrdersAll();
    }
}

 

 

 

Ch07.03 Replace Primitive with Object 기본형을 객체로 바꾸기

public class Order {
    private final Long id;
    private final String priority;

    public Order(Long id, String priority) {
        this.id = id;
        this.priority = priority;
    }

    public Long getId() {
        return id;
    }

    public String getPriority() {
        return priority;
    }
}


//[Before]
public class Orders {
    private final Order[] orders;

    public Orders(Order[] orders) {
        this.orders = orders;
    }

    public Order[] getOrders() {
        return orders;
    }

    public long highPriorityCount() {
        return Arrays.stream(orders)
                 .filter(o -> o.getPriority().equals("high") || o.getPriority().equals("rush"))
                .count();
    }

    public long lowerPriorityCount() {
        return Arrays.stream(orders)
                .filter(o -> o.getPriority().equals("low") || o.getPriority().equals("medium"))
                .count();
    }

    public long lowestPriorityCount() {
        return Arrays.stream(orders)
                .filter(o -> o.getPriority().equals("low"))
                .count();
    }

    public Order getOrder(int index) {
        return orders[index];
    }
}


class OrdersTest {
    @Test
    void testHighPriorityCount() {
        Order[] orders = {
                new Order(1L, "high"),
                new Order(2L, "low"),
                new Order(3L, "rush"),
                new Order(4L, "medium")
        };
        Orders ordersObj = new Orders(orders);

        assertEquals(2, ordersObj.highPriorityCount());
    }
}

 

//[After-방법1]
// 배열 index의 순번을 우선순위 값으로 응용
public class Priority {
	private final String value;

	public Priority(String value) {
		if (!legalValues().contains(value)){
			throw new IllegalArgumentException("Invalid Priority Value");
		}
		this.value = value;
	}

	public static List<String> legalValues(){
		return List.of("low", "normal", "high", "rush");
	}

	public int index(){
		return legalValues().indexOf(value);
	}

	public boolean higherThan(Priority other) {
		return this.index() > other.index();
	}

	public boolean lowerThan(Priority other) {
		return this.index() < other.index();
	}

	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;
		Priority priority = (Priority)o;
		return Objects.equals(value, priority.value);
	}

	@Override
	public int hashCode() {
		return Objects.hash(value);
	}

	@Override
	public String toString() {
		return value;
	}
}

 

//[After-방법2]
// Enum을 이용
public enum Priority {
    LOW,
    MEDIUM,
    HIGH,
    RUSH
}

public class Orders {
    private final Order[] orders;

    public Orders(Order[] orders) {
        this.orders = orders;
    }

    public Order[] getOrders() {
        return orders;
    }

    public long highPriorityCount() {
        return Arrays.stream(orders)
                 .filter(o -> o.getPriority() == Priority.HIGH || o.getPriority() == Priority.RUSH)
                .count();
    }

    public long lowerPriorityCount() {
        return Arrays.stream(orders)
                .filter(o -> o.getPriority() == Priority.MEDIUM || o.getPriority() == Priority.LOW)
                .count();
    }

    public long lowestPriorityCount() {
        return Arrays.stream(orders)
                .filter(o -> o.getPriority() == Priority.LOW)
                .count();
    }

    public Order getOrder(int index) {
        return orders[index];
    }
}

 

//[After-방법3]
//  Enum에 필드값과 메서드를 이용
public enum Priority {
    LOW(1),
    MEDIUM(2),
    HIGH(3),
    RUSH(4);

    Priority(int priorityLevel) {
        this.priorityLevel = priorityLevel;
    }

    private final int priorityLevel;

    public boolean isHigherThen(Priority other) {
        return this.priorityLevel > other.priorityLevel;
    }

    public boolean isLowerThen(Priority other) {
        return this.priorityLevel < other.priorityLevel;
    }

    public boolean isLowest() {
        return this.priorityLevel == LOW.priorityLevel;
    }
}

public class Orders {
    private final Order[] orders;

    public Orders(Order[] orders) {
        this.orders = orders;
    }

    public Order[] getOrders() {
        return orders;
    }

    public long highPriorityCount(Priority priority) {
        return Arrays.stream(orders)
                 .filter(o -> o.getPriority().isHigherThen(priority))
                .count();
    }

    public long lowerPriorityCount(Priority priority) {
        return Arrays.stream(orders)
                .filter(o -> o.getPriority().isLowerThen(priority))
                .count();
    }

    public long lowestPriorityCount( ) {
        return Arrays.stream(orders)
                .filter(o -> o.getPriority().isLowest())
                .count();
    }

    public long equalPriorityCount(Priority priority) {
        return Arrays.stream(orders)
                .filter(o -> o.getPriority().equals(priority))
                .count();
    }

    public Order getOrder(int index) {
        return orders[index];
    }
}