object

오브젝트 9장

OCP - 추상화에 의존하여 컴파일 타임의존성은 유지하면서 런타임 의존성의 가능성을 확장하고 수정할 수 있는 구조.

객체 생성,사용자 분리 - 동일 클래스 안에서 객체 생성과 사용이라는 두가지 이질적인 목적을 가진 코드가 공존할 경우 부적절.
(Movie에게 금액할인정책을 적용할지, 비율할인 정책을 적용할지 알고 있는것은 그 시점에 Movie와 협력할 클라이언트이므로 클라이언트가 할인정책을 생성하고 Movie는 추상화된 메소드를 사용하여 구현)

FACTORY - 생성과 사용을 분리하기 위해 객체생성에 특화된 객체

표현적 분해 - 도메인에 존재하는 사물 또는 개념을 표현하는 객체들을 이용해 시스템 분해

PURE FABRICATION - 특정 책임을 할당하기 위해 Information expert를 찾아봤는데 없을 경우 도메인 모델에 속하지 않는 Factory를 활용. 보통 행위적 분해로 인해 생성되는것이 대부분

의존성주입 - 생성자 주입, setter주입, 메서드 주입(주입인가에 대한 논란이 있다)

SERVICE LOCATOR 패턴 - 저장소(Storage.AmountDiscountPolicy)를 통해 의존성 해결하는 패턴 (가장널리 쓰임 - 그렇지만 비추함)

의존성역전원칙 - 상위 모듈, 하위모듈 둘 다 추상화에 의존하라(과거 패러다임은 상위가 하위에 의존)

SEPARATED INTERFACE 패턴 - 추상화(인터페이스 등)를 별도의 패키지가 아닌 클라이언트가 속한 패키지에 포함시켜라

오브젝트 10장

상속을 위한 경고- 자식 클래스의 메소드 아넹서 super 참조를 이용해 부모클래스의 메소드를 호출할 경우 두 클래스는 강하게 결합된다. super호출을 제거 할 수읶는 방법을 찾아라

경고2 상속받은 부모 클래스의 메소드가 자식 클래스의 내부 구조에 대한 규칙을 깨뜨릴수있다
ex 스택 add(0,”data”) 으로 맨앞에 넣는것…

경고3 자식 클래스가 부모 클래스의 메소드를 오버라이딩 할 경우 부모클래스가 자신의 메소드를 사용하는 방법에 클래스가 결합될 수 있다

경고4 클래스를 상속하면 결합도로 인해 자식 클래스와 부모 클래스의 구현을 영원히 변경하지 않거나, 자식 클래스와 부모 클래스를 동시에 변경하거나 둘 중 하나를 선택할 수 밖에 없다

취약한 기반 클래스문제- 자식클래스가 부모 클래스의 뱐경에 취약해지는 현상

상속문제 해결방법
1.차이를 메서드로 추출하라 -> 중복코드를 부모 클래스로 올려라(메소드 먼저 올리면 불필요한 인스턴스 변수를 남겨두기 쉽다) -> 다른부분은 시그니처만 올려라(protected로)

상속의 오용과 남용은 어플리케이션을 이해하고 확장하기 어렵게 만드므로 정말로 필요한 경우에만 사용하라

오브젝트 11장

재사용을 위한 방법 - 상속, 합성
상속관계 - Is-a - 화이트박스 재사용(부모클래스의 내부가 자식에 공개되기 때문)
합성관계 - Has-a - 블랙박스 재사용(객체의 내부는 공개되지 않고 인터페이스를 통해서만 재사용됨.)

상속은 결합도를 높이며 코드재사용을 위해서는 상속보다는 합성을 써라

상속은 부모클래스의 안에 구현된 코드에 의존하지만 합성은 객체의 퍼블릭 인터페이스를 의존하므로 결합도 낮아진다.

상속은 프로그램을 구성하는 개념들을 기반으로 다형성을 가능하게 하는 타입계층을 구축하기 위한것이다.

