검색결과 리스트
Coding/Design에 해당되는 글 1건
- 2018.06.21 객체지향 프로그래밍 입문
글
인프런 - 객체 지향 프로그래밍 입문 (최범균)을 듣고 느끼고 배운 점을 정리한 내용
미래의 나와 나와같은 사람들이 한번 훓고 갔을 때 흥미라도 생기게 냄겨놓음
왜 객체지향이 필요한걸까?
코드를 생성하는 비용은 시간이 지남에 따라 증가한다 -> 처음에는 영향을 주는 코드가 적어 생산할 수 있지만, 새로운 요구사항과 기능을 추가함에 따라 같은 양의 코드를 생산하는데도 더 많은 노력과 시간이 필요해진다.
아래와 같이 코드가 변함에 따라, 수정할 때 찾아 헤메는 시간이 늘어난다 (같은 코드를 여러군데 사용했기 때문에)
처음에는 시간을 재기 위해 사용하고 이곳 저곳 필요할 때 마다 작성해 사용했다. 하지만 시간이 지나 '밀리초가 아닌 나노초로 보여주세요' 라는 요구사항이 생기면, 여태까지 작성했던 밀리초들을 다 찾아다니면서 나노초로 바꾸어 주어야한다.
코드 분석시간과 변경시간의 증가를 어떻게 막을 것인가?
낮은 비용으로 코드를 수정할 수 있는 방법
1. 객체 지향, 함수향, 리액티브
2. TDD, DDD, 클린 아키텍쳐
3. 애자일, 데브옵스
캡슐화와 다형성(추상화)을 통해 줄이자
절차지향 VS 객체지향
절차지향
데이터를 여러 프로시져가 접근하고 수정하는 방식이다.
처음의 코드작성을 쉽다. (변수를 선언하고, 전달이 용이, 직관적)
시간이 지날수록 데이터를 공유하는 프로시져의 갯수가 늘어나고, 수정을 하기 어렵게 만드는 요인이 된다.
객체지향
데이터와 프로시저를 객체라는 하나의 단위로 묶는다.
특정 객체가 가지고 있는 데이터는 그 객체의 프로시져만 접근할 수 있게한다.
다른 객체는 데이터에 바로 접근할 수 없고, 프로시져를 호출해 데이터에 접근하는 방식으로 연결한다.
시간이 흘러도 코드를 수정하기 수월하다
객체
객체는 기능을 제공한다
내부적으로 가진 필드로 정의하지 않는다
암호변경하기, 차단여부 확인하기 기능 과 같이 정의한다.
기능 명세 : 메소드를 이용해 기능을 명세한다.
- 이름과 파라미터 결과값으로 구성한다.
객체와 객체간은 기능을 사용해 연결한다
기능의 사용은 메소드를 호출하는 것이다
객체간에 데이터를 주고 받는것을 메세지를 주고받는다고 표현한다.
메소드를 호출하는 메세지, 리던하는 메세지, 예외처리 메세지
캡슐화
데이터와 관련기능을 묶는 것으로 시작한다.
객체가 어떻게 구현 되었는지 상세 내용을 외부에 감춘다.
구현에 사용된 데이터의 상세 내용을 외부에 감출 수 있다. -> 정보은닉의 의미 포함
외부에 영향없이 객체 내부 구현만 변경이 가능하다
캡슐화를 하지 않으면, 요구사항이 변했을 때 데이터의 구조 및 사용에 변화를 가져오게된다.
예를 들면
정회원의 권한이 있는지 검사할 필요가 있을 때
정회원의 조건 요구사항이 변경되어도 캡슐화가 되어있으면 오른쪽 코드만 변경하면 된다.
캡슐화를 코드를 수정할 때 연쇄적인 변경 전파를 최소화 할 수 있다.
요구사항의 변화는 내부구현만 변경하면된다.
캡슐화된 코드를 사용하는 코드에 영향을 최소화한다.
기능에 대한 이해도를 높일 수 있다.
캡슐화를 위한 규칙
1. Tell, Don't Ask
데이터를 달라고 해서 비교하거나 처리하지 말아야 한다.
데이터를 넘겨주어 결과를 넘겨받는다.
2. Demeter's Law
메소드에서 생성한 객체의 메소드만 호출하도록하자
파라미터로 받은 객체의 메소드만 호출하도록하자
필드로 참도하는 개체의 메소드만 호출하도록하자
추상화
다형성
여러 모습을 가지는 것을 다형성이라고 한다
객체지향에서는 한 객체가 여러 타입을 가지는 것을 가리킨다.
측 한 객체가 여러 타입의 기능을 제공할 수 있다
타입 상속으로 다형성을 구현할 수 있다 -> 하위 타입은 상위 태입도 된다.
예를 들면
- csp파일 업로드, http 데이터 전송, 데이터 베이스 테이블에 삽입
- 이런 것들을 추상화 하면 푸시 발성 요청으로 표현 가능하다
타입 추상화
여러 구현 클래스를 대표하는 상위 타입을 도출한다
보통 인터페이스 타입으로 추상화 한다 (기능에 대한 의미를 제공하고 그 내부의 디테일한 구현을 제공하지는 않는다.)
추상화 타입과 구현은 타입상속으로 연결된다
EmailNotifier, SMSNotifier, KakaoNotifier 등이 콘크리트 클래스이다.
추상 타입을 사용하면 Notifier 라는 추상타입을 만들어 기능 구현이 아닌 의도를 드러낸다
유연한 설계를 가능하게 만든다 -> 콘크리트 클래스를 직접 사용하면 요청사항이 바뀔 때 마다 코드가늘어난다.
공통점을 도출해서 -> 추상 타입을 사용하도록 한다
추상화는 의존 대상이 변경하는 시점에 진행한다
추상화를 하면 추상타입이 증가하고 코드의 복잡도가 증가하기 때문이다.
아직 존재하지 않는 기능에 대한 추상화를 진행하면 너무 복잡해지고, 잘못 추상화 될 수도 있다.
추상화를 잘 하려면 구현을 한 이유가 무엇인지 생각해 봐야한다
상속보단 조립
상속
조립
상속하기에 앞서 조립으로 풀 수 없는지 검토한다
진짜 하위타입인 경우에만 사용한다
기능 책임과 분리
기능 분해

