객체지향설계

코드스피츠 1강 오브젝트 3회차

iOS_Assin 2019. 10. 22. 05:36

책 오브젝트를 기반으로 하는 코드스피츠 강의 오브젝트 - 3회차 를 정리한 내용입니다.

 

1.코드스피츠 1강 오브젝트 1회차 (1)

2.코드스피츠 1강 오브젝트 1회차 (2)

3.코드스피츠 1강 오브젝트 2회차 

4.코드스피츠 1강 오브젝트 3회차 

5. 코드스피츠 1강 오브젝트 4회차 

6. 코드스피츠 1강 오브젝트 5회차

7. 코드스피츠 1강 오브젝트 6회차

 

객체간 통신

 

나의 데이터는 "돈" 이지만 협력의 메시지로 전달될 때는 "급여지급", "용돈 주기" 의 다양한 양상으로 나타난다.

 

돈(속성) == 캡슐화된 데이터
은닉
 (interface) == 아빠, 사장

책임(메소드) == 급여지급, 용돈 주기


돈(속성) == 캡슐화된 데이터
은닉
 (interface) == 아들, 사원

책임(메소드) == 월급받기, 용돈받기

 

 

현실세계 객체는 하나의 책임을 갖는 것은 불가능하다.

즉, 다양한 책임(메소드)을 가지고 있는 다양한 역할(인터페이스)을 수행하게 된다.

 

 

메시지통신은 어느 객체 책임이 다른 객체의 책임과 통신하는 것이다. 

 

 

// 아빠이라는 역할을 가짐
protocol 아빠 {
    func 용돈주기(t: 아들)
}

// 사장이라는 역할을 가짐
protocol 사장 {
    func 월급주기(t: 사원)
}

 

// 아들이라는 역할을 가짐
protocol 아들 {
    func 용돈받기(t: 아빠)
}

// 사원이라는 역할을 가짐
protocol 사원 {
    func 월급받기(t: 사장)
}

 

class 사람: 아빠, 사장 {
    // 캡슐화된 속성 돈
    private let money: Int = 0
    
    // 아들의 측면으로 메시지가 전달됨
    func 용돈주기(t: 아들) {
    	// 책임을 구현    
    }
    
    // 사원의 측면으로 메시지가 전달됨
    func 월급주기(t: 사원) {
		// 책임을 구현
    }
}


class 사람2: 아들, 사원 {
    // 캡슐화된 속성 돈
    private let money: Int = 0
    
    // 아빠의 측면으로 메시지가 전달됨
    func 용돈받기(t: 아빠) {
		// 책임을 구현
    }

    // 사장의 측면으로 메시지가 전달됨
    func 월급받기(t: 사장) {
		// 책임을 구현
    }
}

통신망의 구성

객체 통신은 동기 뿐만 아니라 비동기를 포함해서 다양한 다른 Type 사이에 대화하는 복잡한 형태로 되어있다. 

 

협력이라는 문맥안에서 객체망과 메시지의 조합으로 표현할 수 있는것이 객체 설계이다.

 

 

순환 객체망

일반적으로 순환하는 망이 생기면 잘못된 것이다.

일반적으로 망이 너무 방대하기 때문에 순환을 파악할 수 없다. 

 

코드 배치의 기술

의존성을 단반향으로 만드는 것이 목표이다.

 

인터페이스의 그룹화

수많은 관점(interface)에 따라 책임(method)이 도출된다. 

 

여러 관점을 수용하는 객체

자신이 소속되어 있는 관점마다 다양한 Interface 를 부분적으로 가지고 있다.

 

상호작용

객체망을 구성해서 서로 모순점이 생기거나 잘못된 Interface 발견되면 "인터페이스의 그룹화" 까지 영향을 미치게 된다. 

 

 

알려진 기본 설계 요령 (SOLID원칙)

SRP 단일책임원칙

변화의 이유를 하나만 가져야 한다. 

 

객체를 분리해서 만드는 이유가 변화율에 따라야 한다.

변화율이란 코드가 변화하는 시간적인 의미도 있지만 변화하는 이유도 포함하고 있다. (어렵다...)

 

오브젝트 책의 책임과 SOLID 책임은 다르다! 자세한 내용은 여기를 확인해주세요.

 

산탄총 수술

SRP 원칙을 위반했을 때 나타나는 현상

한가지 변화 이유로 코드를 수정하면서 수많은 클래스를 수정하게 되는 현상

 

OCP 개방폐쇄원칙

타입에서는 열려있고 변경에는 닫혀있다. 

