🚨깜짝 연장🚨 30% 마지막 할인받기

블로그

[인프런 워밍업 클럽 3기 - BE 클린 코드, 테스트 코드] 1주차 발자국 👣

1주차 발자국 👣 💻 강의 수강👩🏻‍💻 학습 내용 요약잘 읽히는, 이해할 수 있는 코드를 작성하자.추상화는 구체적인 정보들로터 중요한 정보들만 남기는 것이다. 추상화에서 도메인의 문맥이 중요하다.이름을 짓는 행위도 추상화의 일종으로, 단어만으로 읽는 이에게 정보를 쉽게 전달할 수 있어야 한다.메서드의 주제는 반드시 하나여야 한다. 포괄적인 의미를 담아야 하며, 작은 단위의 메서드로 쪼갤 수 있다.상수와 줄 바꿈으로도 가독성을 높일 수 있다.코드를 읽는 이가 너무 많이 고민하지 않게, 기억해야 하는 정보가 많지 않게 읽을 수 있게 하자.Early return, 사고 과정의 depth 줄이기, 부정어 대체하기 등예외가 발생할 가능성을 낮추자. 의도한 예외와 예상하지 못한 예외를 구분하자.Java에서 null로 인한 예외를 주의하자. Optional 사용 고민해 보자.  🤔 학습 내용에 대한 회고강의를 듣고 이해하며 미션을 수행하는데 시간이 빠듯했다.하나하나 기록하며 강의를 듣는 편인데 이러한 방식은 시간이 오래 걸린다. 어떻게 해야 적은 시간 내에 강의 수강 및 이해, 미션 수행이 가능할 지 학습 방법에 대해서 고민해봐야겠다.강의가 너무 재밌다!!!💙 🎯 다음 주 학습 목표효율적인 학습 방법을 알아내서 적용해보자!!  ✉ 미션💭 미션 해결 과정Day 2 미션 우리 가족 귀염둥이와 산책하는 것에 대해서 구체화해보았다. Day 4 미션1번 내용)강의에서 언급한 논리, 사고의 흐름에 따라서 읽기 좋은 코드가 되도록 리팩토링하였다.2번 내용)SOLID 원칙에 대해서 나만의 언어로 작성하였다. 🤔 미션 해결에 대한 간단한 회고미션을 잘 한 건지 모르겠다.. 피드백을 받고 싶다...  📚 출처[강의] Readable Code: 읽기 좋은 코드를 작성하는 사고법 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/dashboard 

백엔드워밍업클럽백엔드clean-code객체지향리팩토링

[인프런 워밍업 스터디] 추상과 객체 지향의 구체화

