[워밍업 클럽 스터디 2기] 1주차 발자국 🐾
강의 : Readable Code: 읽기 좋은 코드를 작성하는 사고법1주차 진도섹션 2. 추상섹션 3. 논리, 사고의 흐름섹션 4. 객체 지향 패러다임 (SOLID) SRP(Single Responsibility Principle) : 단일 책임 원칙클래스는 단 하나의 책임을 가져야 한다.변경하는 이유는 단 하나여야 한다.SRP 원칙을 잘 지키면, 유지보수가 필요한 상황이 생겼을 때 수정할 대상이 명확해진다.클래스를 분리할 때 주의점어떤 파라미터를 전달할지 객체 입장에서 고민하기예) 익셉션 핸들러에게 e를 줄까? e.getMessage를 줄까? ⇒ e ( ∵ e를 가지고 뭘 할지는 핸들러가 알아서 해 ~ )클래스에게서 get으로 필드를 가져오는 건 무례하다구! Rude! ⇒ 메서드로 필요한 정보만을 요청하자예) 지뢰찾기 게임‘게임의 진입점’과 ‘지뢰찾기 실행’ 부분 분리입출력 관련한 것 분리게임보드 분리 OCP(Open Closed Principle) : 개방 폐쇄 원칙확장에는 열려 있고, 수정에는 닫혀 있어야 한다.새로운 요구사항이 생겼을 때, 기존 코드의 수정은 최소화 되고 새로운 기능 확장은 손쉽게 되도록 해야 한다.인터페이스를 활용하면 새로운 기능을 추가할 때기존 코드를 수정하지 않고새로운 구현체 클래스를 생성하면 된다.예) 지뢰찾기 게임 : 난이도에 따라 보드의 크기를 달리 하는 기능 추가상수로 박아둔 크기를 변수 처리GameLevel 인터페이스 사용각 난이도는 각 구현체로 LSP(Listov Substitution Principle) : 리스코프 치환 원칙부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다. 상속 구조로 리팩토링할 때, 타입 체크를 하는 방향은 좋은 설계가 아니다.기존 패턴을 유지하면서 통합하는 방식으로 인터페이스를 만들면 타입 체크가 생기게 돼 ⇒ 더 추상화된 관점에서 새로운 설계를 떠올릴 수 있어야 해예) 지뢰찾기 게임 : 지뢰 Cell, 숫자 Cell, 빈 Cell을 Cell을 상속 받는 구조로 리팩토링지뢰 Cell을 구현체로 만들었으므로 플래그를 세우는 turnOn 메서드 삭제숫자 Cell을 구현체로 만들었으므로 주변부 지뢰 개수를 세서 update하는 메서드 삭제 ⇒ 생성자로 넣기 ISP(Interface Segregation Principle) : 인터페이스 분리 원칙클라이언트는 자신이 사용하지 않는 인터페이스에 의존해서는 안 된다. 예) 지뢰찾기 말고 다른 게임이 추가되는 상황새로운 게임은 initialize 메서드 사용하지 않아 ⇒ Game 인터페이스를 GameInitial과 GameRunnable로 분리 DIP(Dependency Inversion Principle) : 의존 역전 원칙상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다. ⇒ 둘 다 추상화에 의존해야 한다.의존성의 순방향 : 고수준 모듈이 저수준 모듈을 참조하는 것의존을 역전시킨다 = 고수준, 저수준 모듈 모두 추상화에 의존한다.저수준은 구체 쪽에 가깝기 때문에 수정이 잦으므로 고수준이 추상화 된 스펙만 참조하도록 한다.저수준 모듈이 자유롭게 변경되어도 고수준 모듈에는 영향이 가지 않게 설계해야 한다. 예) 콘솔 외의 다른 I/O가 추가되는 상황현재 코드에서 수정하려 한다면, 마인스위퍼의 수정 불가피 해 (저수준 모듈의 변경이 고수준 모듈에 영향을 미치는 것)콘솔 핸들러를 구현체로 가지는 인풋,아웃풋 핸들러 인터페이스 생성해서 마인스위퍼에서 사용기존 메서드명이 어색해진다면 이것도 더 추상화 된 메서드 명으로 수정 : printXXX → showXXX 회고칭찬하고 싶은 점 : 워밍업 클럽의 미션을 성실히 수행했다.아쉬웠던 점 : 진도를 따라잡기 위해 리팩토링을 직접 고민해보는 과정은 생략했다.보완하고 싶은 점 : 강의에서 질문거리가 나오면 잠시 멈춰두고 혼자 고민하는 시간을 가지면 좋을 것 같다. 미션As Is public boolean validateOrder(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } else { if (order.getTotalPrice() > 0) { if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; } } else if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } } return true; }To Beif - else 문은 else 대신 return을 사용해서 Early return중첩 조건문은 메서드로 추출해서 사고의 depth 줄이기의미 단위로 공백 주기부정어 줄이기public boolean validateOrder(Order order) { if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } if (order.getTotalPrice() > 0) { return checkCustomerInfo(order); } log.info("올바르지 않은 총 가격입니다."); return false; } public boolean checkCustomerInfo(Order order) { if (order.hasCustomerInfo()) return true; log.info("사용자 정보가 없습니다."); return false; }