블로그

허수빈

[인프런 워밍업 클럽 2기 클린코드 & 테스트 코드] Day4 과제

해당 글은 [인프런 워밍업 클럽 2기 클린 코드 & 테스트 코드]에 참가하여 박우빈님의 <Readable Code: 읽기 좋은 코드를 작성하는 사고법> 강의를 듣고 작성한 글입니다. 1. 코드 리팩토링/* * 주문 검증 메서드 * 1. 주문 항목이 0 이상이어야 한다. * 2. 총 금액이 0 이상이어야 한다. * 3. 주문을 하는 사용자 정보가 있어야 한다. */ public boolean validateOrder(Order order) { return hasItems(order) && hasCustomerInfo(order) && isTotalPriceInvalid(order); } private static boolean hasItems(Order order) { if (order.getItems().size() > 0) { return true; } log.info("주문 항목이 없습니다."); return false; } private static boolean isTotalPriceInvalid(Order order) { if (order.getTotalPrice() > 0) { return true; } log.info("올바르지 않은 총 가격입니다."); return false; } private static boolean hasCustomerInfo(Order order) { if (order.hasCustomerInfo()) { return true; } log.info("사용자 정보가 없습니다."); return false; } 2. SOLID를 자기만의 언어로 정리하기SRP : Signle Responsibility Principle단일 책임 원칙하나의 클래스는 하나의 책임만 가진다.하나의 일 & 하나의 주제만 가져야 한다!OCP : Open - Closed Principle개방 폐쇄 원칙확장에 열려 있고 수정에 닫혀 있다.기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.LSP : Liskov Substitution Principle리스코프 치환 원칙자식 클래스는 부모 클래스를 대체할 수 있어야 한다.부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 변경했을 때, 프로그램이 잘 작동해야 한다.자식이 부모의 기능을 잘 물려 받아야 한다. -> 자식은 부모의 의지를 잘 담고 있어야 한다.ISP : Interface Segregation Principle인터페이스 분리 원칙사용하지 않는 기능을 의존하지 않도록 인터페이스를 여러 개로 분리한다. -> 필요한 기능만 구현하게 한다.인터페이스를 잘게 쪼개자!DIP : Dependency Inversion Principle의존 관계 역전 원칙고수준 모듈(추상화 레벨 높음)이 저수준 모듈(추상화 레벨 낮음)에 의존하면 안된다. 추상화에 의존해야 한다. -> 둘다 인터페이스에 의존해야 한다.고수준 모듈이 저수준 모듈에 직접적으로 의존하지 않고 인터페이스만 의존시켜 구현체를 쉽게 갈아끼울 수 있도록 한다. 전부 결합도를 낮추고 응집도를 높인다!

백엔드인프런워밍업클럽

jinjanic91

인프런 워밍업 클럽 2기 - CS 1주차 미션

CS 1주차 미션[운영체제]질문 1. 아래 코드는 1초 마다 플레이어가 스킬을 사용했는지 체크하는 코드입니다. 이 방식은 폴링방식입니다. 1초마다 체크하기 때문에 성능에 좋지 않습니다. 이를 해결하기 위한 방식으로 어떤 걸 이용해야 할까요?while(true){ wait(1); // 1초 멈춤 bool isActivated = checkSkillActivated(); // 체크 }답변인터럽트 방식을 이용합니다.인터럽트 방식이란?CPU가 입출력 명령을 내리고 다른 작업을 진행하며, 입출력이 발생하면 입출력 관리자가 CPU에게 신호를 보내주고, CPU는 해당 신호를 받아 ISR (인터럽트 서비스 루틴)을 실행하여 작업을 완료합니다. 질문 2. 프로그램과 프로세스가 어떻게 다른가요?답변프로그램하드디스크와 같은 저장장치(HDD, SSD)에 저장된 명령문의 집합체.애플리케이션 또는 앱이라고도 불립니다.윈도우 운영체제에서는 .exe 확장자를 가지고 있습니다.컴퓨터 관점에서는 저장장치만 사용하는 수동적 존재프로세스실행 중인 프로그램입니다.하드디스크에 저장된 프로그램이 메모리에 올라갔을 때 실행 중인 프로그램 즉, 프로세스가 됩니다.컴퓨터 관점에서 메모리와 CPU를 사용하며, 필요에 따라 입력과 출력도 하는 능동적 존재프로세스의 구성 요소는 다음과 같습니다code자신을 실행하는 코드가 저장되어 있음data전역 변수와 스태틱 변수가 저장되어 있음heap프로그래머가 런타임 시 동적으로 할당할 수 있는 메모리 공간stack지역변수와 함수 호출 시 필요한 매개변수와 돌아갈 주소가 저장됨 질문 3. 멀티프로그래밍과 멀티프로세싱이 어떻게 다른가요?답변멀티 프로그래밍메모리에 여러 프로세스가 올라온 것 (메모리 관점)멀티 프로세싱CPU가 시분할 처리로 여러 개의 프로세스를 처리하는 것 (CPU 관점)단, 오늘 날의 OS에서는 멀티 프로그래밍과 멀티 프로세싱이 공존합니다. 질문 4. 운영체제는 프로세스를 관리하기 위해서 어떤 것을 사용하나요?답변프로세스가 만들어지면 운영체제는 PCB (Process Control Block)을 만들어 저장합니다.운영체제는 PCB를 이용하여 프로세스를 관리하며, CPU가 프로세스 상태, 프로그램 카운터 등 PCB 내 정보들을 이용하여 프로세스를 처리할 수 있도록 합니다.여러 프로세스가 있는 경우 PCB 또한 여러 개 만들어지게 되는데, 이 PCB들은 연결 리스트 구조로 저장이 됩니다.프로세스가 종료되면 운영체제는 이 연결리스트에서 종료된 프로세스의 PCB를 제거합니다. 질문 5. 컨텍스트 스위칭이란 뭔가요?답변프로세스를 실행하는 중에 다른 프로세스를 실행하기 위해 실행중인 프로세스의 상태를 저장하고, 다른 프로세스의 상태값으로 교체하는 작업을 컨텍스트 스위칭이라고 합니다. PCB에 상태를 저장하기 때문에 컨텍스트 스위칭이 발생하면 PCB 내 프로세스 상태, 프로그램 카운터 및 레지스터 정보 등의 값이 변경됩니다. [ 자료구조와 알고리즘 ]질문 1. 여러분은 교실의 학생 정보를 저장하고 열람할 수 있는 관리 프로그램을 개발하려고 합니다. 이 때 여러분이라면 학생의 정보를 저장하기 위한 자료구조를 어떤 걸 선택하실 건가요? 이유를 함께 적어주세요.답변자료구조를 선택하기 전에 좀 더 조건(요구사항)을 다듬어야 할 것 같습니다.다음은 임의로 정한 조건입니다.교실에 최대 40명의 학생까지 존재할 수 있다.학생의 정보는 자주 변경되지 않는다. (1년 주기로 변동) 위 조건에서는 해시 테이블을 이용하여 정보를 저장할 것 같습니다.선택 이유는 다음과 같습니다.성능 상 이점을 얻을 수 있습니다.key만 알면 value를 바로 구할 수 있기 때문에 정보 열람 시 O(1)의 성능을 보입니다.정보가 자주 변경되지는 않겠지만, 변경 발생 시(추가, 변경, 삭제)에도 O(1)의 성능을 보입니다.각각의 학생은 고유한 데이터이기 때문에 중복되는 데이터가 없어 key-value 형태로 관리하기에 유리합니다.   질문 2. 여러분은 고객의 주문을 받는 프로그램을 개발하려고 합니다. 주문은 들어온 순서대로 처리됩니다. 이 때 여러분이라면 어떤 자료구조를 선택하실 건가요? 이유를 함께 적어주세요.답변 주문이 들어온 순서대로 처리되어야 하기 때문에, FIFO(First-in-First-out)의 특징을 가진 큐(Queue) 자료구조를 선택하겠습니다.      

CS운영체제알고리즘자료구조

세뇨르

워밍업 클럽 2기(클린코드, 테스트코드) 과제 (Day 4)

Readable Code: 읽기 좋은 코드를 작성하는 사고법(링크)코드 리팩토링기존 코드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; }리팩토링 코드public boolean validateOrder(Order order) { if (order == null) { log.info("주문 정보가 없습니다."); return false; } if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; } if (order.isInvalidTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.hasNoCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }public class Order { private List<Item> items; private double totalPrice; private CustomrInfo customerInfo; public boolean hasNoItems() { return items == null || items.isEmpty(); } public boolean isInvalidTotalPrice() { return totalPrice <= 0; } public boolean hasNoCustomerInfo() { return customerInfo == null; } }SOLID 정리SOLIDSRPSingle Responsibility Principle (단일 책임 원칙)하나의 클래스는 단 한 가지의 변경 이유만을 가져야 한다.객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경 되어야 함.관심사의 분리높은 응집도, 낮은 결합도OCPOpen-Closed Principle (개방-폐쇄 원칙)확장에는 열려 있고, 수정에는 닫혀 있어야 한다.기존 코드의 변경 없이, 시스템의 기능을 확장할 수 있어야 한다.추상화와 다형성을 활용해서 OCP를 지킬 수 있다.LSPLiskov Substitution Principle (리스코프 치환 원칙)상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.자식 클래스는 부모 클래스의 책임을 준수하며, 부모 클래스의 행동을 변경하지 않아야 한다.LSP를 위반하면, 상속 클래스를 사용할 때 오동작, 예상 밖의 예외가 발생하거나, 이를 방지하기 위한 불필요한 타입 체크가 동반될 수 있다.ISPInterface Segregation Principle (인터페이스 분리 원칙)클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.인터페이스를 잘게 쪼개야 함ISP를 위반하면, 불필요한 의존성으로 인해 결합도가 높아지고, 특정 기능의 변경이 여러 클래스에 영향을 미칠 수 있다.DIPDependency Inversion Principle (의존성 역전 원칙)상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다. 둘 모두 추상화에 의존해야 한다.의존성의 순방향 : 고수준 모듈이 저수준 모듈을 참조하는 것 의존성의 역방향 : 고수준, 저수준 모듈이 모두 추상화에 의존하는 것저수준 모듈이 변경되어도, 고수준 모듈에는 영향이 가지 않는다.