인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.📌 1주차 기간 강의섹션 2. 추상코드를 잘 짠다는 것은?개발자라면 코드를 잘 짜기 위해 노력한다. ‘코드를 잘 짠다’라는 것은 무엇일까?코드를 잘 짠다는 것은 이해하기 쉬운 코드, 즉 ‘읽기 좋은 코드’라는 것에 동의하지 않는 사람은 없을 것이다.내가 짠 코드를 읽는 대상은 결국 나와 동료이다. 미래의 나와 동료를 위해 매 순간 읽기 좋은 코드를 작성하려고 노력해보자.클린 코드와 리팩토링의 가장 좋은 예시는 테스트 코드 생성 사이클로 볼 수 있다.리팩토링 대상/범위 확인기능 보장을 위한 테스트 코드리팩토링 & 테스트 코드로 검증클린 코드를 추구하는 이유그런데 클린 코드를 추구하는 이유는 뭘까?코드가 잘 읽힌다 ⇒ 이해가 잘 된다 ⇒ 유지보수하기 수월하다 ⇒ 시간과 자원이 절약된다!즉, 클린 코드는 우리의 시간과 자원을 절약해준다.클린 코드, 그리고 추상과 구체추상은 클린 코드를 관통하는 주제이다. 그렇다면 추상이란 뭘까?추상 : 중요한 정보는 가려내어 남기고, 덜 중요한 정보는 생략하여 버린다.추상의 반대편은 구체라고 볼 수 있다. 하나의 예시를 들어보자.질문A랑 주말에 뭐 했어?답변답변 1 : 식당에 예약해서 갔다왔어.답변 2 : 케치테이블을 통해 가고자 하는 식당에 들어가서 가고자하는 날짜와 시간을 선택하고 그때 식당에 들어가서 밥을 먹고왔어.답변 1과 답변 2는 같은 의미를 나타낸다. 그런데 표현은 다르다. 답변 2는 구체적인 사실이고 답변 1은 추상화된 문장이라고 할 수 있다.구체에서 추상으로 갈수록 정보는 함축되어 제거되고, 추상에서 구체로 갈수록 생략된 정보를 유추하고 재현한다.이를 개발자답게 컴퓨터 과학에 적용해보자.int는 4 byte이고, char는 1 byte이다. 같은 byte로 이루어지지만, 데이터를 어떻게 읽는지에 따라 달라진다. 즉, 데이터 개념에도 추상화가 존재하는 것이다!잠깐 프로그램의 정의에 대해 생각해보자. 프로그램은 다음과 같이 말할 수 있다.프로그램 = 데이터 + 코드위의 예시를 통해 데이터 개념에 추상화가 존재함을 알 수 있었다. 당연히 코드에도 추상화가 존재한다. 또한 데이터 + 코드에도 추상화가 존재한다.흔히 고수준/저수준 언어 라는 용어를 들어봤을 것이다. 고수준과 저수준이 나뉘는 이유는 추상화에 대한 수준을 나타내기 때문이다. 잊고 있을 수 있지만, 컴퓨터는 0과 1밖에 모른다!적절한 추상화는 복잡한 데이터와 복잡한 로직을 단순화하여 이해하기 쉽게 한다!만약 어느 도시에서 예약을 선택이라고 한다고 가정해보자. 그렇다면 답변 1은 다음과 같이 바뀔 것이다.식당을 선택해서 갔다왔어.이 말을 들으면 무슨 말인가 싶다. 말을 통해 유추하거나 재현하기 쉽지 않다. 단순히 말이 안된다고 생각할 수 있지만 이유에 대해 따져보자.추상화 과정에서 중요한 정보를 남기지 않았다.식당을 방문 날짜를 미리 정하는 예약이 아니라, 단순히 선택했다는 정보만 남김상대적으로 덜 중요한 정보를 남기고 중요한 정보를 삭제해석자가 동일하게 공유하는 Context가 없다.중요한 정보의 기준이나 도메인 영역 별 추상화 기준은 다를 수 있음해당 도시에 살았으면 이해 가능잘못된 추상화가 야기하는 사이드 이펙트는 상당히 크다!적절한 추상화란 해당 도메인의 문맥 안에서 정말 중요한 핵심 개념만 남겨서 표현하는 것이다.이름 짓기이름을 짓는다는 행위는, 추상적 사고를 기반으로 한다.단수와 복수 구분하기이름 줄이지 않기은어/방언 사용하지 않기좋은 코드를 보고 습득하기좋은 이름을 짓기 위해 노력하자!메서드 추상화메서드 이름으로 구체적인 내용을 추상화할 수 있어야 한다.서점에서 책을 샀다.서점에 가서 책을 고른다. 책을 계산대에 가져가 직원에게 건네주고 책 가격 금액을 전달하고 책을 얻었다.잘 쓰여진 코드라면, 한 메서드의 주제는 반드시 하나다!그런데 내용이 다음과 같다고 해보자.서점에서 책을 샀다.돈을 인출하고 가는길에 아이스크림을 사먹고, 책을 구매했다.추상화된 내용을 보고 구체적인 내용의 유추가 어렵다. 의미를 담을 수 있는 더 작은 단위로 쪼개야 한다.현금 인출아이스크림 사먹기서점에서 책 구입하기메서드 작성메서드 선언부반환타입메서드명파라미터메서드명추상화된 구체를 유추할 수 있는, 적절한 의미가 담긴 이름파라미터와 연결지어 더 풍부한 의미를 전달할 수도 있다.파라미터파라미터의 타입, 개수, 순서를 통해 의미를 전달파라미터는 외부 세계와 소통하는 창어떤 재료가 필요한지 알려주는 역할이다. 외부 세계한테 필요한 재료를 요구한다.반환타입메서드 시그니처에 납득이 가는, 적절한 타입의 반환값 돌려주기반환 타입이 boolean인데, 이게 이 메서드에서 무엇을 의미하는거지?void 대신 충분히 반환할 만한 값이 있는지 고민해보기반환값이 있다면 테스트도 용이해진다.코드의 줄 수가 많아서 추상화하는 것이 아니다! 같은 라인 수를 가지더라도 추상화할 수 있다.추상화 레벨메서드를 추출한다 ⇒ 외부 세계와 내부 세계를 나누고, 추상화 레벨이 나뉜다.하나의 세계 안에서는, 추상화 레벨이 동등해야 한다!게임 시작 멘트 출력게임 초기화게임 보드 보여주기게임 상태가 1이면게임 종료 메시지 출력1, 2, 3, 4를 쭉 읽다보면 4에서 멈칫하게 된다. 게임 상태 = 1이라는 것의 의미에 대해서 의문이 생기게 된다. 즉, 읽는 사람이 해석을 하게 만든다. 따라서 동등한 추상화 레벨을 맞춰야 한다.게임 시작 멘트 출력게임 초기화게임 보드 보여주기게임을 이겼다면,게임 종료 메시지 출력전보다 훨씬 읽기 수월하다.메서드로 추출한다는 것은 로직이 복잡하거나 의미를 부여할 수 있어서도 맞지만, 추상화 레벨을 동등하게 맞춤으로써 읽는 사람으로 하여금 자연스럽게 이해할 수 있게 한다.매직 넘버, 매직 스트링의미를 갖고 있으나, 상수로 추출되지 않은 숫자, 문자열 등상수 추출로 이름을 짓고 의미를 부여함으로써 가독성, 유지보수성 증가섹션 3. 논리, 사고의 흐름인지적 경제성코드를 작성할 때 인지적 경제성을 추구하도록 작성해보자!💡 인지적 경제성? - 최소의 인지적 노력으로 최대의 정보 제공 - 한 번에 한 가지 일 즉, 최소한의 인지만 가져가서 최대의 효율을 내보자라는 뜻!코드를 읽는 사람의 메모리를 효과적으로 쓸 수 있도록 해서 읽기 좋은 코드를 작성하자.Early Returnelse if ⇒ 이전의 if에 대해서 생각한다.else ⇒ 앞의 조건들을 모두 알아야 안다.즉, 앞 선 정보들을 모두 기억하고 있어야 함!Early Return을 이용하자!else가 사라짐앞의 코드를 신경 쓸 필요가 없다.사고의 depth 줄이기중첩 분기문, 중첩 반복문2중 for문 내에 if 문이 있다면?3 depth ⇒ 사고의 depth도 많을 것!메서드 추출을 통해 중첩 분기문/반복문을 없애자! 그렇다고 무조건 1 depth로 만들라는 것은 아니다.추상화를 통한 사고 과정의 depth를 줄이는 것이 중요2중 중첩 구조로 표현하는 것이 사고하는 데 더 도움이 된다면 그대로 놔두자.사용할 변수는 가깝게 선언하기사용할 변수는 가깝게 선언하기사용할 변수가 너무 멀리있다면 되돌아가도록 유도한다.공백 라인도 의미를 가진다!복잡한 로직의 의미 단위를 나누어 보여줌으로써 읽는 사람에게 추가적인 정보를 전달할 수 있다.우리나라 글도 문단이 있지 않은가!부정어를 대하는 자세!가 있으면 조건을 먼저 이해하고, 이에 반대되는 조건을 생각하게 된다.가독성이 떨어지고 비틀어서 생각하게 함읽으면서 바로 생각할 수 있게 조건을 작성하자.해피 케이스와 예외 처리예외가 발생할 가능성 낮추기검증이 필요한 부분 = 외부 세계와의 접점의도한 예외와 예상하지 못한 예외 구분사용자에게 보여줄 예외개발자가 보고 처리해야 할 예외항상 NPE가 일어나지 않는지 고려하자.메서드 설계 시 return null 자제Optional은 비싼 객체이고 반환 타입에 사용한다.orElse, orElseGet, orElseThrowe.printStackTrace는 실무에서는 안티패턴이다!섹션 4. 객체 지향 패러다임추상의 관점으로 바라보는 객체 지향절차 지향정해진 순서 차례대로 처리하는 프로그래밍객체 지향객체를 만들어서 객체들 간의 협력을 통해 이루어지는 프로그래밍함수형순수 함수를 정의Side Effect가 없는 함수A ⇒ 항상 A에 대한 정해진 결과가 나오는 것객체란?추상화된 데이터 + 코드객체간의 협력과 객체가 담당하는 책임객체 지향의 특징 ⇒ 캡슐화, 추상화, 상속, 다형성정말 이를 이해하고 적용하고 있는지 스스로 질문해보자!코드 레벨에서 잘 녹여서 쓸 수 있어야 한다.관심사의 분리유지보수가 원활해짐높은 응집도와 낮은 결합도같은 관심사는 응집도가 높아야 한다.각 관심사끼리는 결합도가 낮아야 한다.객체 설계객체로 추상화는..비공개 필드(데이터), 비공개 로직(코드)공개 메서드 선언부를 통해 외부 세계와 소통객체의 책임이 나뉨에 따라 객체 간 협력 발생!객체를 통해절차 지향에서 보이지 않았던 개념 가시화관심사가 한 군데로 모이기 때문에 유지보수성 증가객체를 사용하는 입장에서 보다 높은 추상화 레벨에서 도메인 로직을 다룰 수 있다!!주의사항1개의 관심사로 책임이 분리되었는지 스스로 질문해보자.객체 생성 시, 유효성 검증이 가능setter는 지양, 필요할 경우 메서드명에 의도를 드러내자.getter는 필요한 경우에만.필드 수는 적을 수록 좋다.불필요한 데이터를 굳이 가지고 있지 말자. ex) 주문의 총 가격도메인 지식은 만드는 것이 아니라 발견하는 것!SRP (단일 책임 원칙)하나의 클래스가 하나의 책임만 갖도록 설계해라!하나의 클래스는 한 가지의 변경 이유만 가진다.객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경 되는지 질문하자.변경이유 = 책임관심사의 분리높은 응집도, 낮은 결합도응집도 = 클래스나 모듈 내 요소들이 서로 긴밀하게 연관되어 있는 정도결합도 = 두 개 이상의 객체가 협력할 때, 한 객체가 변경되었을 때 다른 객체가 영향받는 정도각 객체가 독립적인 책임을 가지도록 잘 쪼개진다.객체를 하나의 변경 지점, 하나의 책임으로만 가지도록 설계하자!책임을 발견하기 어려움경계선이 사람마다, 도메인마다 다를 수 있음설계한 객체가 하나의 책임만 가지고 있는지 끊임없이 질문책임을 보는 눈을 기르자!객체를 설계했을 때 책임이 잘 응집되었는가?, 단일 책임만을 가지고 있는가?OCP (개방-폐쇄 원칙)확장에는 열려 있고, 수정에는 닫혀 있어야 한다.기존 코드 변경 없이 기능 확장이 가능해야 함!추상화와 다형성을 활용하여 OCP를 지키자.새로운 요구사항이 추가되었을 때, 기존 코드가 과도하게 변화된다?OCP를 지키지 못하고 있는게 아닌지 생각하자.LSP (리스코프 치환 원칙)상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.자식 클래스는 부모 클래스의 책임을 준수하고 행동을 변경하지 않아야 한다.부모가 일하는 곳에 자식이 가더라도 문제가 없어야 한다!LSP 위반상속 클래스를 사용할 때 오동작 발생ISP (인터페이스 분리 원칙)클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.기능 단위로 인터페이스를 잘게 쪼개라!내가 구현하려는 인터페이스의 기능 명세에 사용하지 않는 기능이 있다.자신이 사용하지 않는 인터페이스에 의존하게 됨불필요한 의존성으로 인해 결합도가 높아진다.특정 기능의 변경이 여러 클래스에 영향을 미친다.DIP (의존성 역전 원칙)상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다.둘 모두 추상화에 의존해야 한다.의존성의 순방향 : 고수준 모듈이 저수준 모듈을 참조하는 것의존성의 역방향 : 고수준, 저수준 모듈이 모두 추상화에 의존하는 것저수준 모듈이 변경되어도, 고수준 모듈에는 영향이 가지 않는다.기능을 추상화하여 고수준 모듈은 추상화된 스펙만 참조추상화를 중간에 두고 의존하도록 하자!저수준 모듈이 자유롭게 변경되도 고수준 모듈에는 영향 X💡스프링에서의 DI, IoC DI = Dependency Injection - 필요한 의존성을 직접 생성하는 것이 아니라, 외부에서 주입받는다. - 제 3자(스프링 컨텍스트)가 런타임 시점에 의존성을 주입해준다. IoC = Inversion of Control - 프로그램의 흐름을 프레임워크가 담당 - 빈 생성, 의존성 주입, 생명주기 관리를 스프링 컨테이너가 해준다. 섹션 5. 객체 지향 적용하기상속과 조합상속보다 조합을 사용하자.상속은 시멘트처럼 굳어지는 구조수정이 어렵다.부모와 자식의 결합도가 높다.자식이 부모에 대해서 다 알고 있어야 함부모 수정 ⇒ 자식이 다 영향을 받는다.자식은 부모에 있는 필드를 직접적으로 알고 있음조합과 인터페이스를 활용하는 것이 유연한 구조상속을 통한 코드의 중복 제거가 주는 이점보다, 중복이 생기더라도 유연한 구조 설계가 주는 이점이 더 크다.Value Object도메인의 어떤 개념을 추상화하여 표현한 값 객체값으로 취급하기 위해 불변성 동등성 유효성 검증 등을 보장해야 한다.불변성 : final 필드, setter 금지동등성 : 다른 인스턴스여도 내부 값이 같으면 같은 값 객체로 취급유효성 검증 : 객체가 생성되는 시점에 값에 대한 유효성 보장VO vs EntityEntity식별자 O식별자가 아닌 필드의 값이 달라도, 식별자가 같으면 동등한 객체로 취급!equals & hashcode도 식별자 필드를 통해 재정의VO식별자 X내부의 모든 값이 다 같아야 동등한 객체일급 컬렉션일급 시민다른 요소에게 사용 가능한 모든 연산을 지원하는 요소변수로 할당될 수 있다.파라미터로 전달될 수 있다.함수의 결과로 반환될 수 있다.일급 함수함수형 프로그래밍에서 함수는 일급 시민함수는 변수에 할당될 수 있고, 인자로 전달될 수 있고, 함수의 결과로 함수가 반환될 수도 있다.일급 컬렉션컬렉션을 포장하면서, 컬렉션만을 유일하게 필드로 가지는 객체컬렉션을 다른 객체와 동등한 레벨로 다루기 위함단 하나의 컬렉션 필드만을 가진다.컬렉션을 추상화하며 의미를 담고, 가공 로직의 보금자리가 생긴다.가공 로직에 대한 테스트도 작성 가능getter로 컬렉션을 반환할 일이 생긴다면, 외부 조작을 피하기 위해 새로운 컬렉션을 만들어 반환해야 한다.Enum의 특성과 활용enum은 상수의 집합상수와 관련된 로직을 담을 수 있음특정 도메인 개념에 대해 종류와 기능 명시변경이 잦은 개념은 DB로 하는게 나을 수도 있다.enum은 코드 ⇒ 배포를 해야만 변경할 수 있다.변경이 잦은 요소라면 DB로 관리하자.숨겨져 있는 도메인 개념 도출하기도메인 지식은 만드는 것이 아니라 발견하는 것객체 지향은 현실을 100% 반영하는 도구가 아니라, 흉내내는 것현실 세계에서 쉽게 인지하지 못하는 개념도 도출해서 사용해야 할 때가 있다.설계할 때는 근시적, 거시적 관점에서 최대한 미래를 예측하고, 시간이 지나 틀렸다는 것을 인지하면 언제든 ㄷ돌아올 수 있도록 코드를 만들어야 한다.완벽한 설계 X, 그 당시의 최선이 있을 뿐 📌 1주차 수강 후 느낀점강의 제목들을 보면, 사실 개발자라면 다 한번씩 들어봤을 내용이다. SOLID 원칙, 추상화, VO, Enum 등등 누군가 질문하면 그럴듯한 답변을 할 수 있는 주제다. 그런데 ‘이 내용들을 깊이 있게 이해하고 있을까?’ 라는 질문에는 아니었던 것 같다.이러한 개념들을 직접 코드 레벨에 적용하며 개선하는 과정에서 배울 점이 많았다. 내가 알고 있던 깊이는 상당히 얄팍했다. 가끔 헷갈리면 검색해서 ‘아 대충 이런내용이었지.’하고 금방 닫곤 했는데, 이번 기회에 직접 경험해보며 내 지식들을 구체화시킬 수 있었다. 개발자가 가볍게 여기고 넘어갈 수 있는 주제들에 대해 다시 생각하며, 기본기의 중요성을 알 수 있었다! 그리고, 기존에 프로젝트하며 고민했던 지점이 있었다. 추상화를 할 때 ‘같은 라인 수를 가져도 굳이 해아하나?’ 생각했었는데, 나한테 해답이 되는 1주차였던 것 같다!! 전부터 ‘한 번 들어야지!’했던 강의인데 워밍업 스터디를 계기로 수강하길 잘한 것 같다🙂 📌 1주차 미션Day 2 : 추상과 구체 예시 생각해보기뭔가 추상화를 실생활에 생각해본 적은 없었는데, 느낌이 새로웠다. 되게 개발스러운 단어라고만 생각했는데 추상은 우리의 삶에도 가까이 적용되는 단어라는 것을 느꼈다.농구에서 슛을 한다고 해보자. 이때 무릎을 구부리고, 팔꿈치를 올리고, 무릎을 다시 피고, 팔꿈치를 다시 피고, 손목을 피며 공을 던진다. 위 여러 과정들이 ‘슛’으로 추상화된다. 우리 삶의 추상화는 상당히 자주 쓰인다!Day 4 : [섹션 3. 논리, 사고의 흐름] 내용을 중심으로 리팩토링하기이전에는 코드를 리팩토링하면, 되게 느낌적으로 다가갔던 것 같다. ‘뭔가 이게 나은 것 같긴한데..’하며 접근하거나 단순히 코드 라인을 줄이는 등 이유와 근거가 빈약했다. 하지만 Day 4 미션을 통해 리팩토링의 이유와 근거를 통해 리팩토링을 진행해보았다.필요한 정보를 빼내 추상화부정어구 최소화케이스 확인위 과정을 통해 미션에서 주어진 코드를 읽기 좋게 개선하였다. 읽으면서 사고의 흐름이 멈칫하거나 꼬일 수 있던 코드를 바로 읽어나갈 수 있게 작성하려고 노력했다.강의를 들으면서 내가 납득됐던 내용에 대해 직접 리팩토링에 적용하여 ‘읽기 좋은 코드’를 만들어 나갈 수 있었다. 단순히 불필요한 작업을 없애는 것만이 리팩토링이 아니라, ‘읽기 좋은 코드’를 만드는 것도 중요한 리팩토링이라는 것을 깨달았다!