훅메서드 - 추상 메서드와 동일하게 자식 클래스에서 오버라이딩할 의도로 메서드를 추가했지만 편의를 위해 기본 구현을 제공하는 메서드.
ex , 추상클래스 내 afterCalculate 메소드를 단순히 retrun fee; 로 구현하고 다른 메서드가 afterCalculate를 호출할 경우 추상클래스를 상속한 클래스들은 afterCalculate메서드를 적절히 오버라이딩 하여 활용할 수 있다.

클래스폭발/조합의 폭발- 상속의 경우 부모의 구현에 강하게 결합되므로 하나의 기능을 추가하기 위해 필요이상으로 많은 수의 클래스를 추가해야하는 경우를 말함.

상속관계는 컴파일 타임에 결저되고 고정되기 때문에 코드를 실행하는 도중에는 변경불가함.
합성은 컴파일타임 관계를 런타임 관계로 변경함으로써 문제를 해결한다. 합성을 사용하면 구현이 아닌 퍼블릭 인터페이스에 대해서만 의존할 수 있기 때문에 런타임에 객체의 관계를 변경할 수 있따.

상속이 조합의 결과를 개별 클래스 안으로 밀어넣는 방법이라면 합성은 조합을 구성하는 요소들을 개별 클래스로 구현한 후 실행 시점에 인스턴스를 조립하는 방법을 사용하는것이다.

컴파일 타임 의존성, 런타임 의존성의 거리가 멀수록 설계가 유연해지지만 거리가 멀면 멀수록 설계의 복잡도가 상승한다. 대부분의 경우 단순한 설계가 정답이지만 변경에 따르는 고통이 복잡성으로 인한 혼란은 넘어선다면 유연성에 손을 들어주는것이 맞다.

믹스인은 객체를 생성할 때 코드 일부를 클래스 안에 섞어 넣어 재사용하는 기법이다. 컴파일 시점에 필요한 코드 조각을 조합하는 재사용 방법이다.(합성은 실행시점에 객체를 조합하는 재사용방법)

상속과 믹스인은 다르다. 상속의 진정한 목적은 자식클래스를 부모 클래스와 동일한 개념적인 범주로 묶어 is-a관계를 만들기 위한것. 믹스인은 코드를 다른 코드안에 섞어넣기 위한 방법으로 상속과 같이 결합도 문제를 유발하지 않음

12장

행동관점의 상속은 부모클래스가 정의한 일부 메소드를 자식 클래스의 메서드로 포함시키는 것을 의미
런타임에 시스템이 자식 클래스에 정의되지 않은 메서드가 호출되었을 경우 이메서드를 부모클래스 안에서 탐색한다.

인스턴스는 자신의 상태를 인스턴스별로 메모리를 할당받는다. 메서드를 갖고있는 클래스는 한번만 메모리를 할당받는다.

업캐스팅:부모클래스 타입으로선언된 변수에 자식클래스의 인스턴스를항당하는것이 가능한것.
다운캐스팅: 부모클래스의 인스턴스를 자식클래스 타입으로 변환하기 위해 명시적으로 캐스팅하는것

정적바인딩(static binding)/초기바인딩(early binding)/컴파일타임 바인딩 : 코드를 작성하는 시점에 호출될 코드가 결정되는것. 즉 컴파일 타임에 결정되는것. 코드상에서 bar 함수를 호출하는 구문이 나타나면 실제로 실행되는 코드는 그 bar라는 함수.

동적바인딩/지연바인딩(late binding): bar메소드가 해당 클래스의 상속계층 어디에 위치하는지 알아야 어떤함수가 호출될지 알 수 있다. 즉 실행될 메서드가 런타임에결정되는것.