백엔드클린코드SOLID리팩토링

미션 Day4

미션 Day4 리팩토링하기 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; }public boolean validateOrder(Order order) { if (isEmptyOrder(order)) { log.info("주문 항목이 없습니다."); return false; } if (hasNoCustomerInfo(order)) { log.info("사용자 정보가 없습니다."); return false; } if (isInValidTotalPrice(order)) { log.info("올바르지 않은 총 가격입니다."); return false; } return true; } private boolean isInValidTotalPrice(Order order) { return !(order.getTotalPrice() > 0); } private boolean hasNoCustomerInfo(Order order) { return !order.hasCustomerInfo(); } private boolean isEmptyOrder(Order order) { return order.getItems().size() == 0; } SOLID에 대해 나의 언어로 정의하기 SRP : 하나의 클래스는 하나의 책임만을 가져야 한다. OCP : 기능 추가에는 열려있고, 수정에는 닫혀있어야 한다. LSP : 상속관계에서 자식클래스는 부모클래스의 기능을 대신할수있다. ISP : 인터페이스는 필요한 기능만 가지게 만들어야한다. DIP : 추상클래스에 의존하도록 만들어 교체가 쉽게 이루어질수있게 한다.

wisehero

[워밍업 클럽 2기 BE 클린코드 & 테스트코드] BE 2기

 첫 번째 미션 : 아래 코드를 읽기 좋은 코드로 변경하기아래의 코드는 물론 기능 구현상에서 오작동을 일으키는 코드는 아니겠지만 이번 섹션에서 배운 내용을 토대로 읽는 사람으로하여금 과부하까진 아니더라도 부하를 겪을 수 있습니다. 아래의 코드는 그럼 어떤 문제를 가지고 있을까요?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; }첫 번째는 캡슐화가 잘 지켜지지 않고 있습니다. 객체의 값을 지속적으로 getter 메서드를 가져오면서 확인하고 있습니다.이는 TDA(Tell, Don't Ask) 원칙을 지켜지지 않고 있고 객체가 그 자체의 역할을 하게끔 만들지 않았습니다. 두 번째는 읽는 사람으로 하여금 머리를 많이 쓰게 하고 있습니다. 머리를 많이 쓰게 한다는 것은 다음과 같은 이유에서 입니다.모든 조건문이 한 호흡에 이어져 있다.공백으로 각 작업간의 STEP을 구분하지 않았다.Depth가 깊다.부정 연산자 사용으로 인해서 조건을 한 번 더 이해해야 한다.이를 리팩토링 한다면 어떻게 할 수 있을까요? public boolean validateOrder(Order order) { // 주문 항목 확인 if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; } // 총 가격 확인 if (order.isTotalPriceInvalid()) { log.info("올바르지 않은 총 가격입니다."); return false; } // 사용자 정보 확인 if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; } 리팩토링한 코드는 위와 같습니다. Order 객체 스스로 판단할 수 있는 메서드로 조건문을 처리하여 TDA 원칙을 따랐습니다.공백을 둚으로서 각 작업의 STEP을 구분했습니다.3-Depth 까지 타고 들어가야하는 코드를 전부 1-Depth 이내에 처리했습니다.부정 연산자를 사용하기보단 더 의미있는 이름의 메소드를 만들어 직관적으로 조건문을 판단할 수 있도록 했습니다.과제를 수행하면서 느꼈던 것은 좋은 코드란 좋은 글을 작성하는 것과 마찬가지라는 느낌이었습니다. 좋은 글을 쓰는 첫 출발점은 단문 쓰기를 통해 호흡을 짧게하여 읽는 사람으로 하여금 부하를 느끼지 않게 하는 것이 중요했습니다. 문장의 마침표가 문장의 시작으로부터 너무 멀리 있게되면 이는 읽는 사람을 배려하지 않은 글쓰기이기 때문입니다. 클린 코드는 나만의 만족을 위한 것이 아니라 이타적인 마음을 다시 한번 갖게 되는 좋은 원칙인 것 같습니다. 두 번째 미션 : SOLID를 자신의 언어로 정리하기 SRP : Single Responsibility Principle단일 책임 원칙은 클래스 혹은 메서드, 함수는 한 가지 책임만 가져야 한다는 것입니다. 자동차를 예로 들어볼까요? 자동차 클래스를 작성한다면, 우리는 이 원칙을 지키기 위해 엔진, 연료, 네비게이션, 에어컨 조절, 음악 재생과 관련된 변수와 메서드를 전부 넣어놓으면 안됩니다. 자동차는 그저 여러 가지 요소의 조립이 끝난 채로 존재해야합니다. 그래서 우리는 엔진, 연료 시스템, 네비게이션, 오디오 클래스를 별도로 분리하고 그에 맞는 기능들을 작성해야 합니다. OCP - Open/Closed PrincipleOCP는 소프트웨어는 구성에 있어서 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다는 원칙입니다. 솔직히 많은 사람들이 이 이야기를 처음에 들었을 때 의구심을 품습니다. '어떻게 확장을 하는데 수정을 안할 수가 있어?' 라고요. 하지만 적어도 소프트웨어 세상에서는 가능합니다. 앞서 말했던 SRP를 지킨다면 말이죠. 만약 자동차 클래스에 오디오 관련 기능들을 전부 다 작성했다면 어떻게 되었을까요? 우리의 자동차에는 보스의 것도, 뱅앤올룹슨의 것도, 하만 카돈의 것도 들어갈 수 있습니다. 하지만 자동차 클래스에 오디오 관련 코드를 전부 작성했다면 자동차 클래스는 늘어나는 오디오 업체의 코드를 계속해서 생산해내야 합니다. 그러나 이전에 별도의 객체로 만들고 추상화까지 더했다면? 확장에 있어서 기존 코드를 수정하는 일은 없을 것이고 우리는 단순히 '추가'만 하면 될 것입니다. LSP - Liskov Substitution Principle리스코프 치환 원칙은 자식이 언제든지 부모를 대체할 수 있어야 한다는 원칙입니다. 다시 차량의 오디오 기능을 생각해봅시다. 오디오 인터페이스가 있고 오디오 인터페이스에 명세되어 있는 기능은 볼륨 켜키, 볼륨 끄기 입니다. 하지만 어느 한 회사의 특정 제품 클래스는 볼륨 켜키 기능이나 볼륨 끄기 기능이 기대한대로 동작하지 않는다면 어떻게 될까요? 예를 들어서 볼륨 끄기를 1씩 끄는게 아니라 한번에 0으로 만들어버린다거나 말이죠. 이렇게 되면 프로그램이 균일하게 동작하지 않을 것이고 서로가 서로를 완벽하게 대체할 수 없을 것입니다. 따라서 인터페이스를 준수하는 것이 LSP가 전달하고자 하는 메세지입니다. ISP - Interface Segregation PrincipleISP는 클라이언트가 사용하지 않는 인터페이스에 의존하지 않도록 하는 것입니다. 모든 자동차들은 구매할 때 고객이 원하는 옵션을 가지고 있습니다. 하지만 내 자동차에 내가 고르지 않은 옵션을 동작하는 버튼이 자리만 차지하고 있다면 어떻게 될까요? 이는 운전자에게 혼동을 줄 수 있고 불필요한 공간 낭비를 발생시킬 수 있습니다. 코드에서는 어떤가요? 특정 클래스가 구현하지 않아도 되는 부분을 구현해야할 것처럼 개발자에게 혼동을 줄 수 있고 이는 장애를 유발하거나 불필요한 용량 차지로 이어질 수 있습니다. 인터페이스는 가능한 범용적이 아닌 세부적으로 분리해야한다는 것이 ISP가 전하고자 하는 메세지입니다. DIP - Dependency Inversion PrincipleDIP는 고수준 모듈은 저수준 모듈에 의존해서는 안되고, 고수준 모듈과 저수준 모듈 모두 추상화에 의존해야 한다는 원칙입니다.이 역시 말이 어렵습니다. 저도 처음에 OCP 다음으로 어려웠던 것 같습니다. 하지만 위에서 자동차 예시로 다 말씀을 드렸습니다.자동차 클래스는 여러 인터페이스의 조합으로 이뤄져야 한다고 했습니다. 자동차 클래스는 가솔링 엔진, 전기 엔진과 같은 구체 클래스에 의존해서는 안됩니다. Engine 인터페이스가 있고 이를 구현한 가솔린 엔진과 전기 엔진이 있어야 합니다. 오디오 역시 오디오라는 인터페이스만 있을 뿐, 이를 구현한 각 제조사의 오디오가 있을 뿐이죠. 자동차 클래스는 따라서 어떤 특정 엔진이나 특정 오디오의 정보는 알 수 없습니다. 그저 인터페이스만 갖고 있을 뿐입니다.   

백엔드워밍업클럽백엔드

[클린 코드 & 테스트 코드 가이드 로드맵, 미션 Day4] 코드 리팩토링 및 SOLID

[미션]1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.#TO-BEpublic boolean validate(Order order) { if (Objects.isNull(order)){ return false; } if (order.isItemsEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (order.notValidateTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.notHasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }메서드 시그니처메서드명 변경 (validateOrder -> validate)파라미터 타입이 있어서 Order 삭제 구현부파라미터 검증 추가order이 null인 경우에도 유효하지 않다고 응답추상화 레벨에 맞춰 order == null이 아닌 Objects.isNull(order) 적용Early Return 적용실제 검증항목은 아이템 개수, 아이템 가격합계, 고객 정보 유무 3가지이고, 각각 독립적이므로 불필요한 else, else if 구문 삭제부정연산자(!) 대신 메서드명에 명시반환타입수정 없음 #AS-ISpublic 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; }2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP공통적인 것들로 묶어주기같은 기능 : 메서드같은 도메인 : 클래스OCP동적으로 활용할 수 있게 설계LSP부모의 기능을 자식들이 공통으로 쓸 수 없다면 분리하기ISP인터페이스는 최소 단위별로 생성DIP추상에 의존 

백엔드

도호

[인프런워밍업클럽2기] DAY 4 미션

코드 리팩토링요구사항 사용자가 생성한 '주문'이 유효한지 확인해야한다.주문 항목은 1개 이상이어야한다.주문 전체 가격은 1원 이상이어야 한다.주문한 사용자 정보는 필수 정보이다.Order는 주문 객체이고, 필요하다면 Order에 추가 메서드를 만들 수 있다.필요하다면 메서드를 추출할 수 있다.public boolean validate(Order order) { if (order.isEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (order.isInvalidPrice()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.isInvalidUser()) { log.info("사용자 정보가 없습니다."); return false; } return true; }SOLIDSingle Responsibility Principle (단일 책임 원칙)하나의 모듈은 하나에 대한 책임만을 가져야한다.클래스 내부 private 메서드가 많거나 단위테스트가 애매한 경우에는 책임을 분리하는 것을 고려할 수 있다. Open-Close Principle (개방-폐쇄 원칙)확장에 열려있고 수정엔 닫혀있어야한다.새로운 기능을 추가할 때 기존 기능에 영향 없이 추가할 수 있어야한다. 즉 서로간의 의존 관계를 최소화해야한다.이 원칙은 B기능을 추가하였는데 A기능 테스트가 깨짐으로써도 확인할 수 있다.Liskov Substitution Principle (리스코프 치환 원칙)부모 클래스의 역할을 자식이 대체할 수 있어야한다.List 인터페이스의 구현체를 LinkedList에서 ArrayList로 변경하더라도 결과가 동일해야한다.Interface Segregation Principle (인터페이스 분리 원칙)하나의 인터페이스가 많은 책임을 가지고 있다면 인터페이스를 분리해야한다.Dependency Inversion Principle (의존관계 역전 원칙)구현체를 직접 사용하는 것 보다 추상화 된 것을 바라보는 것이 확장에 용이하다.List를 사용하는 것이 ArrayList를 사용하는 것 보다 확장에 유리하다.

백엔드인프런워밍업클럽2기

hhw0850

워밍업 클럽 2기 BE 클린코드&테스트코드 DAY 4 미션

Readable Code: 읽기 좋은 코드를 작성하는 사고법 수강 후 작성한 DAY 4 미션입니다.  1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.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; }사용자가 생성한 '주문'이 유효한지를 검증하는 메서드.Order는 주문 객체이고, 필요하다면 Order에 추가적인 메서드를 만들어도 된다. (Order 내부의 구현을 구체적으로 할 필요는 없다.)필요하다면 메서드를 추출할 수 있다.  풀이과정validateOrder 메서드에서 수행하는 기능을 간단하게 정리하면,주문 항목이 없는지 체크총 가격이 0보다 작은 수인지 체크사용자 정보가 없는지 체크총 3단계로 정리하였다. 그리고 각 단계에서 Early Return을 적용하여 불필요한 else를 없애고 depth를 줄였다.public boolean validateOrder(Order order) { // 1. 주문 항목이 없는지 체크 if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; } // 2. 총 가격이 0보다 작은 수인지 체크 if (!(order.getTotalPrice() > 0)) { log.info("올바르지 않은 총 가격입니다."); return false; } // 3. 사용자 정보가 없는지 체크 if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; } 위 코드에서 부정 연산자(!)를 긍정어로 수정하고 get~ 같은 직접적인 메소드명 대신 Order 객체에게 의도된 메세지를 전달하여 가독성 좋게 if문을 수정하였다.public boolean validateOrder(Order order) { // 1. 주문 항목이 없는지 체크 if (order.hasNoneItem()) { log.info("주문 항목이 없습니다."); return false; } // 2. 총 가격이 0보다 작은 수인지 체크 if (order.isTotalPriceLessThenZero()) { log.info("올바르지 않은 총 가격입니다."); return false; } // 3. 사용자 정보가 없는지 체크 if (order.hasNoneCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP: Single Responsibility PrincipleSRP는 하나의 클래스, 하나의 객체는 하나의 책임만을 가져야 한다는 원칙이다. 우리는 코드를 작성할 때 응집도가 높게, 결합도가 낮게 동작하도록 구현하여야 하는데 SRP를 지키게 되면 높은 응집도, 낮은 결합도를 가지게 된다. 응집도란 클래스나 모듈 내에 있는 요소들이 연관되어 있는 정도를 말하며 SRP를 지키면 이 연관되어 있는 정도가 낮아지므로 응집도는 높아진다. 결합도는 두 개 이상의 객체가 협력한다고 했을 때 하나의 객체가 변경될 경우 다른 객체가 영향 받는 정도를 의미한다. SRP를 지키면 두 객체간 의존성을 최소화시킬 수 있으므로 결합도를 낮출 수 있다. OCP: Open-Closed PrincipleOCP는 확장에는 열려있고, 수정에는 닫혀있어야 한다는 원칙이다. 우리는 코드를 한 번 작성하게 되면 끝이 아니라 지속적으로 코드를 수정하게되는 상황이 발생한다. 만약 코드 작성 후 새로운 요구사항이 발생하면 기존 코드를 수정해야하는데 기존 코드의 변경 없이 새로운 요구사항을 적용시킬 수 있어야 한다. 그렇게 하기 위해서는 인터페이스와 같은 추상을 통해 개방-폐쇄 원칙을 지킬 수 있으며 항상 기능의 확장을 고려하여 코드를 작성해야 한다. LSP: Liskov Substitution PrincipleLSP는 상속 구조를 설계할 때 부모 클래스를 자식 클래스로 변경해도 동일 결과를 낼 수 있도록 설계해야한다는 원칙이다. 자식 클래스는 부모 클래스의 기능 + @로 구현되어야 하고 부모 클래스의 기능이 변경되면 안 된다. 만약 LSP를 위반한다면 상속 구조에서 오동작이 발생할 수 있으므로 LSP를 지켜 상속 구조를 설계해야 한다. ISP : Interface Segregation PrincipleISP는 기능 단위로 인터페이스를 분리하라는 원칙이다. 하나의 인터페이스 안에 기능이 여러가지 들어있다면 인터페이스 사용 시 사용하지 않는 기능이 생길 것이다. 이렇게 되면 불필요한 의존성이 발생하므로 결합도가 높아지게 된다. 또한 인터페이스에 기능 변경이 생겼을 때 여러 클래스에 영향을 끼칠 수 있다. 이를 방지하게 위해서 인터페이스는 기능 단위로 잘게 쪼개서 사용해야한다. DIP: Dependency Inversion PrincipleDIP는 상위 수준의 모듈은 하위 수준의 모듈에 의존하면 안되며 모두 추상화에 의존해야한다는 원칙이다. 상위 수준의 모듈은 추상화 레벨이 높은 모듈을 의미하고, 하위 수준의 모듈은 추상화 레벨이 낮은 모듈을 의미한다. 만약 상위 수준의 모듈이 하위 수준의 모듈을 참조하게되면 문제가 발생할 수 있다. 하위 수준 모듈은 구체에 가깝기 때문에 변경될 가능성이 높다. 변경 가능성이 높다면 상위 수준 모듈에도 영향을 줄 수 밖에 없다. 그래서 인터페이스와 같은 추상을 통해 상위수준, 하위수준 모듈 모두 직접 서로 의존하는게 아니라, 추상화에 의존하도록 하는 의존성 역전이 필요하다.  

백엔드

인프런 스터디 미션 4

1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.✔ 사용자가 생성한 '주문'이 유효한지를 검증하는 메서드.✔ Order는 주문 객체이고, 필요하다면 Order에 추가적인 메서드를 만들어도 된다. (Order 내부의 구현을 구체적으로 할 필요는 없다.)✔ 필요하다면 메서드를 추출할 수 있다.변경 전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; }해당 코드는 주문이 유효한지 판단하는 코드인 것 같다.의미 단위로 보면1. 주문항목이 존재하는지 판단하는 부분이다.getter를 사용하는 부분을 hasNoItems() 메서드로 변경하고 항목이 존재하지 않는다면 false를 리턴하도록 리팩토링한다.그 뒤 else 부분은 필요 없으므로 else는 제거한다.2. 총 가격이 유효한지 판단하는 부분이다.사용자 정보 유효성을 판단하는 if문까지 같이 있기 때문에 우선 이 부분을 분리하고getter를 사용하는 부분을 wrongTotalPrice() 메서드로 변경한다. 그 뒤의 else if와 else 또한 필요 없으므로 제거한다.3. 사용자 정보가 유효한지 판단하는 부분이다.두 번째에서 분리한 if문을 !가 사용되고 있는데 이것을 부정의 의미를 가진 hasNotCoustomerInfo() 메서드로 변경한다.마지막으로 이 3개의 if문들을 의미 단위로 공백을 준 뒤, 마지막에 if문에 걸리는 것이 없는 경우 true를 리턴 할 수 있도록 return true를 한다.변경 후public boolean validateOrder(Order order) { if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; } if (order.wrongTotalPrice()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.hasNotCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }   2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.SRP : Single Responsiblity Principle단일 책임 원칙 말 그대로 책임을 하나만 갖는다는 것이다. 하나의 책임은 하나의 역할을 갖는다는 의미와 같다.ex) 옛날 레노버 키보드를 보면 빨콩이라고 키보드에서 마우스 역할을 하는 트랙 포인터가 달려 있다. 현재는 키보드와 마우스를 따로 분리한 것이 더 효율적이다.OCP : Open-Closed-Principle개방-폐쇄 원칙으로 확장에는 열려 있고 변경에는 닫혀 있다.ex) 기능의 확장은 한 클래스 내의 메서드 추가로 가능하지만 기능의 변경은 클래스 내에 존재하는 메서드의 내용을 변경하는 일이기 때문에 기존의 코드가 변경된다.LSP : Liskov Substitution Principle리스코프 교체 원칙으로 상속 구조에서 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있다.ex) 부모 클래스 내에 잡기, 때리기, 뛰기 3개의 기능을 하는 메서드가 있을 때 어떤 한 자식 클래스에서 잡기, 때리기, 눕기라는 부모 클래스와는 다른 메서드가 존재할 경우 예외가 발생할 수 있다.ISP : Interface Segregation Principle인터페이스 분리원칙으로 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.ex) 어떤 인터페이스를 구현한 구현체가 2개가 있다. 그런데 한 구현체에서는 인터페이스 내의 기능들을 모두 사용하지만, 나머지 한 구현체는 인터페이스 내의 기능 중 단 한 가지 기능만을 사용한다. 이를 해결하기 위해 인터페이스를 필요한 기능 단위로 분리한 뒤 구현체에 필요한 인터페이스들을 상속시켜주면 된다.DIP : Dependency Inversion Principle의존성 역전 원칙으로 상위 모듈은 하위 모듈에 의존하면 안 되며, 둘 다 추상화에만 의존해야 한다.하위 모듈이 변경되어도 상위 모듈에는 영향이 가지 않는다.ex) 스마트폰이라는 상위 모듈이 존재할 때 스마트폰은 어플이라는 인터페이스를 통해 다양한 기능의 어플들을 사용할 수 있다.지도 어플, 금융 어플, 게임 어플 등의 하위 모듈은 어플이라는 인터페이스를 통해 구체화된다.    

