개요
1편을 작성한지 어언 한달이 지났는데요, 못다한 마무리를 이번주 내로 마무리 지어볼 예정입니다.
•
클래스안에 1개이상 4개 이하의 객체를 캡슐화
•
클래스안에 5개 이하의 퍼블릭 메서드 사용.
•
정적 메서드 금지 (유틸리티 클래스, 싱글턴 금지)
•
인자의 값으로 null금지
•
불변 객체 사용
•
Getter setter 사용 금지
객체를 다른 객체와 상호작용할 수 있도록 준비시키기 위해 필요한 몇 가지 원칙
•
클래스안에 4개이하의 객체를 캡슐화 할 것을 권장한다.
•
상태는 객체의 식별자여야 한다. -> 4개 이상의 객체로 식별자를 구성할 경우 직관에 위배된다. -> 복잡도 증가.
•
상태는 객체의 식별자.
•
상태가 없는 객체는 악명 높은 정적 메서드와 유사하다.
•
애플리케이션 전체를 유지보수 가능하도록 만들기 위해서는 최선을 다해서 객체를 분리해야 한다.
•
기술적인 관점에서 객체 분리란 상호작용하는 다른 객체를 수정하지 않고도 해당 객체를 수정할 수 있도록 만든다는 것이다.
•
객체를 분리하는 데 사용하는 도구 = 인터페이스
•
의존성 역전의 법칙
◦
의존관계를 갖는 모듈 인스턴스의 구성이 추상화에 의존하는 것
◦
구체적인 의존 관계가 추상화에 의해 런타임에 결정되기 때문에 다형성을 적극적으로 활용할 수 있으며 모듈의 재사용성이 높아진다.
•
메서드의 목적이 무엇인지 먼저 생각하라.
•
빌더 = 명사 : 뭔가를 만들고 새로운 객체를 반환하는 메서드 -> 객체에게 무엇을 만들라고 요청한다.
•
조정자 = 동사 : 엔티티를 수정하는 메서드 -> 객체에게 무엇을 할지를 알려주어야 한다.
•
두 개의 개념이 섞여 있는 메서드가 존재해서는 안됨.
•
좋은 메서드 이름 : 객체를 설계한 목적, 객체가 수행해야 하는 임무, 객체의 존재 목적과 살아가는 의미를 더 잘 이해할 수 있도록 해준다.
•
boolean값을 반환하는 경우 : 가독성 측면에서 이름을 형용사로 지어야 한다.
•
객체들은 어떤 것도 공유해서는 안된다. 대신 독립적이어야 하고 닫혀 있어야 한다.
•
개방 -폐쇄 원칙 : 확장에 대해서 열려있고, 수정에 대해서 닫혀있다.
•
결합도 증가, 응집도 저하
◦
전역 가시성 안에 방치됨 : 이 객체가 어떤 문맥 안에서 어떻게 사용되어야 하고, 이 객체의 변경으로 인해 사용자가 어떤 영향을 받지에 관해서도 알 수 없음.
◦
객체가 자신의 문제를 해결하는데 덜 집중.
•
불변성 : 크기가 작고, 응집력이 높으며, 느슨하게 결합되고, 유지보수하기 쉬운 클래스를 만들 수 있도록 한다.
•
불변 객체를 수정해야 한다면 프로퍼티를 수정하는 대신 새로운 객체를 생성해야 한다.
•
불변 객체를 사용해야 하는 이유
◦
식별자 가변성 : 불변 객체에는 식별자 가변성 문제가 없다.
◦
실패 원자성을 보장할 수 있다. : 완전하고 견고한 상태의 객체를 가지거나 아니면 실패하거나 둘 중 하나만 가능한 특성. 중간은 없다.
◦
시간적 결합을 제거할 수 있다. : 가변 객체를 처리하는 코드의 순서에 대해 신경 쓸 필요 없음.
◦
부수효과 제거 : 수정할 수 없으니까ㅇㅇ
◦
null참조 없애기 : 모든 객체가 불변이면 애포에 null을 포함 시키는게 불가능해진다.
◦
스레드 안전성 :
◦
작고 더 단순한 객체 : 객체가 더 단순해질 수록 응집도는 더 높아지고, 유지보수하기는 더 쉬워진다.
•
나쁜 설계는 문서화를 강제한다.
•
작은 객체 : 유지보수가 가능 + 높은 응집력 + 용이한 테스트
•
클래스의 크기를 정하는 기준 : 퍼블릭 메서드의 개수
•
왜 적은 수의 퍼블릭 메서드를 가져야 할까?
◦
많은 수 보다 적은 수의 메서드 들을 가지고 조화를 이루도록 만드는 것이 쉽다.
◦
단일 책임 원칙을 지키는데 도움이 된다.
◦
클래스가 작으면 메서드와 프로퍼티가 더 '가까이' 있을 수 있기 때문에 응집도가 높아진다.
•
자바 jvm이랑 연결해서 설명하면 좋을 듯
•
정적 메서드 대신 객체를 사용해라.
•
정적 메서드는 소프트웨어를 유지보수하기 어렵게 만든다.
•
객체지향 프로그래밍과 절차적 프로그래밍의 차이는 'is a'이다.
◦
객체지향적인 생각 : 객체가 무엇인지만 정의하고 객체들이 필요할 때 스스로 상호작용하도록 제어한다.
◦
cpu에게 할 일을 지시하는 것이 아니라 정의한다. ( x는 5와 9의 최댓값이다.)
•
선언형 스타일 vs. 명령형 스타일
•
명령형 : 프로그램의 상태를 면경하는 문장을 사용해서 계산 방식을 서술
◦
메서드를 호출한 시점에 cpu가 즉시 결과를 계산
•
선언형 : 제어흐름을 서술하지 않고 계산 로직을 표현
◦
메서드가 무엇인지만 정의 -> 실제 사용 시점에 값을 계산한다
◦
cpu에게 결과가 실제로 필요한 시점과 위치를 결정하도록 위임, cpu는 요청이 있을 경우에만 계산을 실행
•
왜 선언형 방식이 더 좋은가?
1.
직접 성능 최적화를 제어할 수 있기 때문에 더 빠르다.
2.
다형성 (코드 블록 사이의 의존성을 끊을 수 있는 능력 ) : 객체 사이의 결합도를 낮출 수 있을 뿐만 아니라, 이 작업을 우아하게 처리할 수도 있다.
◦
정적 메서드는 분리가 불가능 하다. 정적 메서드를 전달하는 것을 불가능 하다. (???)
1.
표현력
•
선언형 방식은 결과를 이야기하는데 반해 명령형 방식은 수행 사능한 한 가지 방법을 이야기 한다.
•
때문에 명령형 방식에서 결과를 예상하기 위해서는 먼저 머릿속에서 코드를 '실행' 해야한다. -> 선언형보다 덜 직관적.
2.
코드의 응집도
•
불변 객체가 시간적인 결합 문제를 해결할 수 있다. (선언형 프로그래밍 역시 마찬가지)
•
정적 메서드가 이미 있을 경우 : 우리 코드가 객체를 직접 처리할 수 있도록 정적 메서드를 감싸는 클래스를 만들어 종양을 고립시키는 것이다.
•
유틸리티 클래스 , 싱글톤 패턴 = 안티패턴 \
◦
유틸리티 클래스와 싱글톤의 차이 : 싱글톤은 분리 가능한 의존성으로 연결되어 있는데 반해, 유틸리티 클래스는 분리가 불사능한 하드코딩된 결합도를 가진다.
•
함수형 프로그래밍과 조합 가능한 데코레이터
◦
함수합성이랑 비슷한 개념
•
정적 메서드는 조합이 불가능하다.
•
null을 사용하는 방식은 각각의 객체가 자신의 행동을 온전히 책임진다는 객체지향 패러다임과 상반되는 아이디어이다. 121p
•
null여부를 체크함으로써 객체가 맡아야 하는 상당량의 책임을 빼앗게 된다. 123p -> 객체를 멍청한 자료구조로 퇴화 시키고 있는 것
•
전달할 것이 없다면, 비어있는 것처럼 행동하는 객체를 전달하면 된다. 대신 항상 객체를 전달하되, 전달할 객체에게 무리한 요청을 한다면 응답을 거부하도록 객체를 구현해야 한다. 125p -> 모나드 같다!
◦
실제적인 구현이 필요하다! 한 번 고민하고 만들어 보자!
•
null확인 로직으로 코드를 오염시켜서는 안된다.
•
불변 객체 : 객체가 살아있는 동안 상태가 변하지 않음 ( 객체가 대표하는 엔티티에 충성하기 때문 ). 객체의 행동이나 메소드의 반환값은 중요하지 않음.
•
상수처럼 동작하는 객체 = 불변성의 특별한 경우
•
객체 = 실제 엔티티(ex : 디스크에 있는 파일)의 대표자
•
불변 객체와 가변 객체의 차이
◦
불변 객체에는 식별자가 존재하지 않으며, 절대로 상태를 변경할 수 없다.
◦
불변 객체의 식별자는 객체의 상태와 완전히 동일하다.
•
객체지향의 사실과 오해와 충돌되는 내용인것 같다.
•
클래스는 어떤 식으로든 멤버에게 접근하는 것을 허용하지 않는다. -> 캡슐화
•
자료구조는 투명하지만 객체는 불투명하다.
•
모든 프로그래밍 스타일의 핵심 목표는 가시성의 범위를 축소해서 사물을 단순화 시키는 것
•
특정 시점에 이해해야 하는 범위가 작을수록, 소프트웨어의 유지보수성이 향상되고 이해하고 수정하기도 쉬워진다.
•
데이터를 객체 안에 감추고 절대로 외부에 노출해서는 안된다.
•
getter와 setter는 캡슐화 원칙에 위배된다.
•
객체를 데이터 저장소로 취급하지 않고 객체로서 존중해 줄 것.
•
메소드 안에서 new연산자를 사용하여 다른 객체 참조 = 하드 코딩된 의존성
•
하드 코딩된 의존성 : 클래스가 다른 클래스에 직접 연결되어 있음.
•
하드 코딩된 의존성이 소프트웨어를 테스트하고 유지보수하기 어렵게 만든다. -> 변경의 전파가 일어남.
•
객체가 필요한 의존성을 직접 생성하는 대신, 생성자를 통해 의존성을 주입해야 한다.
•
부 생성자에서만 new를 사용할 것.
•
코드가 런타임에 다른 코드에 의해 수정된다는 사실을 항상 기억해야 한다면, 코드를 읽기가 매우 어려울 것이다.
•
런타임에 객체의 타입을 조사하는 것은 클래스 사이의 결합도가 높아지기 때문에 기술적인 관점에서 좋지 않다.
•
반환된 값이 객체인지 확인해야 하기 때문에 객체를 신뢰할 수 없게 된다. 객체에 대한 존중 없음.
•
객체 = 신뢰( 객체가 자신의 행동을 전적으로 책임지고 외부는 어떤 식으로든 간섭하지 않는다. )하는 엔티티
•
반환값을 검사하는 방식은 애플리케이션에 대한 신뢰가 부족하다는 신호이다.
•
안전하게 실패하기 보다 빠르게 실패하기
◦
안전하게 실패하기 : 문제가 발생한 상황에서도 소프트웨어가 계속 실행될 수 있도록 최대한 많은 노력을 기울일 것을 권장.
◦
빠르게 실패하기 : 일단 문제가 발생하면 곧바로 실행을 중단하고 최대한 빨리 예외를 던지는 것.
▪
상황을 구조하는 대신, 가능하면 실패를 분명하게 만든다.
▪
문제를 빨리 발견 할 수록 문제를 수정하는 시간도 빨라진다.
•
null의 대안 : null 대신 어떤 것을 사용해야 할까?
◦
null을 반환 하거나 예외를 던지는 대신 객체 컬렉션을 반환한다.
◦
Null object 디자인 패턴 사용
▪
겉으로 보기에는 원래의 객체처럼 보이지만 실제로는 다르게 행동하는 객체 반환.
•
체크 예외란 무엇인가?
◦
자바에서 RuntimeException을 상속하지 않은 예외. 반드시 예외 처리를 하는 코드를 작성해야 한다,
•
언체크 예외란 무엇인가?
◦
RuntimeException을 상속한 예외. 예외 처리가 강제 되지 않는다. 언체크 예외는 무시할 수 있으며 예외를 잡시 않아도 무방하다.
•
왜 체크 예외만 던져야 하는가?
◦
어떤 예외가 던져질 지 예상할 수 없다. 비가시적
◦
체크 예외는 가시적이기 때문에 예외에 대한 타입을 인지할 수 있음 (?)
•
예외 체이닝
◦
상위로 던져
◦
예외 체이닝은 의미론적으로 문제와 관련된 문맥을 풍부하게 만들기 위해 필요하다.
•
부모 클래스에서 자식 클래스에 대한 접근은 문제를 발생하게 할 수 있다.
•
클래스와 메서드를 final이나 abstract로 제한하면 문제 발생 가능성을 제한할 수 있다.