포인터를 직접참조하지 말고 포인터의 포인터를 사용하자!

여기에서 내용을 확인할 수 있습니다.

 

LSP 리스코프 치환원칙

 

업캐스팅 안전이 되야 한다.

추상층의 정의가 너무 구체적이면 구상층의 구현에서 모순이 발생함.

(추상층이 너무 나대면 안된다;;;)

 

즉, 추상층은 구상층의 확장을 포용할 수 있는 교집합만 가지고 있어야 한다.

 

여기에서 내용을 확인할 수 있습니다.

 

ISP 인터페이스 분리 

구상형으로 쓰지말고 인터페이스로 분리해야함 

 

DIP 의존성 역전

구상층에 의존하지 말고 추상화 된 것에 의존해야 한다.

다운캐스팅을 하면 안된다.

 

SOLID 원칙 이외에 필요하다고 생각되는 원칙들

DI (ioC) - 의존성 역전

Dry 원칙 - 중복 방지

Hollywood 원칙 - 묻지말고 시켜라

디미터의 법칙 - 최소 지식(객체 내부 구조에 강하게 결합되지 않도록 협력 경로를 제한하는 것)

screening.getMovie().getDiscountConditions()

 

Hollywood 원칙, 디미터의 법칙 모순점

두 원칙을 지키면 해당 객체와 메시지를 주고 받는 것은 가능하나 내부를 들여다볼 방법이 없음 

객체 통신망 - 객체는 메시지를 주고 받는다. 

 

메시지를 정상적으로 수신되었는지? 어떻게 알 수 있을까? 

 

코드상으로 객체 간 통신은 Runtime Time 에서 일어나기 때문에 정상적으로 통신이 이루어졌는지는 확인할 수 없다. 

 

결국 객체가 제대로 작동하는가를 테스트하려면...?

1. 객체통신망에서 테스트할 객체에게 메시지를 보낸뒤

2. 그 객체가 이웃 객체에게 메시지를 잘 보냈는지 확인

3. 3번을 위해 통신한 이웃 객체를 조사하면 된다. 

 

즉, Mock 객체를 활용하여 Unit Test 를 작성하고 특정 함수가 호출되었는지를 검증해야함!

 

 

Mock 객체를 활용한 검증

예제 코드

 

 

 

전체 코드는 여기에서 확인할 수 있습니다.

 

 

GRASP

원칙수준 보다는 이렇게 하는 것이 좋다 라는 의미로 사용하면 된다.

정보 전문가 Infomation Expert (139 page)

해당 정보를 갖고 있는 객체에게 책임을 할당하라. 

객체의 본질과 데이터 은닉을 지킬 수 있는 패턴

 

소유권한 Creator (145 page)

객체 시스템이 이질적인 부분인 생성 시에도 정보전문가 패턴을 따르자!

어떤 객체가 대상을 포함, 이용, 부분으로 삼거나 잘 알고있다면 그 대상을 생성하게 시키자.

 

컨트롤러 Controller

Mediator 패턴의 설계 판 확장으로 서비 시스템으로 묶을 수 있다면 컨트를러를 도입하자!

객체들은 더 이상 다른 객체와 서로 직접 통신하지 않으며 대신 중재자를 통해 통신한다.

 

낮은 결합도 Low Coupling (143 page) & 높은 응집도 High Cohesion (144 Page)

결합도를 낮추려면 아는 객체 수를 줄여야 함.

하지만 책에서는 모순점을 가지고 있다.

"적절한 객체를 알고 있을때 Low Coupling" 이라고 부른다. 

우리는 적절한 객체를 알 수 없지만 알아야할 객체를 알 순 있다. 

알아야할 객체간 의존성에서  중요한 것은 단방향의 의존성이다.

 

응집도를 높이려면 객체를 도출할 떄부터 변화율을 고려해야 함.

변화 보호 Protected Variations (159 page)

추상적인 수준에서 책임을 정의하여 다양한 구상가능성으로부터 사용할 모듈을 보호하라.

 

다형성 Polymorphism  (158 page)

전략 패턴처럼 분기가 예상되는 책임이라면 다형성을 이용하라

 

순수 조립 Pure Fabrication (292 page)

공통된 기능이나 순수 기능적인 객체는 따로 모아서 작성한다. 

 

간접 참조 Indirection

SOLID 의 OCP 원칙을 이야기하는 것이다.

직접 참조관계를 피하고 중계 객체를 이용하면 개별 객체의 충격을 중계 객체에서 흡수할 수 있다.