워밍업 클럽 2기 BE 클린코드&테스트 코드 DAY 4 미션

1. 아래 코드와 설명을 보고, [섹션 3. 논리, 사고의 흐름]에서 이야기하는 내용을 중심으로 읽기 좋은 코드로 리팩토링해 봅시다.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; }섹션 3에서 강조하는 것은 뇌 메모리 적게 쓰기. 즉, 코드를 읽는 사람으로 하여금 생각을 많이하지 않도록 하는 것이다. 그 방법에는 여러가지가 있는데, 순서대로 수정하면서 알아보자.if (order.getItems().size() == 0) { log.info("주문 항목이 없습니다."); return false; }우선 첫 줄부터 마음에 들지 않는다. 코드의 의도를 보니 주문(Order ) 내의 상품(Item) 컬렉션의 요소 개수가 0개인 지 확인하는 것으로 추측된다. 해당 코드에 Order , Item 객체에 대한 내용은 없으니 해당 클래스도 수정한다는 가정하에 진행하겠다. Items를 꺼내지 말고, Order 클래스에 Item 컬렉션이 비었는 지확인하는 메시지인hasNoItems() 을 만들어 다음과 같이 수정해보자.if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; }좀 더 직관적이고 객체지향적으로 수정되었다. 하지만 아직 마음에 들지 않는 것이 있는데, 바로 다음에 나올 else이다. 주문 항목이 없다면 return false 로 해당 메서드를 빠져나가기 때문에 다음 else가 있을 필요가 없다. 메서드에서 if와 else 가 공존하려면 if-else 뒤에 해당 if문에서 분기된 코드가 if-else 뒤에서 사용되는 경우밖에 없다. Early Return을 해보자. 사실 이미 Early Return이긴 하다.. else만 없애보자.public boolean validateOrder(Order order) { if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; } // 주문 항목이 없으면 Early Return! if (order.getTotalPrice() > 0) { // (1) if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; } } else if (!(order.getTotalPrice() > 0)) { // (2) log.info("올바르지 않은 총 가격입니다."); return false; } return true; }else 문이 한 레벨 사라졌기 때문에 전체적인 depth가 1 줄었다. 하지만 depth가 최대 2이므로 직관적인 사고를 하기에는 피곤하다. 남은 코드들도 depth를 줄여보자. 코드(사고)의 depth를 줄이는 데에는 Early Return이 최고다. 위에서도 봤지만, Early Return을 하면 굉장히 직관적으로 코드를 읽을 수 있게 된다. (1), (2) 분기문이 있는데, (1)은 아직 2 depth 이기 때문에 (2) 부터 Early Return 하는 게 쉽겠다.public boolean validateOrder(Order order) { if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; } // 주문 항목이 없으면 Early Return! if (!(order.getTotalPrice() > 0)) { // (2) log.info("올바르지 않은 총 가격입니다."); return false; } if (order.getTotalPrice() > 0) { // (1) if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; } } return true; }(2)를 Early Return을 하고 보니 분기 조건이 굉장히 거슬린다. 부등호를 굳이 Not 연산을 사용해서 한 것부터 약간 킹받는다. getter로 객체를 조회하여 비교하지말고, 메시지를 보내보자. if (order.isTotalPriceNegative()) { // (2) log.info("올바르지 않은 총 가격입니다."); return false; }주문이 음수인 지 확인하는 메서드(메시지)를 만들어 Order 객체로부터 정보를 얻자. 이제 (1)을 손볼 차례다. 우리가 (2)를 Early Return 하면서 (1)의 가장 최상위 분기는 의미 없어졌다. (2)를 통해 Early Return 되었다면 Order의 Total Price는 0 이상이라는 것이기 때문이다. if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } else { return true; }이제 (1)도 depth가 1이 되었다. 하지만 여기서도 Early Return으로 else 를 없앨 수 있다. depth를 최소화하면서 Early Return을 적용한 코드를 확인해보자.public boolean validateOrder(Order order) { if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; } // 주문 항목이 없으면 Early Return! if (order.isTotalPriceNegative()) { log.info("올바르지 않은 총 가격입니다."); return false; } // 총 가격이 0 이하이면 Early Return! if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } // 사용자 정보가 없으면 Early Return! return true; }정리하면서, 각 if문 사이에 공백을 한 줄씩 추가했다. 이렇게 논리 사이에 공백을 주는 것은 생각의 컨텍스트를 분리시킬 수 있는 좋은 방법이다. 예를들어 주문 항목이 없는 것과 가격이 0 이하인 것은 전혀 다른 문제이기 때문에 굳이 줄을 붙여서 사고를 연결할 필요가 없다.거의 다 마무리 됐다 싶었는데 부정 연산 `!` 하나가 거슬린다. 부정 연산은 읽는 사람으로 하여금 한 번 더 생각하게 만든다. if (!order.hasCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; }이 코드의 분기 조건을 읽자하면, 주문에 사용자 정보가 있는 지 확인하고 그것을 뒤집어서 사용자가 없는 지를 확인하는 게 되는데.. 이미 말로만 해도 복잡하고 귀찮다. ! 는 될 수 있으면 최대한 사용하지 않고 메서드명으로 추상화하자. if (order.hasNoCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; }모든 작업이 완료되었다. 1번 미션의 최종 코드를 확인해보자.public boolean validateOrder(Order order) { if (order.hasNoItems()) { log.info("주문 항목이 없습니다."); return false; } if (order.isTotalPriceNegative()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.hasNoCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }2. SOLID에 대하여 자기만의 언어로 정리해 봅시다.Robert.C.Martin의 SOLID는 어쩌면 캡추상다 보다 중요한 객체지향 이론이 되었다. 하나씩 살펴보자.SRP (Single Responsibility Principle)단일 책임 원칙. 하나의 객체는 하나의 책임만을 가져야한다는 뜻이다. SRP에서 가장 중요한 것은 객체를 변경해야할 사유가 단 하나라는 것이다. 개인적으로 SOLID 원칙 중에 가장 직관적이고 쉬운 원칙이라고 생각한다. 하지만 실제로 개발하다보면 가장 지키기 어려운 것이 또 SRP이다. 객체지향프로그래밍은 여러 객체가 각각의 책임을 가지고 협력하는 시스템이다. 책임을 각각 가져야한다. 내 책임이 아니면 다른 객체에게 이관하자.OCP (Open-Closed Principle)개방 폐쇄 원칙. 확장에는 열려있고 수정에는 닫혀있다는 원칙이다. OCP는 추상화를 통해 코드의 변경이 아닌 확장이 중요하다. 뒤에 나올 DIP와 OCP가 꽤 맞물려 있다고 생각한다.LSP (Liskov Substitution Principle)리스코프 치환 원칙. 상속 관계에서 자식클래스는 부모 클래스의 역할을 할 수 있어야 한다. 예를 들어, 부모 클래스의 run()은 스레드를 실행시키는 메서드인데, 자식 클래스의 run()은 육상 경기를 시작하는 메서드이면 안 된다. 그렇게 되면 해당 클래스를 참조하는 클라이언트 코드 입장에서 더 이상 그 클래스의 모든 족보를 신뢰하지 못하고 난처해진다.ISP (Interface Segregation Principle)인터페이스 분리 원칙. 어떤 인터페이스의 구현체는 사용하지 않는 메서드까지 구현할 필요가 없다. 그 말은 즉, 인터페이스가 쓸 데 없이 너무 광범위한 메서드까지 추상메서드로 가지고 있다는 것이다. 추상화는 구현으로 부터 공통 개념을 뽑아내는 것이다. 그렇다면 구현이 우선일까 추상이 우선일까? 메서드를 누구에게 맞춰야할까?DIP (Dependency Inversion Principle)의존성 역전 원칙. 구현(저급 모듈)이 아닌 추상(고급 모듈)에 의존하라는 것이다. OCP와 맞물려 Spring 프레임워크에서 매우 중요한 원칙이라고 생각한다. A 객체가 B 객체를 의존할 때, B 객체의 저수준인 구현체를 의존하게 되면 구현체를 바꿀 때 마다 A 객체의 코드를 수정해야 한다. 이 행위는 OCP를 지키지 못하는 모습도 된다. DIP를 위해 B 객체의 추상화된 고수준 모듈(인터페이스, 추상클래스, 부모 클래스)을 의존하고, 외부에서 A 객체를 생성할 때 B 객체의 실제 구현체에 대한 의존성을 주입(DI)하게 되면 A 객체를 B 객체 때문에 변경할 일은 없을 것이다.