백엔드추상객체지향

스프링 핵심 원리 - 기본편 / 객체 지향 설계 / DI와 정적, 동적 의존 관계

스프링 핵심 원리의 기본편을 수강중입니다. 이론에 해당되는 내용 중 일부를 재해석해서 정리해보았습니다.용어 정리의존 관계이 글에서는 일단 "A가 B에 의존한다"를 "B가 변경될 경우 A가 변경되어야 한다"라고 정의하겠습니다.예를 들어 어떤 프로젝트 내에서 데이터 저장소를 ARepository에서 BRepository로 변경할 때 CService의 코드 내에 해당 부분을 변경해야 할 경우, "CService 클래스 구현 코드는 ARepository 클래스에 의존한다"고 표현하겠습니다. 정적 / 동적이 글에서만 관심 있는 영역의 코드가 컴파일되기 전에 이미 고정된 것을 정적인 것으로 정의하고, 코드 상으로는 고정되어 있지 않고, 컴파일 후 실행될 때에야 정해지는 것을 동적인 것으로 정의하겠습니다.다른 글들을 보았을 때 맥락에 따라 "컴파일" 대신 다른 단어가 들어오는 경우도 있는 것 같습니다.OCP (개방-폐쇄 원칙)"코드는 기능 확장에는 열려 있으면서, 수정에는 닫혀 있어야 한다."는 원칙입니다.다시 말해 코드 자체를 수정하지 않고도 기능을 확장할 수 있어야 한다는 원칙입니다. SRP (단일 책임 원칙)모듈은 한 가지 책임만 지녀야 한다는 원칙입니다.여기서 책임이란 모듈의 변화에 대한 이유입니다.이 글의 맥락에서는 지엽적인 내용이지만, 솔직히 말씀드리면 위 정의를 보긴 했지만 적용이 가능할 정도로 명확히 알지 못해 다음 자료들을 참고하였습니다. 제가 일단 이해한 내용을 공유드리겠습니다.일단 이해한 내용소프트웨어를 개발할 때 사용하는 자료 구조나 알고리즘의 종류와 같이 변화가 예상되는 부분들이 있다. 소프트웨어를 모듈로 나타낼 때, 실행되는 순서로 모듈을 구성하기보다는 변화하는 부분들을 다른 모듈들로부터 격리시키는 방식으로 모듈화를 한다면 특정 요소가 변하더라도 그 변화에 대한 한 모듈만 수정할 수 있다.비즈니스 소프트웨어에서는 변화를 요구하는 대상이 특정 비즈니스 로직과 관련된 특정 부서이기 때문에, 모듈을 설계할 때 그 모듈에게 변화를 요청하는 대상이 한 부서가 되도록 구성하는 것이 이상적이다.참고 자료https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.htmlhttps://en.wikipedia.org/wiki/Single-responsibility_principlehttps://dl.acm.org/doi/pdf/10.1145/361598.361623https://www.sicpers.info/2023/10/ive-vastly-misunderstood-the-single-responsibility-principle/DI (의존성 주입)해결하고자 하는 문제어떤 기능을 실행 시에 다양한 구현체들을 사용하는 방식으로 확장하고 싶습니다.예시: 데이터 저장을 위한 Repository를 운영 시에는 DatabaseRepository를 사용해서, 개발 시에는 InMemoryRepository를 사용하여 실행하고 싶습니다.그러나 Repository 구현체를 변경하려고 보니 Repository를 사용하는 모든 곳에서 구현체를 수정해야 하는 문제가 발생했습니다.예시 class Service { private final Repository repository = new InMemoryRepository(); // 코드 수정 필요 // Repository 사용 로직 ... }DI를 사용한 해결 방안Repository를 사용하는 클래스들이 DatabaseRepository와 같은 구체적인 구현체들을 사용하지 않게 지워버린 후 Repository 인스턴스를 외부에서 주입받도록 합니다. 여기서는 생성자를 통해 주입받았습니다.예시 class Service { private final Repository repository; public Service(Repository repository) { this.repository = repository; } // Repository 사용 로직 ... }Config 클래스를 따로 두어 인스턴스를 생성하는 코드들을 모두 이 클래스에 둡니다. 인스턴스가 필요한 경우 이 클래스에서 사용하면 됩니다. 예시class Config { repository() { return new InMemoryRepository(); } service() { return new Service(repository()); } }OCP 관점에서의 해석원래 문제에서는 실행 시점에 다른 구현체를 사용하는 기능 확장을 하고 싶었지만, 이를 위해 구현체를 사용하는 코드를 변경해야 했습니다. 따라서 OCP를 위반하는 설계였습니다. 이 때 OCP의 각 부분을 이 맥락에서 다음과 같이 해석할 수 있습니다.기능 확장의 대상은 실행 시점에 실행되는 코드로, 이는 Repository 클래스의 인스턴스입니다.반면 소스 코드 수정과 관련된 부분은 컴파일 시점에 결정되는 Service 클래스의 코드 자체입니다.따라서 OCP를 만족하기 위해서는 "Service 인스턴스의 실행 시점에는 서로 다른 Repository 구현체 인스턴스를 사용하되, 컴파일 시점에 같은 Service클래스 코드를 사용하고 싶다"는 문제를 해결해야 합니다.다시 말하면 각 Service 인스턴스는 서로 다른 Repository 구현체 인스턴스에 의존하게 하되, Service 클래스는 구체적인 Repository 구현체 클래스에 의존하지 않도록 해야 합니다.그래서 DI를 사용해서 Service와 Repository 간의 동적인 의존관계와 정적인 의존관계를 서로 다르게 만듦으로써 OCP를 만족하는 설계를 구성하였다고 해석할 수 있습니다. SRP 관점에서의 해석기존의 Service 코드에서는 Repository의 인스턴스를 생성하는 책임과 Repository를 사용하는 책임이 같이 있었습니다. 따라서 SRP를 위반하는 설계였습니다.이 때 Repository를 사용하는 로직은 Repository 인터페이스만 알고 구현체 클래스를 몰라도 문제가 없습니다.반면 Repository 인스턴스를 생성하려면 구체적인 Repository 구현체 클래스의 정보를 알아야 합니다.여기서 Repository 구현체를 변경하면, 원래는 구현체를 몰라도 되는 Repository 사용 로직이 인스턴스 생성 로직과 같은 클래스 안에 담겨 있어 수정의 대상이 되었습니다.DI를 사용하여 수정된 설계에서는 객체들의 인스턴스를 생성하는 책임을 Config 클래스에 몰아줌으로써 Repository 구현체를 변경할 때에도 Service 코드는 영향을 받지 않고 Config 클래스만 수정하면 되도록 바뀌었습니다.