self 참조 (자바, C++,C# 에선 this): 객체가 메시지를 수신하면 컴파일러는 self참조라는 임시 변수를 자동으로 생성한 후메시지를 수신한 객체를 가리키도록 설정하고 동적 메서드 탐색은 self가 가리키ㅡㄴ 객체의 클래스에서 시작해서 상속계층의 역방향으로 이뤄진다. 동적 메서드 탐색은 self가 가리키는 객체의 클래스에서 시작해서 상속계층의 역방향으로 이뤄지며 메서드 탐색이 종료되는 순간 self 참조는 자동으로 소멸된다,.
이는 부모를 가리키는 parent 포인터와 함께 메서드를 탐색한다.

동적메서드 탐색 원리는 자동적인 메시지 위임 (이해할 수 없는 메시지일 때 부모에게 위임)/ 동적인 문맥(컴파일 시점이 아닌 실행시점에 무엇을 실행할지 결정)으로 이뤄진다.

C++에서는 부모 클래스의 메서드와 동일한 이름의 메서드를 자식 클래스에서 오버로딩하면 그 이름을 가진 모든 부모클래스의 메서드를 감춰버린다.

super.evaluate() 는 부모클래스의 메서드가 아니라 더 상위에 위치한 조상 클래스의 메서드일 수 있다.
super를 통해 메서드를 사용할 때 메서드를 호출한다고 표현하지 않고 super참조를 이용해 메서드를 전송한다고 한다.(super 전송)

위임 : 자신이 수신한 메시지를 다른 객체에게 동일하게 전달해서 처리를 요청하는것. 위임은 항상 형재의 실행 문맥을 가리키는 self 참조를 인자로 전달한다.
포워딩 : 처리를 요청할 때 self 참조를 전달하지 않느경우,

13장

서브타이핑: 상속의 첫 번째 용도는 타입계층을 구현하는것.
타입계층 안에서 부모 클래스는 일반적인 개념을 구현하고 자식 클래스는 특수한 개념을 구현함. 즉 부모클래스는 자식의 일반화이고 자식클래스는 부모클래스의 특수화이다.
인터페이스 상속이라고 부르기도 함. ex, DiscountPolicy

서브클래싱: 상속의 두 번째 용도는 코드 재사용이다.(이는 복잡성이 높아지므로 추천하지 않음)
구현상속 또는 클래스 상속이라고 부르기도 함

상속은 타입계층을 목표로 사용한다면 다형적으로 동작하는 객체들의 관계에 기반해 확장 가능하고 유연한 설계가 가능해진다.

객체기반 프로그래밍: 상태와 행동을 캡슐화한 객체를 조합해서 프로그램을 구성하는 방식
객체지향 프로그래밍: 객체들을 조합해서 어플리케이션을 개발하지만 상속과 다형성을 지원
위 둘은 상속과 다형성 지원여부로 구분되기도 하지만 클래스를 사용하는 프로그래밍으로 구분되기도 한다.

심볼 : 타입에 이름을 붙인것(ex. 프로그래밍 언어)
내연 : 타입의 정의로서 타입에 속하는 객체들이 가지는 공통적인 속성이나 행동(ex. 프로그래밍을 위한 어휘와 문법적 규칙의 집합)
외연 : 타입에 속하는 객체들의 집합 (ex. 자바, 루비, 자바스크립트 등등)

프로그래밍 언어에서 타입의 두 가지 목적
1.타입에 수행될 수 있는 유효한 오퍼레이션의 집합을 정의한다. (자바에서 +는 int, 문자열에 사용가능하며 다른타입에는 불가능)
2.타입에 수행되는 오퍼레이션에 대해 미리 약속된 문맥을 제공한다. (+는 int의 경우 더하기 연산, 스트링의 경우 문자열 concat)
즉 코드의 의미를 명확하게 전달하고 개발자의 실수를 방지하는데 사용된다.

상속의 올바른 용도는 타입 계층을 구현하는것!!!
다음 조건을 만족할 때 상속을 사용하자.
상속관계가 is-a 관계를 모델링하는가 - 일반적으로 [자식클래스]는 [부모클래스]다. 라고 할 때
클라이언트 입장에서 부모 클래스의 타입으로 자식 클래스를 사용해도 무방한가 - 상속 계층을 사용하는 클라이언트 입장에서 부모 클래스와 자식 클래스의 차이점을 몰라야함(이를 행동 호환성이라 함)
클라이언트 관점에서 두 클래스에 대해 기대하는 행동이 다르다면 그것이 is-a 관계이더라도 상속을 사용해선 안된다.
예) 펭귄은 새다 - 새는 날 수 있다. 상속을 거치면 펭귄은 날 수 있다.. 즉 기대되는 행동에 따라 타입 계층을 구성해야된다! 결론은 두 타입 사이에 행동이 호환될 경우에만 타입 계층으로 묶어야 한다
정리하자면 클라이언트가 두 타입이 동일하게 행동할 것이라고 기대한다면 두 타입을 타입 계층으로 묶을 수 있다,