Seul Ki Lee

워밍업 클럽 2기 BE 클린코드&테스트 day4 과제

Readable Code: 읽기 좋은 코드를 작성하는 사고법(링크)아래 내용은 위의 인프런 강의를 들으면서 과제 및 내용을 정리해봤습니다. 아래 코드 리팩토링 하기public boolean validateOrder(Order order){ if(order.getItems().size() == 0){ log.info("주문 항목이 없습니다"); }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; }본 코드의 문제는 아래와 같다if, else if, else 그리고 중접 if 등으로 인하여 주문의 유효성 조건이 한 눈에 들어오지 않는다=> early return을 사용해보자 무분별한 getter의 사용(if(order.getItems().size() == 0))=> 주문에 담긴 상품이 있는지 확인하는 내용인데 굳이 getter를 써야할까?=> 오히려 order에 item 항목이 있다고 세부 내용을 노출해버리고 있다=> 의미있는 이름의 메소드를 order에서 그 역할을 책임하도록 해보자 부정어의 사용 if(order.hasCustomerInfo()) / !(order.getTotalPrice() > 0)=> 부정어를 사용하면 코드의 가독성을 해친다 => 1순위 긍정어를 사용하여 메소드 만들 수 있는지 확인=> 2순위 긍정어를 사용할 수 없다면 메소드명으로 부정을 나타내서 만들어보자 위의 내용을 고려한 최종 코드는 아래와 같다public boolean validateOrder(Order order){ if(order.isEmptyOrderItem()){ log.info("주문 항목이 없습니다"); return false; } if(order.isLessThanOrEqualToZero()){ log.info("올바르지 않은 총 가격입니다"); return false; } if(order.isEmptyCustomerInfo()){ log.info("사용자 정보가 없습니다"); return false; } return true; }public class Order{ private List<Item> items; public boolean isEmptyOrderItem(){ return order.getItems().size() == 0; } public boolean isLessThanOrEqualToZero(){ return order.getTotalPrice() <= 0 }; public boolean isEmptyCustomerInfo(){ return !hasCustomerInfo(); } public double getTotalPrice(){...} public boolean hasCustomerInfo(){...} }SOLID에 대하여 자기만의 언어로 정리SRP클래스가 변경될 이유는 단 하나다!!클래스 설계 시, 하나의 관심사로 응집하여 구현하도록 하자OCP기존 코드의 변경 없이, 기능 추가하도록 설계가 되어야 한다 다형성을 이용하여 클라이언트 코드의 변경 없이 구현체를 쉽게 변경이 가능하다LSP부모 클래스에서 정의한 스펙을 자식 클래스에서도 지켜야 한다instance of로 타입 체크하여 특정 타입에 대해서만 처리가 필요하다는 건 LSP를 위반한 사례이다!!ISPSRP와 비슷하게 인터페이스 또한 한 가지 책임을 가지도록 분리를 잘해야 한다DIP클래스 참조 시, 추상화 레벨이 높은 인터페이스, 추상클래스, 상위 클래스를 참조하도록 하자코드에 구현체가 드러나게 되면 기능 변경시, 클라이언트 코드의 변경이 생기므로추상화 레벨이 높은 인터페이스 등을 메소드 파라메터, 필드, 리턴타입으로 활용하도록 하자