개발 · 프로그래밍 기타객체지향SOLIDDI

윤대

[인프런 워밍업 0기 Day5] 한 걸음 더! 객체 지향으로 클린코딩!

!! 해당 글은 독자가 인프런 워밍업 0기를 수강하고 있다는 전제 하에 작성되었습니다 !!과제 수행에 있어 스프링부트 3.2.2 버전을 사용하고 있다는 점을 미리 알려드립니다!안녕하세요🙌! 인프런 워밍업 5일차 과제입니다!이번에는 클린코드의 중요성에 대하여 학습하고 클린코드를 작성하는 방법에 대해 배웠습니다!😎저는 이번 과제를 그동안 책으로만 공부했던 객체 지향을 적용하여 해결해보고자 했습니다.이론으로만 공부했기 때문에 많이 서툴 수 있다는 점! 그렇기에, 저의 말이 정답이 아니라는 점을 미리 말씀 드리며!지금부터 객체 지향을 향한 저의 여정을 소개하겠습니다! 🤸‍♂️💡과제 살펴보기아래는 과제로 주어진 지저분한 코드입니다! 바라보기만 해도 머리가 어지러운데요.. 😥public class Main { public static void main(String[] args) throws Exception { System.out.print("숫자를 입력하세요 : "); Scanner scanner = new Scanner(System.in); int a = scanner.nextInt(); int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0; for (int i= 0; i < a; i++) { double b = Math.random() * 6; if (b >= 0 && b < 1) { r1++; } else if (b >= 1 && b < 2) { r2++; } else if (b >= 2 && b < 3) { r3++; } else if (b >= 3 && b < 4) { r4++; } else if (b >= 4 && b < 5) { r5++; } else if (b >= 5 && b < 6) { r6++; } } System.out.printf("1은 d%번 나왔습니다.\n", r1); System.out.printf("2는 d%번 나왔습니다.\n", r2); System.out.printf("3은 d%번 나왔습니다.\n", r3); System.out.printf("4는 d%번 나왔습니다.\n", r4); System.out.printf("5는 d%번 나왔습니다.\n", r5); System.out.printf("6은 d%번 나왔습니다.\n", r6); } }위 코드는 결국 다음과 같은 동작을 수행합니다!주어지는 숫자를 하나 받는다.해당 숫자만큼 주사위를 던져, 각 숫자가 몇 번 나왔는지 알려준다.위 코드처럼 로직을 열거하여 프로그래밍 하는 방식을 절차 지향 프로그래밍이라 말하며, 저는 이 코드를 클린코드로 수정해야 합니다!위의 코드는 클린 코딩의 중요성을 위한 극단적인 예시일 뿐, 절차 지향이라 지저분한 것이 아닙니다!지나친 추상화는 오히려 코드의 가독성을 떨어트릴 수 있으니 무조건 '객체 지향이 좋다!'의 글이 아니라는 점 알아주세요!저는 위의 코드를 깔끔하게 바꾸기 위해서 아래와 같이 객체 지향의 형태로 수정하여 과제를 완수했습니다!public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); Dice luckyDice = UnknownDice.decideFaces(6); Note memoPad = new MemoPad(); Dealer dealer = Dealer.playWith(luckyDice, memoPad); System.out.print("숫자를 입력하세요 : "); dealer.rollDiceMultipleTimes(scanner.nextInt()); dealer.tellTheResult(); } }어떤가요? 내부 로직이 어떻게 되는지는 모르겠지만, 메소드명만을 보고도 원래의 코드와 똑같은 동작을 수행할 것이 기대할 수 있습니다!그렇다면, 내부는 어떻게 구현됐을까요? 내부 구현을 함께 살펴보기 전에 객체 지향이 무엇인지 짧게 설명 드리고 가겠습니다! 💡객체 지향 프로그래밍이란?객체 지향은 '프로그램이'라는 거대한 로직을 '객체'라는 작은 역할로 나누고, 그 '객체'들끼리 상호 협력하여 데이터를 처리하는 방식을 말합니다!어젯밤에 저는 오렌지를 하나 먹었는데요! 이 오렌지가 집에 오기 까지의 과정을 '프로그램'이라고 보겠습니다.그리고, 지금부터 오렌지의 여정을 절차 지향적으로 설명해보겠습니다! 😎먼저 오렌지 나무를 볕 좋은 곳을 찾아서 심고, 거름도 포대를 찢어서 뿌리 주변에 뿌려주고, 해충도 유기농을 위해 핀셋으로 잡아주고.. 설명이 끝도 없이 길어집니다!그렇다면 이번엔 오렌지의 여정을 객체 지향적으로 설명해볼까요?오렌지를 농부가 '재배'하고, 운송 회사가 '운송'하고, 마트에서 '판매'되어 저의 집까지 왔습니다!어떤가요? 훨씬 설명이 쉽고 이해하기 좋지 않은가요? 오렌지가 어떻게 재배되었는지, 운송되었는지, 판매되었는지 우리는 구체적으로 알 필요가 없습니다! 궁금하지도 않고요!이렇게 내부 구현을 숨기고, 역할 만을 외부에 공개하여 코드의 가독성을 올리고 협업을 용이하게 하는 것이 객체 지향의 장점입니다!이는 개체 지향의 단편적인 장점입니다! 더 많은 장점이 있지만, 지금은 클린코딩과 관련하여 추상화와 캡슐화로 인한 장점 만을 이야기하고 넘어가겠습니다! 😭😭 💡도메인 선정하기!  자! 이제 다시 과제로 넘어와 보겠습니다~ 위에서 객체 지향에서 중요한 것은 역할과 협력이라고 했습니다!우리는 요구 사항을 잘 읽고 작은 역할을 찾고 그 역할을 수행할 주인공(도메인)을 선정해야 합니다. 😎사용자로부터 숫자를 하나 입력 받는다.해당 숫자만큼 주사위를 던져, 각 숫자가 몇 번 나왔는지 알려준다. -> 핵심 로직!!저는 핵심 로직에서 두 가지 역할을 찾았습니다! 하나는 무작위의 수를 생성하는 것, 다른 하나는 생성된 수를 각각 세는 것입니다.그리고, 발견한 역할을 바탕으로 이를 수행할 두 가지 도메인을 만들었습니다.무작위 수를 생성하는 🎲주사위(Dice)와 이를 기록해주는 📃노트(Note)입니다!그렇다면, 이 둘을 재빠르게 설계해 볼까요?abstract public class Dice { private final int faces; protected Dice(int faces) { this.faces = faces; } protected int getFaces() { return this.faces; } abstract int throwDice(); } public interface Note { void record(Integer number); void printTheResult(); }다양한 경험을 위해, 저는 주사위는 추상 클래스로 노트는 인터페이스로 만들었습니다!Dice의 throwDice()는 주사위를 굴리는 행위를 나타내며 무작위 수를 생성합니다!Note의 record()는 입력되는 수를 기록하고 기록된 수는 printTheResult()를 통해 출력할 예정입니다!이렇게, 추상 클래스와 인터페이스로 만드는 이유는 해당 동작을 수행할 수 있다면 그게 무엇이든 역할을 대체할 수 있게 하기 위함입니다! 숫자를 기록하고 출력할 수 있다면 메모지던, 스케치북이던, 스마트폰이던 상관이 없습니다!이제 이 둘을 구현해보겠습니다!public class UnknownDice extends Dice { private UnknownDice(int faces) { super(faces); } public static UnknownDice decideFaces(int faces) { return new UnknownDice(faces); } @Override public int throwDice() { return (int) (Math.random() * super.getFaces()) + 1; } } import java.util.HashMap; import java.util.Map; public class MemoPad implements Note { private final Map<Integer, Integer> page = new HashMap<>(); @Override public void record(Integer number) { page.put(number, page.getOrDefault(number, 0) + 1); } @Override public void printTheResult() { for(Map.Entry<Integer, Integer> number : page.entrySet()){ System.out.printf("%d은(는) %d번 나왔습니다.\n", number.getKey(), number.getValue()); } } }이렇게, 구현이 끝이 났습니다! 그런데, 이럴 수가! 여전히 문제가 있습니다. 도메인을 구현한 것 만으로는 로직을 수행할 수가 없습니다..! 😥바로, 주사위와 노트를 어떻게 사용할 것인지 맥락(컨텍스트)이 없기 때문입니다! 💡컨텍스트 만들기!주사위는 무작위 수를 생성하고! 노트는 기록을 합니다! 제가 생성한 도메인은 자신의 역할을 잘 수행합니다!그러나 노트는 숫자라면 무엇이든 잘 기록할 수 있습니다! 그게 꼭 주사위의 숫자가 아니어도 상관이 없습니다.그렇기에, 우리는 맥락(컨텍스트)이 필요한 것입니다. 도메인을 연결하여 의미가 있는 역할을 수행하게 하는 것이죠!그렇게 저는 딜러(Dealer)라는 새로운 객체를 만들었습니다! 요청을 받아 주사위를 굴리는 게 게임 같았거든요..!public class Dealer { private final Dice dice; private final Note note; private Dealer(Dice dice, Note note) { this.dice = dice; this.note = note; } public static Dealer playWith(Dice dice, Note note) { return new Dealer(dice, note); } public void rollDiceMultipleTimes(int numberOfRoll) { for (int i=0; i<numberOfRoll; i++) { note.record(dice.throwDice()); } } public void tellTheResult() { note.printTheResult(); } }딜러의 역할은 게임의 진행입니다! 사용자에게 요청 받은 숫자만큼 주사위를 굴려주고! 노트에 결과를 전달하고 기록된 결과를 사용자에게 알려주는 역할을 수행합니다! 😃주사위와 노트는 딜러의 게임 진행이라는 맥락(컨텍스트) 아래에서 자신들의 역할을 수행합니다! 어떤가요? 객체들이 서로 협업 하며 역할을 잘 수행하여 로직을 수행하고 있습니다!또한, 흥미로운 점은 딜러는 주사위와 노트가 자신의 역할만 잘 수행할 수 있다면(인터페이스를 충실히 구현했다면) 얼마든지 다른 주사위나 노트로 바꿀 수 있습니다! 사기를 칠 지도 모르겠군요! 😜 💡정리하며...자, 이제 클린코딩을 수행한 코드를 다시 보겠습니다! 현재의 내부 구현은 모두 알지만, 구현체인 주사위와 노트는 언제든지 바뀔 수 있습니다!그러나, 우리는 주사위와 노트가 자신의 역할만 잘 수행할 수 있다면, 그것이 바뀌어도 상관이 없다는 사실도 알고 있습니다!이것이 객체 지향이 주는 다형성이라는 장점입니다! 글이 너무 길어져서 짧게 설명하는 점 죄송합니다...😭public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); Dice luckyDice = UnknownDice.decideFaces(6); Note memoPad = new MemoPad(); Dealer dealer = Dealer.playWith(luckyDice, memoPad); System.out.print("숫자를 입력하세요 : "); dealer.rollDiceMultipleTimes(scanner.nextInt()); dealer.tellTheResult(); } }객체 지향 정말 매력적이지 않은가요? 책을 통해 이론으로만 배운 객체 지향을 직접 설계부터 구현하며 쓴 저의 긴 기록을 지금까지 읽어주셔서 감사드리며, 객체 지향을 모르시던 분들께 조금이라도 도움이 되었으면 합니다!사실.. SOILD부터 대뜸 외우라고 하면, 어렵습니다 객체 지향..남은 스터디 기간도 다들 즐거운 코딩하시길 바라겠습니다! 🙇‍♂️