기능은 곧 책임을 의미한다
변경대상 확인의 요구사항 중 변경대상을 구하는 기능 하나의 책임을 지운다.
분리한 각 기능을 알맞게 분배해야 한다.
하위의 기능을 사용한다.
큰 클래스, 큰 메소드 분해
책임 분배/분리 방법
패턴 적용
계산 기능 분리
외부 연동 분리
조건별 분기는 추상화
패턴 적용
전형적인 역할 분리의 방법이다.
간단한 웹
컨트롤러, 서비스, DAO
복잡한 도메인
엔티티, 밸류, 리포지토리, 도메인 서비스
AOP
Aspect(공통 기능)
GoF
팩토리, 빌더, 전략, 템플릿 메서드, 프록시/데코레이터 등
계산 하는 부분을 분리
포인트를 계산하는 부분을 별도의 클래스로 사용
연동하는 부분을 분리
네트워크, 메시지, 파일전송 등 연동부분 처리
조건 분기 추상화
연속적인 if-else문의 경우에는 추상화를 고민할 수 있다.
역할 분리시 주의할 점
의도가 잘 드러나는 이름을 사용해야 한다
HTTP로 추천 데이터 읽어오는 기능을 분리할 경우 RecommendService 보다는 HttpDataService가 더 명확한 표현이다.
역할분리가 잘 되면 테스트에도 용이해진다.
의존과 DI
의존
기능 구현을 위해 다른 구성 요소를 사용하는 것이다
의존의 예 : 객체 생성, 메서드 호출, 데이터 사용
의존은 소스코드를 변경할 경우 다른 소스코드 까지 변경이 전파될 가능성을 의미한다
의존하는 대상이 바뀌면 바뀔 가능성이 높아진다
예 : 호출하는 메서드의 파라미터가 변경
예 : 호출하는 메서드가 발생할 수 있는 익셉션 타입이 추가
순환 의존
서로 의존하는 관계가 얽혀 하나의 변경에 변경 연쇄 전파 가능성 존재
클래스, 패키지, 모듈 등 모든 수준에서 순환 의존이 없도록해야한다
의존하는 대상이 많다면?
의존하는 대상이 많으면 의존하고 있는 대상이 변경될 확률이 높다
의존하는 대상은 적을수록 좋음
의존 대상이 많을 경우
1) 기능이 많은 경우
한 클래스에서 많은 기능을 제공하는 경우
각 기능마다 의존하는 대상이 다를 수 있고
한 기능 변경이 다른 기능에 영향을 줄 수 있음
기능 별로 분리 고려할 필요가 있다
클래스 개수는 증가하나 클래스마다 필요로 하는 의존이 적어짐 책임이 줄어든다
2) 묶어보기
몇 가지 의존 대상을 단일 기능으로 묶어서 생각하면 의존 대상을 줄일 수 있다
기능 구현을 추상화한다
의존 대상 객체를 직접 생성하면?
생성 클래스가 바뀌면 의존하는 코드도 바뀜(추상화 때 언급)
의존 대상 객체를 직접 생성하지 않는 방법
팩토리, 빌더
의존 주입(Depedency injection)
서비스 로케이터(Service Locator)
의존 주입(Depedency Injection)
외부에서 의존 객체를 주입
생성자나 메서드를 이용해 주입
조립기
조립기가 객체 생성, 의존 주입을 처리
예 : 스프링 프레임워크
DI의 장점
1) 상위 타입을 사용할 경우 의존 대상이 바뀌면 조립기(설정)만 변경하면 된다
2) 의존하는 객체 없이 대역 객체를 사용해서 테스트 가능하다
DI를 습관처럼 사용하기
의존 객체는 주입받도록 코드 작성하는 습관!
지금은 아니여도 점점 사용해보기
다음 학습 추천
강의 복습
TDD (개발 속도, 좋은 설계 가능성 높여줌)
함수형 프로그래밍 기초 (비용을 낮춰주는 다른 방법)
각 패러다임의 설계 패턴(지식/지혜 재사용)
UML(도식화)
부록(DIP)
고수준 모듈, 저수준 모듈
고수준 모듈
의미있는 단일 기능을 제공한다 -> 요구사항을 반영한다
상위 수준의 정책 구현
저수준 모듈
고수준 모듈의 기능을 구현하기 위해 필요한 하위 기능의 실제 구현
하지만 고수준이 저수준에 직접 의존하면
저수준 모듈이 변경되었을 때 고수준 모듈에 영향을 미친다
고수준 정책이 바뀌지 않았으나 저수준 구현 변경으로 코드 변경이 발생 해버린다.
Dependency Inversion Principle 을 지켜야한다.
의존 역전 원칙
고수준 모듈은 저수준 모듈의 구현에 의존하면 안됨
저수준 모듈이 고수준 모듈에서 정의한 추상타입에 의존해야 함
고수준 관점에서 추상화
고수준 입장에서 저수준 모듈을 추상화
구현 입장에서 추상화하지 말자
구현을 하면서 추상화를 하면 저수준 입장에서 추상화 할 수있기 때문이다
DIP는 유연함을 높임
고수준 모듈의 변경을 최소화하면서 저수준 모듈의 변경 유연함을 높인다
부단한 추상화 노력 필요하다
처음부터 바로 좋은 설계가 나오진 않는다
요구사항/업무 이해가 높아지며 저수준 모듈을 인지하고 상위 수준 관점에서 저수준 모듈에 대한 추상화 시도해보는 것이 좋다.
Reference
인프런 -
(최범균)
RECENT COMMENT