백엔드ReadableCode워밍업클럽2기

seonman.kim

워밍업 클럽 2기(클린코드, 테스트코드) 과제

Readable Code: 읽기 좋은 코드를 작성하는 사고법(링크)이 글은 위의 강의중 제가 중요하다고 생각한 부분과, 저만의 의견을 덧붙여서 정리해보았습니다.모든 내용과 정확한 내용을 보고싶다면 위의 강의를 수강하여 들으시는 것을 제안 드립니다. SOLIDSRPSingle Responsibility Principle (단일 책임의 원칙)하나의 객체는 하나의 역할만 해야 한다. 하나의 객체에서 하나의 역할만 처리하기 때문에 높은 응집도와 낮은 결합도를 유지하게 된다.하나의 기능을 하나의 객체에서만 처리하기 때문에 처리에 필요한 요소는 하나의 클래스에 존재하여 응집도가 향상된다.하나의 객체에서만 처리하기 때문에 다른 클래스와의 결합도는 약해진다. OCPOpen-Closed Principle (개방 폐쇄 법칙)(기능)확장에는 열려있고, (코드) 수정에는 닫혀있는 방식, 즉, 코드를 수정하지 않고 기능 추가를 할 수 있는 방식인터페이스와 다형성을 이용하여 구현한다.LSPLiskov Substitution Principle상속 관계에서 자식이 부모 클래스의 역할을 변경하거나 영향을 주면 안된다.ISPInterface Segregatin Principle (인터페이스 분리 원칙)인터페이스의 구현한 클래스는 인터페이스의 모든 메소드를 사용해야 한다.문법 상으로 전부 사용해야 하지만, 사용하면 예외(Unsupport Opeartion Exception)를 발생시키는 방식으로 사용하지 않을 수 있다.DIPDependency Inversion Principle (의존성 역전 방식)높은 추상화 객체에서 낮은 낮은 추상화 객체로 의존해야 하지만, 모든 객체는 높은 추상화 객체를 의존해야 한다.낮은 추상화 객체를 참조하면, 구현이 달라졌을 경우 의존하는 모든 객체에 영향이 생긴다.