백엔드객체지향클린코딩스프링부트인프런워밍업클럽스터디도메인컨텍스트추상화캡슐화

워밍업 클럽 3기 BE 클린코드 (1주차 발자국) - 객체지향에 무릎 꿇지 않기

객체지향에 무릎 꿇지 않기"나는 SOLID원칙에 대해 암기만 했을 뿐, 개발을 하면서 이를 적용하려고 노력해본적 있는가?"박우빈 지식공유자님의 SOLID원칙, 그리고 객체지향 스타일을 적용시켜 리팩토링하는 과정을 보면서 감탄만 나왔을 뿐이다.취준시절부터 취업이후에도 꾸준히 객체지향 언어를 사용하면서, 단 한번도 객체를 객체답게 사용해본적이 없는 것 같다는 약간의 후회..? 안타까움이 밀려왔다. 아직, Day7 의 미션을 시작하진 않고 어떤 내용인지 보기만 했는데, 객체지향,클린코드를 지향하며 바꿔나갈 생각에 막막하기도 하지만, 한편으로는 두근거린다. 배웠던 내용들을 잘 적용시키기 위해 노력해봐야겠다.내가 취준시절, 추상화에대해 정리했던 노션 내용을 뒤져보니 "객체들의 공통점을 추출하여 정의하는 것 (교통수단 - 자동차, 자전거)"라고 정의되어 있었다. 지금 생각해보면 참으로 와닿지 않는다. 하지만 우빈님의 말은 참으로 와닿았다."사물의 특징을 가려내어 포착하는 것"언뜻 보면 비슷하지만, 나에게는 굉장히 다르게 다가왔다.내가 바라보는 추상화는 컴퓨터 세계속에서만 적용되는 느낌이라면, 우빈님이 바라보는 추상화는 마치 살아 숨쉬는 세계속에 적용되는 느낌이랄까?한주간 배웠던 내용은 이런 느낌의 연장선이었다. 섹션 2에서는 추상화를 정의하는 방식에 대해 신선함을 느꼈다면,섹션 3에서는 어떻게 해야 개발자들이 코드에 대해 편하게 이해할 수 있을지에 대해 정리할 수 있었고,섹션 4에서는 SOLID 원칙에 대해 실질적으로 적용하는 과정을 느끼면서 나 또한 SOLID 원칙을 나 스스로 정의해볼 수 있게 되었다. Day2미션은 크게 고민할 거리는 없어서 넘어가고, Day4는 다음과 같이 정의해보았다.섹션 5에서는 객체지향을 적용하는 TIP들에 대해 익혔던 느낌이다.다음주에도 열심히!  

백엔드클린코드객체지향

[학습일기] 디자인 패턴 관련 학습 정리 - 실습 관련

개요스프링 입문 강의의 예제 코드 강의를 보고 따라해보면서 모듈화를 하는 전략에 대해서 고민하고 어느 정도 정해진 루틴을 생각해보았습니다.기본적으로 이 정해진 루틴으로 초안을 작성하고 나서 상황에 맞는 리팩토링하는 방법으로 빠르게 설계 및 구현을 시도해볼 계획입니다. 루틴먼저 구현하고자 하는 기능을 책임으로 나눕니다.책임을 더 작은 책임들로 분할합니다.이 때 책임의 분할에 도움이 되는 전략으로는 다음을 사용하였습니다.책임을 수행하기 위해 필요한 정보에 따라서 분할하였습니다.예를 들면 어떤 정보는 객체를 생성할 때만 필요하고 사용할 때는 필요하지 않을 수 있습니다.이 이유로 저번 블로그 글에서 디자인 패턴을 관심사의 분리 측면에서 관찰하게 되었습니다.같은 맥락에서 사용되고 변하는 정보의 경우 맥락을 객체로 캡슐화하였습니다.다른 맥락에서 사용되는 정보의 경우, 다른 객체에서 정보를 제공할 수 있도록 책임을 분할하거나, 분할이 어려운 경우 고차함수를 이용해서 책임을 따로따로 주입할 수 있도록 하였습니다.지금 생각났는데 일단 구현하고 나서 리팩토링하는 것도 시도해볼 만한 전략인 것 같습니다.예시많이 많이 부끄럽습니다만 해당 커밋에 코멘트 형식으로 사고 과정을 달아놓았습니다.원래 공개할 계획이 없던 리포지토리다 보니 깔끔하지 못한 부분이 있습니다. 죄송합니다ㅠㅠ앞으로는 비공개 리포도 깔끔하게 정리해야겠습니다.

개발 · 프로그래밍 기타객체지향디자인패턴학습일기

고리오영감

[인프런 워밍업 클럽 스터디 2기 백엔드] 2주차 발자국