인터페이스를 클라이언트 기대에 따라 분리함으로써 변경에 의해 영향을 제어하는 설계원칙(인터페이스 분리원칙(Interface Segregation Principle, ISP)이라 함. 이는 비대한 인터페이스의 단점을 해결한다.(450p)

행동호환성: 서브타입이 슈퍼타입이 하는 모든 행동을 동일하게 할 수 있어야 한다.
대체가능성: 자식클래스와 부모클래스 사이의 행동호환성은 부모클래스에 대한 자식 클래스의 대체가능성을 포함한다.

행동호환성과 대체가능성은 올바른 상속관계를 구축하기 위해 따라야 할 지침이다. 이 지침은 리스코프 치환 원칙이라는 이름으로 소개되어 왔다.

리스코프 치환 원칙: 서브타입은 그것의 기반 타입에 대해 대체 가능해야한다는 것으로 클라이언트가 차이점을 인식하지 못한 채 파생 클래스의 인터페이스를 통해 서브클래스를 사용할 수 있어야 한다.

리스코프치환 원칙은 자식 클래스가 부모 클래스를 대체하기 위해서는 부모 클래스에 대한 클라이언트의 가정을 준수해야한다는 것을 강조한다. Square를 Rectangle의 자식 클래스로 만드는 것은 Rectangle 에 대해 클라이언트가 세운 가정을 뒤흔드는 것이다.
ex. Vector를 사용하는 클라이언트의 관점에서 Stack의 행동은 Vector의 행동과 호환되지 않음. Vector의 클라이언트는 임의의 위치에 추가, 삭제가 가능할것을 예상하지만 Stack의 클라이언트는 임의의 위치에 조회나 추가를 금지할것이라 예상함
항상 클라이언트 입장에서 생각하라!! 클라이언트입장에서 정사각형은 직사각형이다. -> 클라이언트가 이 둘을 동등하게 취급할 수 있는지 생각해보자

리스코프 치환 원칙을 따르면 클라이언트의 입장에서 퍼블릭 인터페이스의 행동 방식이 변경되지 않는다면 클라이언트의 코드를 변경하지 않고도 새로운 자식 클래스와 협력할 수 있게된다.

일반적으로 리스코프 치환 원칙 위반은 잠재적인 개방-폐쇄 원칙 위반이다.

계약에 의한 설계: 클라이언트가 정상적으로 메서드를 실행하기 위해 만족시켜야 하는 사전조건과 메서드가 실해오딘 후에 서버가 클라이언트에게 보장해야 하는 사후조건, 메서드 실행 전과 실행 후에 인스턴스가 만족시켜야 하는 클래스 불변식의 세 가지 요소로 구성된다.

서브타입에는 같거나 더 약한 사전조건을 정의할 수 있다.
서브타입에는 슈퍼타입과 같거나 더 강한 사후조건을 정의할 수 있다.

14장

재사용을 위해서는 객체들의 협력 방식을 일관성 있게 만들어야 한다. 일관성은설계에 드는 비용을 감소시키고 과거의 해결 방법을 반복적으로 사용해서 유사한 기능을 구현하는 데 드는 시간과 노력을 대폭 줄일 수 있기 때문이다. 또한 특정한 문제를 유사한 방법으로 해결하고 있다는 사실을 알면 문제를 이해하는 것만으로도 코드의 구조를 예상할 수 있게된다.

유사한 기능을 구현하기 위해 유사한 협력패턴을 사용하면 시스템이해&확장에 대한 부담이 줄어든다

일관성있는 협력을 위한 지침
1.변하는 개념을 변하지 않는 개념으로부터 분리하라 (if문을 활용하지 않고 다형성을 통해 극복한 사례)
2.변하는 개념을 캡슐화하라(클라이언트는 Movie 안에 calculationDiscountAmount 메시지가 있다는것만 알고있다. 실제 호출은 Movie를 구현한 자식들로 인해 실행됨)
훌륭한 추상화를 찾아 추상화에 의존하도록 만들어라. 추상화에 대한 의존은 결합도를 낮추고 결과적으로 대체 가능한 역할로 구성된 협력을 설계할 수 있게해준다. 따라서 선택하는 추상화의 품질이 캡슐화의 품질을 결정한다.

객체와 협력하는 클라이언트 개발자는 객체의 인터페이스가 변하지 않기를 원한다. 따라서 자주 변경된느 내부 구현을 안정적인 퍼블릭 인터페이스 뒤로 숨겨야 한다. 캡슐화란 단순히 데이터를 감추는것이 아닌 변할수 있는 어떤 개념이라도 감추는것이다.

4가지 캡슐화 496 읽어보자 꼭

  • 데이터캡슐화
  • 메서드 캡슐화(protected메서드는 외부에서 접근 불가능)
  • 객체 캡슐화(합성을 의미)
  • 서브타입 캡슐화 (다형성으로 가능함. 서브타입에 대해 모르는지만 슈퍼클래스로 사용할 수 있는 상황)

서브타입 캡슐화는 인터페이스 상속을 사용하고 객체 캡슐화는 합성을 사용한다.

15장

패턴은 아키텍처 패턴, 분석패턴, 디자인패턴, 이디엄으로 분류된다

디자인패턴- 특정 정황 내에서 일반적인 설계 문제를 해결하며 컴포넌트들 사이에서 반복적으로 발생하는 구조를 서술한다.
아키텍처 패턴- 디자인패턴의 상위로 소프트웨어의 정체적인 구조를 결정하기 위해 사용. 각 서브시스템들의 책임을 정의하며 서비시스템들 사이의 관계를 조직화하는 규칙과 가이드라인을 포함.
이디엄- 디자인패턴의 하위로 특정 프로그램언어에 국한된 하위레벨 패턴이다. 주어진 언어의 기능을 사용해 컴포넌트 혹은 컴포넌트 간의 특정 측면을 구현하는 방법을 서술한다.
분석패턴 - 도메인 내의 개념적인 문제를 해결하는데 초점을 맞춘다. 분석패턴은 업무 모델링 시에 발견되는 공통적인 구조를 표현하는 개념들의 집합이다.

제어의 역전 : 프레임워크가 어플리케이션에 속하는 서브클래스의 메서드를 호출한다.이를 제어의 역전 또는 할리우드 원리라고 한다. (할리우드에서 배우한테 내가 연락하기 전까지 먼저 연락하지마! 하는것)
프레임워크는 일반적인 해결책만 제공하고 애플리케이션에 따라 달라질 수 있는 특정한 동작은 비워둔다. 이를 훅이라고 부른다. 흑은 프레임워크 코드에서 호출하는 프레임워크의 특정 부분이다. 재정의된 훅은 제어 역전원리에 따라 원하는 시점에 호출된다.

/

Share