kimusvita

Readable Code (1) : 읽기 좋은 코드를 작성하는 사고법 (Day2)

추상강사님이 최대한 더럽고 시간 복잡도가 높은 최악의 코드를 골라오셨고, 우리는 이번 시간을 통해 이 코드를 읽기 좋은 코드로 거듭나기 위한 순서를 따라갔다. 1. 직관적으로 변수이름을 바꾸기를 실행하자.예를 들어, for loop에서 단순히 i, j로 두던 것을 row, col로 바꾸는 등의 refactoring을 진행했다.Refactoring Variable# for (int i = 0; i < 8; i++) { # for (int j = 0; j < 10; j++) { # board[i][j] = "□"; # } # } //수정 사항: 변수 이름 직관적으로 짓기 for (int row = 0; row < 8; row++) { for (int col = 0; col < 10; col++) { board[row][col] = "□"; } }   2. 잘 쓰여진 메서드는 주제가 하나다.한 가지를 실행하는데 집중한다. 코드를 읽고 이 메서드가 하는 일이 여러 개라면 작은 메서드로 나누는 게 좋다.기본적이지만 처음엔 어려웠던 부분인데 다시 짚고 넘어가봐서 좋았다. 3. 메서드 선언부- void 대신 충분히 반환할 값이 있는지 고민해보는 습관을 가지자. 반환값이 있다면 테스트도 용이해진다. 물론 void가 더 깔끔한 순간도 많으나, 한번쯤 고민해보자.- 구체화된 타입을 반환받는다.- 의미가 명확한 변수 이름을 사용한다._사용하는 입장에서도 더 편하게 쓸 수 있고, 후손 개발자 입장에서도 어떤 것을 넣어야하는 변수인지 알기 쉽다._  4. 추상화 레벨method를 추출한다는 것 자체가 외부 세계와 내부 세계의 경계를 만든다는 뜻이다. 하나의 세계 안에서는 추상화 레벨이 동등해야 한다.추상화 레벨이 맞지 않아 일어나는 문제가 꽤나 잘 들어난다.추상화 레벨이 맞지 않는 상황이 나타나면 클린코드라 하기 어렵다. 자세한 사항은 개인 블로그에 기재하였습니다.https://velog.io/@dhlee47-l/Readable-Code-%EC%9D%BD%EA%B8%B0-%EC%A2%8B%EC%9D%80-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EC%82%AC%EA%B3%A0%EB%B2%95-Day2