개요2주차 종료!Readable Code: 읽기 좋은 코드를 작성하는 사고법 을 완강했다.2주차 공부한 내용목요일에 Readable Code 강의를 완강하고, 금요일 부터는 Practical Testing 강의를 수강 시작했다. 배운 것들1. 일급 컬렉션일급 컬렉션이란 걸 처음 알게 되었다.일급 시민과 컬렉션(List, Set, Map)이 합쳐진 개념이다.컬렉션을 가공하는 로직을 캡슐화할 수 있는 장점이 있다.처음 접하는 개념이라 코드로 적용하는 하는 부분에서 어려움을 느꼈다.그렇지만, 잘만 사용하면 엄청 유용한 기법이라는 생각이 든다. 2. supports()이 메서드를 Spring Security 학습하면서 많이 봤었다.그 때는 이게 왜 필요하지 했었는데, 강의를 보면서 궁금증이 해소되었던 것 같다.다형성, 객체지향에 대해서 좀 더 가까워진 느낌? 3. 주석을 어떨 때 써야하는지주석은 웬만하면 사용하지 않는 편이 좋다고 생각했다.하지만, "코드에 히스토리, 개발자의 의도를 전부 녹일 수 없을 때"에는 주석을 써야 한다는 걸 배웠다. 4. 메서드 배치 순서이거 진짜 유용했다.메서드를 어떤 기준으로 배치해야 하는지 고민이 많았었는데,덕분에 기준을 세울 수 있었다. 5. sonarlint정적 코드 분석 도구로, 인텔리제이에서 플러그인 형태로 사용 가능하다.앞으로 Stack 과 Queue 를 쓸 일이 있을 때는 무조건 Deque을 사용할 거다.sonarlint가 Deque을 사용하라고 추천해줬음. 미션이번 주차에는 Day.7 미션이 있었다. Day.7 미션 - 스터디 카페 프로젝트 리팩토링 실습하기강의에서 배운 것을 토대로 실제로 리팩토링하는 실습이었다.추상화 레벨, 일급 컬렉션, DIP, SRP 를 염두에두고 리팩토링 했다.생각보다 쉽지 않았다.스스로 해보고 답지(강의)를 보니 갈 길이 멀어보인다.나의 가장 큰 문제점은 주어진 틀에서만 해결하려고 한다는 점이다.그래서 새로운 객체를 만든다든지, 기존의 if 문들을 다른 구조로 변경하는 것에 두려움을 느끼는 것 같다.문제를 마주할 때 넓게 보는 연습이 필요한 것 같다. 차분히.리팩토링을 더 잘하기 위해서 섹션 8 - 기억하면 좋은 조언들 - 능동적 읽기 강의 내용을 적용하면 좋을 듯 하다. 공백으로 단락 구분하면서 읽기메서드와 객체로 추상화하면서 읽기주석으로 이해한 내용 표기하면서 읽기Day.7 미션을 수행할 때 위의 조언들을 적용했더라면 좀 더 나은 결과물을 얻을 수 있었을 것 같다. 마무리2주 동안 Readable Code 보면서 많이 배웠다.배운 내용들을 체화시키려면 좀 더 노력해야 할 것이다.일단은 3주차부터 테스트 코드 진도를 나가야하니 테스트 코드에 신경을 쓰도록 하고테스트 코드를 빨리 끝낼 수 있다면, Readable Code의 내용을 복습하면 좋겠다. 

백엔드읽기좋은코드클린코드워밍업클럽객체지향추상화

고리오영감

[인프런 워밍업 클럽 스터디 2기 백엔드] 1주차 발자국

개요인프런 워밍업 스터디 클럽 2기 백엔드(읽기 좋은 코드, 테스트 코드) 9/27 금요일 시작되었다.스터디 주제는 박우빈님의 강의인Readable Code: 읽기 좋은 코드를 작성하는 사고법Practical Testing: 실용적인 테스트 가이드이다.1 ~ 2주차에는 읽기 좋은 코드 강의를 수강하고, 3 ~ 4주차에는 테스트 강의를 수강 하게 되는데,이번 글은 1주차, 읽기 좋은 코드 강의를 수강하고 배운 것들을 정리해 보려고 한다. 1주차 공부한 내용발자국 작성을 위해 표로 정리했다. 생각보다 번거로운 작업이었다. 강의 하나 듣고 로그 기록하고...권장 진도표에 따르면, 섹션 5까지 1주차에 수강 하도록 되어 있는데,이번 주는 섹션 4까지가 한계였다. 생각보다 내용이 많았고, 한 번에 이해되지 않는 내용들은 복습해야 했다. 해결한 것 - 인텔리제이 한글 깨짐 이슈처음부터 난관을 겪었다.인텔리제이 한글 깨짐 이슈...강의에서는 지뢰찾기 게임을 콘솔 프로그램으로 구현이 되어 있고,콘솔에 출력되는 내용이 한글, 지뢰 모양, 네모박스 모양, 깃발 모양 등이 있는데,전부 깨져서 출력되었다.구글링을 통해 해볼 수 있는 것들은 다 해봤던 것 같다.그래도 안 되서 포기하려던 찰나, 유튜브에는 관련 내용이 없는지 살펴보았다.결국, 해결! (유튜브 만세!)Project Structure 에서 SDK와 Language level 을 17 버전으로 동일하게 맞춰주니 한글 깨짐 이슈가 해결되었다.기존에는 SDK는 21 버전이 적용되어 있었다.버전이 제대로 맞지 않으면 한글 깨짐 이슈가 일어나는구나를 깨달았다.  이미 알고 있던 것들1. 이름 짓기 이름을 잘 지어야 한 다는 것은 알고 있었다. 어려워서 그렇지...2. 메서드 추출 이거는 코드짤 때 열심히 활용하고 있다.Ctrl + Alt + M추가적으로 메서드로 추출할 때 주의사항을 설명해주셨는데 도움이 많이 되었다.메서드를 추출할 때마다 항상 주의사항 4가지를 점검해야겠다. 메서드 추출시 주의사항 4가지메서드명은 적절한가?혹시 메서드가 두 가지 일을 하고 있지는 않은가?파라미터는 적절한가?반환타입은 적절한가?왜 이 4가지를 주의 해야 하는지는 강의를 통해 확인하시라. 배운 것들1. 추상화 레벨 "하나의 세계 안에서는 추상화 레벨이 동등해야 한다." - 박우빈추상화 레벨을 동등하게 맞춰야 한다는 개념은 처음 알게 되었다.이해를 돕기 위해 들어주신 서점 비유가 와 닿았다.추상화 레벨을 맞추지 않는 것은 서점 주인이 책들 사이에 표지, 제목도 없는 종이 모음을 책 이랍시고 내놓는 것과 같다.서점에 들어갔는데 종이 모음을 보게 되면 많이 당황스러울 것 같긴 하다. ㅋㅋㅋ나는 이런 서점 주인이 되어서는 안되겠다고 다짐했다. 2. Early returnEarly return 이라는 용어는 처음 접했다.이거를 무의식적으로, 이런 용어가 있다는 것도 모른채 사용해왔던 것 같다.Early return을 사용하면 뇌 메모리의 부하를 줄일 수 있겠구나를 깨달았다. 3. orElse, orElseGet, orElseThrow의 차이자바 8 문법을 배울 때 한 번씩 사용해본 메서드들인데 이번에 제대로 정리할 수 있었다. 4. getter 사용 자제setter를 사용하지 말라는 것은 들어봤지만, getter도 사용 하지 않는 것이 좋다는 것은 처음 들었다.어쩔 수 없을 때는 getter를 써야 겠지만, 클래스를 설계할 때 무지성으로 getter를 선언하는 행동은 지양해야겠다. 미션인프런 워밍업 클럽 스터디는 강의 수강 뿐만 아니라 미션 수행도 해야 한다.이번 1주차에는 Day 2, Day 4 미션이 있었다. Day 2 미션 - 추상과 구체 예시일상 생활에서 추상과 구체에 대한 예시를 찾는 미션이었다.요즘 관심 있는 주제에서 예시를 찾으려고 했다.요즘 관심 있는 주제는 주식, 운동, 스타크래프트인데,운동 분야가 가장 설명하기 쉬워 보였고, 모든 사람이 알만한 스쿼트에 대해서 구체적으로 설명했다. Day 2 미션 답변 - 스쿼트를 구체 레벨에서 표현한다면?발을 어깨너비로 벌리고 무게중심을 발뒤꿈치에 두면서 무릎을 굽힌다.숨을 참고 복압을 유지한 상태로 엉덩이를 뒤로 밀며 허벅지가 지면과 평행해질 때까지 내려간다.하체 근육을 사용해 다시 상체를 들어 올린다.동작이 끝난 후 숨을 내쉰다. Day 4 미션 - 메서드 리팩토링 실습, SOLID 개념을 자신 만의 언어로1. 메서드 리팩터링 실습 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; }위의 메서드를 읽기 좋은 코드로 리팩토링 해보는 미션이었다.Early return, 추상화 레벨을 맞춰주기, 공백 라인, 부정어, 이름 짓기에 주의하며 리팩터링을 수행했다.아래 코드가 리팩터링을 거친 코드public boolean validateOrder(Order order) { if (order.getItems().isEmpty()) { log.info("주문 항목이 없습니다."); return false; } if (order.isTotalPriceInvalid()) { log.info("올바르지 않은 총 가격입니다."); return false; } if (order.hasNoCustomerInfo()) { log.info("사용자 정보가 없습니다."); return false; } return true; }2. SOLID 개념을 자신만의 언어로견고한 객체지향 프로그램을 위해 제시된 개념인 SOLID를 자신만의 언어로 설명하는 미션이었다.글이 너무 길어지니까 이거는 생략하겠다. 아쉬웠던 점읽기 좋은 코드로 리팩터링하기 위해서는 테스트 코드가 꼭 필요하다.원래라면 테스트 코드를 짰을 테지만, 시간이 없었다는 핑계로 테스트 코드는 건너 뛰었다.하지만, 테스트 코드 없는 리팩터링이 오히려 시간을 더 잡아먹을 수 있고, 훨씬 개발자를 불안하게 만드는 것 같다.3주차부터는 테스트 코드를 본격적으로 배우게 될텐데 기대된다. 마무리100점 짜리는 아니었지만, 내 상황에서 최선을 다한 한 주였다.미션을 제 때 낼 수 있어서 기쁘다.2주차도 잘해보자. 

백엔드읽기좋은코드클린코드워밍업클럽객체지향추상화

윤대

[도서 정리] - 결합도와 응집도 - { 오브젝트 : 코드로 이해하는 객체지향 설계 }

결합도 : 의존성의 정도, 다른 모듈에 대해 얼마나 많은 지식을 가지고 있는 지를 나타내는 척도.'결합도가 높다'는 두 가지 의미로 해석 될 수 있다.A라는 모듈이 B 모듈의 내부 구현을 자세히 알고 있어, B의 작은 변경에도 A까지 변경해야 할 경우.A라는 모듈을 B, C, D, E 등 다수의 모듈이 의존하고 있어. A를 변경할 시 나머지 모듈도 함께 변경해야 할 경우.즉, '결합도가 낮다'라고 것은 단순히 의존하는 모듈이 적은 것이 아니다. 변경이 일어날 수 있는 부분을 모두 철저히 캡슐화하여, 내부 구현 변경에 의하여 의존하는 다른 모듈이 영향을 받지 않도록 억제 하는 것이다.응집도 : 모듈 내부 요소들이 서로 연관돼 있는 정도.'응집도가 낮다'면 다음과 같은 특성이 나타난다.클래스 변경되는 이유가 두 가지 이상이다.클래스의 인스턴스를 초기화하는 시점에 경우에 따라 다른 속성들을 초기화하고 있다.메서드 그룹이 속성 그룹을 사용하는 여부가 갈린다.위의 3 경우는 해당 클래스의 응집도가 낮다는 지표임으로, 위의 증상이 나타난다면 클래스를 분리할 것을 고려해야 한다. ✒ 정리결국, 모든 것은 변경과 관련되어 있다. 변경이 일어날 수 있는 부분을 추상화하여 구현을 감추고 객체가 인터페이스에만 의존하게 한다면 자연스레 결합도가 낮아진다.서로 다른 속성을 써서 작동하는 비슷한 메소드를 캡슐화하여 새로운 객체로 추출하면, 자연스레 응집도가 높아지게 된다.그렇기에 우리는 적절하게 도메인을 뽑아낸 뒤, 협력에서 책임을 찾고 이를 적절하게 분배해야 할 것이다. 그리고 구현을 하며, 끊임 없이 리팩토링을 한다면 객체지향이 잘 지켜진 코드를 얻을 수 있을 것이다.

백엔드결합도응집도객체지향OOP

진영준

[인프런 워밍업 클럽 스터디 0기 발자국] 2주차 회고

안녕하세요. 주니어 백엔드 개발자를 꿈꾸는 12hugs 실명은 진영준입니다. 😉지난주 월요일부터 인프런 워밍업 클럽 스터디 0기를 시작했으며,2주차에는 무엇을 느꼈고 무엇을 공부했으며 어떤 사실을 깨달았는 지에 대해공유를 해볼까합니다. 그럼 시작합니다!내가 알던 JPA는 더욱 넓은 세계였다.제가 알던 JPA는 단순히 백엔드 개발자가 SQL언어에 대한 공부를 따로 해야하기도 하고,좀 귀찮기때문에 나온 개념이라고 생각했습니다.그래서 별 거 아닌 존재라고 생각하기도 했고, 간과한 부분이 없지 않아있었습니다.그렇지만, 강의를 들으면서 JPA의 기능에는 놀라운 기능들이 있다는 것을 알게되었습니다.existBy, countBy 등등 많은 기능을 지원하기도 하고, 직접 @Query 어노테이션을 통해JPQL언어로 DB에 SQL을 날릴 수 있는 것도 확인하였습니다. 그리고 하이버네이트란 무엇이고, JPA와 ORM은 무엇인지에 대한 개념도 잡히게 되었습니다.아무래도 너무 야생형으로 공부한 입장으로 해당 기능과 관련된 지식이 없으면구글링조차 할 수도 없기때문에 해당 단어들과 기능들은 사막의 오아시스와도 같았습니다. 객체지향 모두 지향하지만, 사실 지양하고 있다.객체지향이 중요하다는 점은 모르는 사람이 없을 것입니다.사실 이런 개념때문에 객체지향언어들이 각광을 받기도 합니다.하지만, 저는 전혀 객체지향을 하고 있지 않았습니다. 제가 짠 로직들은 전부 절차지향이었으며,class가 주인이 되는 코드를 짜왔습니다. 그렇지만, 이번 2주차를 통해 객체지향에 대한 개념이 조금 잡히기 시작했습니다.이건 미니프로젝트를 진행하며, 느낀 부분이었습니다.저는 원래 평소와 같이 서비스에 모든 비즈니스 로직을 쑤셔넣었습니다.하지만, 제가 쓴 코드인데도 불구하고, 읽기가 힘들고, 이해하기가 힘들었습니다.다시 말하자면, 코드싸개였고, 전혀 객체지향적이지도 못했습니다.출처 : https://namu.wiki/w/%EC%BD%94%EB%8D%94 그렇지만, 객체지향을 공부하기 시작하면서 객체의 불변성을 유지하기 위해서기존 테이블 @Entity 객체의 @Setter을 빼고, 정말 객체끼리 상호작용 및 의사소통을 하며,비즈니스 로직의 간소화를 진행할 수도 있었고, 훨씬 깔끔해진 코드와 함께 가독성 또한 좋아지게 되었습니다. 그래서 2주차를 통해 얻게 된 것은?프로젝트를 진행하기 전에 요구사항과 설계가 중요했다는 점을 강조하고 싶습니다.요구사항 스펙은 사람들이 원하는 기능을 함축해놓은 스펙입니다.즉, 클라이언트가 원하는 기능이 없다면, 개발자는 필요가 없어지게 되겠죠. 그리고 설계가 굉장히 중요합니다.설계를 잘 해놓고 시작해야 변수사항에 대해 잘 대처를 할 수 있으며,보다 유연하고, 객체지향적인 코드를 짤 수 있다는 생각을 했습니다. 그래서 저는 처음에 설계를 안하고 미니프로젝트를 시작했기 때문에며칠뒤 코드를 처음부터 다시 짜는 불상사를 일으켰습니다. 더욱 유연하고 뛰어난 개발자가 되고 싶다면,공부도 물론 중요하겠지만, 설계와 요구사항을 충분히 이해하고객체지향적으로 코드를 짤 줄 아는 개발자여야 한다는 걸 깨닫게 된 2주차였습니다.

인프런객체지향절차지향설계워밍업클럽스터디0기

스프링 핵심원리 기본편(김영한) 1 - 객체지향 DIP와 스프링 DI, IoC

  객체는 객체와 끊임없이 상호작용한다. 그렇기에 유연한 변경이 가능해야한다. 예를 들어, 자동차라는 상위 클래스를 다양한 자동차 브랜드로 구현될 수 있고, 운전자가 변화해도 자동차는 영향을 받지 않는다. 사용자, 주문, 할인 등 여러 독립적인 특징을 가진 기능은 클래스로 분리하여 각 클래스에서만 수정 및 사용한다.   역할과 구현을 분리 - 인터페이스와 콘크리트 클래스 인터페이스는 안정적이게, 확장이 무한대로 가능하게 설계해야한다.   SOLID 객체지향 설계 원칙 1. SRP 단일책임원칙 - 변경이 용이한 단위적 책임인가2. OCP 개방폐쇄원칙 - 코드의 변경 없이 확장이 가능한가(조립만으로 변경)3. LSP 리스코프 치환 원칙 - 하위 클래스는 인터페이스(상위 클래스)를 위반하지 않아야한다4. ISP 인터페이스 분리 원칙 - 여러 개의 인터페이스를 통해 명확한 기능을 갖고 있고, 대체 가능성이 높은 환경을 구현할 것5. DIP 의존관계 역전 원칙 - 추상화에 의존할 것, 인터페이스(역할)가 중심이 되어야한다. 구현체에 의존하면 다형성을 잃는다(재활용성을 잃는다) 스프링 컨테이너에 객체 지향 적용 객체를 생성하는 역할과 객체를 실행하는 역할을 분리.의존은 인터페이스로 하고, 설정 파일을 통해 구체적인 구현체를 의존 주입구현체 변경 시 설정 파일만 변경하면 된다.(조립)=> 제어의 역전; 어떤 구현체를 사용할 것인지 AppConfig(Spring)가 결정한다. 동적인 인스턴스 의존관계    

객체지향javaSOLIDspringDIIoCDIP강의김영한

객체 지향 프로그래밍 입문(최범균) 2 - 다형성, 추상화, 조립

  다형성이란, 여러 모형으로 변화하는 것이다. 하나의 객체가 여러 타입을 갖는 것이다. 추상화란, 특정한 성질(interface) 또는 공통 성질(abstract, 일반화)을 뽑아내는 과정이다. 추상화를 통해 객체는 다형적인 모형을 변화 가능하다   <추상화 시점> 추상화는 의존 대상이 변경하는 시점에 추가한다. 실제 변경 및 확장이 일어날 때 공통점을 파악하고 뽑아낸다.   <추상화 예시> 클라우드 파일 관리 기능이 있고, 대상 클라우드의 종류가 n가지일 경우.클라우드 종류에 따라 if문으로 분기하는 로직이 아닌 공통기능인 클라우드 파일 시스템을 추상화한다.클라우드 파일 시스템에서는 파일 목록과 관련된 CRUD 기능을 추상화하고,클라우드 파일에서는 개별 파일의 CRUD 기능을 추상화한다. 특정 클라우드 구현체에서는 추상 클래스를 상속받아서 기능을 재정의한다. 추상화가 진행되면, 구현 클래스의 변경은 있더라도(조립) 서비스 로직은 바뀌지 않는다.   <상속보다는 조립> 상속을 통해서 재사용을 하게 된다면,1. 상위 클래스의 변경이 어렵고2. 기능과 확장이 필요한 만큼 클래스가 증가하고3. 상속을 오용하게 된다.(비슷한 메서드 착오) 상속은 하위타입일 경우에 진행하고, 보통의 경우 객체를 참조하는 방식으로 진행할 것.    