웹 개발CleanCode박우빈강사님추상JAVA발자국

seonman.kim

인프런 워밍업클럽 2일차

Readable Code: 읽기 좋은 코드를 작성하는 사고법(링크)이 글은 위의 강의중 제가 중요하다고 생각한 부분과, 저만의 의견을 덧붙여서 정리해보았습니다.모든 내용과 정확한 내용을 보고싶다면 위의 강의를 수강하여 들으시는 것을 제안 드립니다. 논리 사고의 흐름정리 시스템에서 중요한 과제는 최소의 인지적 노력으로 최대의 정보를 제공하는 것이다.정보를 제공하는 것은 공통된 특징을 추상화하고 범주화하여 정리 시스템을 만드는 것이다.인지적 경제성 : 최소한의 인지로 최대한 성능을 내는 것핵심은 뇌의 메모리를 적게 쓰는 것이다.1. Early returnif-else 절을 사용할 때 두 조건을 동시에 기억해야 하기 때문에 코드 분석하기 복잡해진다.if 조건을 참일 경우 리턴함으로써 else 를 사용하지 않아도 된다. 2. depth 줄이기여러 depth를 메소드로 분리하여 줄이는 방법보이는 depth를 줄이는 것이 아니라 사고 과정의 depth을 줄여야 한다.무작정 1 depth로 만드는 것이 정답이 아니다. 3. 사용할 변수는 가깝게 선언4. 공백라인복잡한 의미 단의를 공백으로 분리5. 부정어는 자제한다.!연산자는 머리 속에서 한번 더 생각해야 하기 떄문에 복잡해진다.부정할 경우 대표할 수 있는 경우 메소드 이름을 변경하고, 대표할 수 없다면 메소드 명에 부정어구를 추가한다.6. 해피 케이스와 예외처리예외값의 유효성을 체크할 경우 외부 세계에서 값이 들어올 경우 접점에서 검사한다.사용자 입력, 객체 생성자, 외부 서버등내가 처리하지 않은 예외는 추후 분석이 가능하도록 콜스택을 출력해둔다.널을 대하는 자세변수에 null이 들어오지 않도록 하는 것이 가장 좋다.return null은 사용하지 않는다. 개발자는 메소드가 null을 리턴하지 않을 경우를 가정하고 메소드를 사용한다.optional을 사용하지 말하야하는 이유호출 비용이 크다.포함한 값의 널값 여부와 자체가 널인지도 확인해야 한다.optional을 사용할 때는 아래 메소드를 최대한 사용한다.orElseGet(), orElseThrow(), ifPresent(), ifPresentOrElse()

seonman.kim

인프런 워밍업클럽 1일차

https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95이 글은 위의 강의중 제가 중요하다고 생각한 부분과, 저만의 의견을 덧붙여서 정리해보았습니다.정확한 내용과 더 많은 내용을 듣고싶다면 위의 강의를 수강하여 들으시는 것을 제안 드립니다. 시작하며..지금까지 회사를 다니면서 간단한 CRUD 작업만 하다보니 스프링 프레임워크에서 제공해주는 기본적인 구조로만 작업을 해왔다. 나름 객체지향, 클린코드를 지향하며 코드를 작성했다고 생각했는데 객체지향은 개뿔.. 아무것도 사용하지 않았더라.. 물론 코드는 간단했고, 가독성도 있어보였고, 가독성도 좋더라.왜냐하면 로직 자체가 간단했으니까.. 그래서 이번 기회에 인프런 워밍업 클럭 밴엔드 과정을 진행하면서 매일마다 간단 후기를 작성하고자 한다. 읽기 좋은 코드코딩 작업의 대부분은 코드를 작성하는 시간보다 코드를 읽는 시간이 압도적으로 많다.내가 작성한 코드는 작성하는 순간 레거시 코드가 된다. 다른 개발자가 볼 수 있지만, 내가 될 수도 있수도 있고..!! (내가 작성한 코드는 내가 가장 많이 본다 ㅋㅋㅋㅋ)읽기 좋은 코드는 유지보수 하기 편하고, 내가 업무하기 편해지고, 월급이 올라간다!! 프로그램의 정의프로그램 : 데이터 + 코드잘 추상화된 데이터는 코드로 다루기가 쉬워진다. 추상추상 : 사물을 정확하게 이해하기 위해서 사물이 지니고 있는 여러가지 측면 가운데서 특정한 측면만을 가려내어 포착하는 것 (위키)즉, 중요한 정보는 남기고, 중요하지 않은 정보는 생략하는 것추상의 반대는 구체이다.구체 -> 추상 : 추상화, 추상 -> 코드 : 구체화타입도 데이터의 추상화이다. 이진수 -> 바이트 -> 문자, 숫자 -> 구조체 -> 객체기계어 -> 프로그래밍 언어하드웨어 -> 운영체제 -> 애플리케이션OSI 7계층변수의 이름을 짓는 행위메소드의 추상화 추상화 레벨하나의 영역 안에서 추상화 레벨은 동등해야 한다. 같은 영역(메소드, 블럭 등) 안에서 추상화 레벨이 동일해야 가독성이 좋아진다.

백엔드

채널톡 아이콘