java객체지향최범균강의추상화다형성조립객체참조

객체 지향 프로그래밍 입문(최범균) 3 - 분리, 의존 주입, DIP

<역할과 기능 분리 방법> 1. 패턴 적용 전형적 분리(아키텍처, 디자인패턴) 2. 계산 분리 로직의 기능화 3. 연동 분리 클래스 분리 4. 연속적인 if-else는 추상화 고민할 것   적절한 역할 분리는 테스트도 용이하게 한다.사용자와 직접적으로 관련된 기능은 내부 메서드로, 간접적으로 관련있는 기능은 별도의 클래스로 분리한다.   <의존> 순환 의존은 변경이 연쇄적으로 전파된다. 기능 변경의 파장이 커지면 안 좋기 때문에 의존은 적을수록 좋다. 의존대상의 기능이 많은 경우 클래스로 분리하거나 단일 기능으로 묶을 수 있는지 확인하라. 예를 들어 민원팩토리, 민원리포지토리를 민원등록으로 묶기   <스프링 의존 주입> 추상적 인터페이스를 의존하고, 의존 주입은 보통 생성자 방식으로 외부(스프링)에서 진행한다. 내부에서 new()로 생성하는 것과 반대이다. 1. 의존 대상이 바뀌면 그 대상을 조립하는 부분만 수정하면 됨 2. 대역 객체를 통해 테스트가 가능하다   <DIP 의존 역전 원칙> 고수준 모듈(기대수준), 저수준 모듈(단위적 실제 행위) 고수준 모듈을 의존해야한다. 반대로 고수준 모듈이 저수준 모듈을 의존하는 경우, 저수준 모듈이 변화할 때 고수준 모듈에 영향을 끼침 (목표를 향해 개발하는 것이 아닌, 개발에 따라 목표가 변하는 현상)고수준 모듈을 구현한 추상타입(인터페이스)을 저수준 모듈이 의존하는 방식을 추구해야한다.    

java강의최범균DIPDIinterface분리객체지향

객체 지향 프로그래밍 입문(최범균) 1 - 객체지향, 캡슐화

좋은 코드란, 낮은 비용으로 변화할 수 있는 코드이다 이것은 1. 캡슐화 2. 추상화(다형성 지향)로 이루어낼 수 있다.   절차지향적 코드는 진행될수록 여러 조건문으로 복잡해질 수 있다. 객체지향적 코드는 객체가 제공하는 기능(메서드)이 중심이 되어 설계하는 것이다.  - 호출, 리턴, 익셉션 등의 메세지의 교환 - 데이터 클래스(VO, DTO)는 객체가 아니다. 객체의 기능이 없이 값에만 접근하기 때문이다.   캡슐화는 데이터와 관련된 기능을 묶는 것이다. 데이터의 상세 내용을 외부에 감추고, 외부와 무관하게 객체 내부의 구현 변경이 가능하다. 객체의 기능을 비즈니스 로직이 아닌 객체 내부의 메서드로 구현하면, 기능에 변화가 요구될 때 해당하는 내부 기능을 변경하면 캡슐화를 사용한 곳에 별도의 수정이 필요하지 않다.   캡슐화의 규칙 1. 데이터를 요구하는 것이 아닌 데이터의 처리를 요구할 것if(member.getAge() > 19) Xif(member.isAdult()) O 2. 메서드에서 생성한 객체의 메서드만 호출할 것파라미터로 받은 객체의 메서드만 호출할 것필드로 참조하는 객체의 메서드만 호출할 것 >> 연속적인 메서드 호출이 아닌 객체에 있는 하나의 메서드로 처리member.isAdult() + member.isVIP() + member.addCoupon()으로 하나씩 처리하는 것보다member.receiveBenefits()로 위 세 개 기능 묶기     객체는 속성과 기능으로 구성되어있다. 객체의 여러 기능을 참조하고 묶어서 새로운 기능에 사용하는 것은 객체 지향적인 방식이다.        

java객체지향최범균강의캡슐화DDD

<도서정리> 객체지향의 사실과 오해

객체지향이란. 흔히 객체지향을 세상을 객체들의 모임으로 보고, 객체들이 상호작용하는 것을 소프트웨어 세계에 모방하는 방식으로 소개한다. 하지만, 객체지향은 실세계를 모방하는 것이 아닌 새로운 세계를 창조하는 것이다. 현실 속에서 수동적인 존재가 소프트웨어 객체로서는 능동적인 객체가 된다. 사물의 의인화가 이루어지며, 얼마든지 필요한 능력을 가질 수 있다. 객체 상태와 행동의 명명 또한 창조의 영역이다.   객체란. 객체들 간에는 특정한 목표를 위해 협력하며, 객체는 협력 속에서 맡은 역할에 책임을 진다. 협력은 연쇄적인 요청과 응답으로 구성되어있다. 객체 특징재활용성 - 여러 객체가 동일한 역할을 수행할 수 있다.활용성 - 역할을 대체가능성을 의미한다.다형성 - 책임을 수행하는 방법은 자율적으로 선택할 수 있으며, 다양한 방식으로 요청 수행이 가능하다.한 객체가 동시에 여러 가지 역할을 수행할 수 있다. 다양한 곳에 참여한다.(참조되어 사용된다)   협력. 시너지; 전체는 부분의 합보다 크다. 성공적인 협력을 위해서는 적절한 단위적인 책임을 부여하는 것이 중요하다.  객체는 충분히 협력적이어야한다. 다른 객체에게 적극적으로 도움을 요청할 정도로 열린 마음을 지녀야 하며, 모든 것을 스스로 해결하려는 전지전능한 객체는 복잡함에 의해 자멸한다. 객체는 자율성을 가진다.   객체의 자율성. 캡슐화 - 객체의 자율성은 객체의 내부와 외부를 명확하게 구분하는 것으로부터 나온다. 객체의 사적인 부분은 객체 스스로 관리하고, 외부에서 일체 간섭할 수 없도록 차단해야한다. 객체의 외부에서는 접근이 허락된 수단을 통해서만 객체와 의사소통해야한다. 무엇을 수행하는지는 알 수 있지만 어떻게 수행하는지에 대해서는 알 수 없다. 객체는 상태와 행동을 지닌다.  객체는 자신의 상태를 직접관리하고 상태를 기반으로 스스로 판단하고 행동하는 자율적인 존재이다. 객체의 행동이 상태를 결정한다. 객체의 행동은 객체가 협력에 참여하는 방법이며, 객체의 적합성은 객체의 행동으로부터 결정된다. 책임주도설계 - 객체가 어떤 책임을 갖는가가 설계를 주도한다. interface를 통해 제공가능한 정보와 서비스를 알려주는 동시에 캡슐화를 수행할 수 있다 인터페이스가 변경되지 않는다면, 내부 방식을 변경하더라도 그것이 영향을 끼치지 않는다. 동일한 인터페이스에 의존한다면, 어떠한 구현체와도 상호작용을 할 수 있다.   객체와 추상화. 추상화를 통해 현실세계의 복잡성을 극복할 수 있으며, 그를 통해 사물의 본질에 접근할 수 있다. 추상화 과정 - 유사성, 공통점을 통해 분류한다. 객체의 추상화는 분류(classification)를 통해 이루어진다. 객체지향은 객체를 지향하는 것이지 클래스를 지향하는 것이 아니다 일반화/특수화 서브타입은 슈퍼타입(본질)을 대체할 수 있어야한다. 슈퍼타입의 행동은 서브타입에 자동으로 상속된다. 타입 타입은 추상화이다. 타입은 동적인 상태 변경을 단순화하는 정적 객체 특징에 집중한다. 클래스는 타입을 구현하는 도구이다. 추상화를 통해 다양한 객체들이 조립되어 역할을 수행할 수 있다면 협력이 유연해지며 객체들의 재사용성이 높아진다.   메세징 메세징(요청)은 객체에게 접근할 수 있는 유일한 방법이다. 어떻게 할지는 객체 스스로가 결정하며, 메세지로는 무엇을 할지만을 요청한다. 요청을 받은 후 객체는 적절한 메서드를 선택하여(다형성) 요청을 수행할 것이다.  객체지향은 객체들이 주고받는 메세지들로 구성된다. 클래스는 객체들의 속성과 행위를 담는 틀일 뿐이며, 객체의 속성과 행위가 중심이 되어야한다. 또한 객체지향은 객체를 넘어서 객체들 간의 커뮤니케이션에 초점을 맞출 때 이루어진다. 메세징을 이용한 객체지향 객체가 책임을 완수하기 위해, 다른 객체의 도움이 필요하다고 판단되면도움 요청 메세지를 결정한다. 메세지를 결정한 후 메세지를 수행하기에 적합한 객체를 선택한다. -> 메세지가 수신자의 책임을 결정한다.   

도서객체지향협력역할책임추상화다형성캡슐화

채널톡 아이콘