04. 역할, 책임, 협력

리뷰기간: 2024년 10월 28일 → 2024년 11월 3일
태그: 작성완료

✨ 내용 요약

  • 협력
    • 객체가 협력하는 이유 : 동일한 목적을 이루기 위함
    • 1장의 내용처럼 협력관계를 통해 동일한 목적을 달성하기 위해 메세지 ( 요청 ) 과 응답으로 협력 관계를 이룬다
  • 책임
    • 어떠한 요청에 응답을 하거나 적절한 행동을 취해야할 의무
    • 책임의 분류
      • 하는것 ( doing ) - 어떠한 행동을 취하는것
      • 아는것 ( knowing ) - 알고있는 어떠한 값을 할당하는것 ( ex. init 구문..? )
    • 메세지를 통해 협력관계에 있는 객체들은 책임을 가지고 있어야 한다
  • 객체지향의 여러 설계 기법
    • 책임주도설계(RDD) - 협력에 필요한 책임들을 식별하고 적합한 객체에게 책임을 할당
    • 디자인패턴 ( Design Pattern ) - 해결방법을 정의해 놓은 설계 템플릿 ( MVC, MVVM ..etc )
    • 테스트주도개발(TDD)
      • 테스트를 먼저 작성하고 테스트를 통과하는 구체적인 코드를 추가하며 Application을 완성해 가는 방식
      • 책임주도 설계를 통해 도달해야 하는 목적지를 테스트라는 안전장치를 통해 빠르고 견고하게 도달하도록함

📝 감상 및 리뷰

  • 1장부터 계속 나왔던 역할, 책임, 협력 내용이 반복적으로 나와 지루했다 그만큼 중요한 내용인것 같다
  • TDD 와 디자인패턴 등이 뭔지 알고 있었지만 이것이 객체지향의 개념인것도 처음 알게 되었다

🛠️ 실무/프로젝트 적용

  • 책임을 갖는 협력관계들의 객체들에게 적절한 역할을 부여하도록 코드를 작성해야 겠다
  • TDD는 꼭 한번 적용해야지 생각만 하고 적용해보지 못하였다, 프로젝트를 통해 꼭 구현 해보고싶다.

리뷰기간: 2024년 10월 21일 → 2024년 10월 27일
태그: 1회독, 작성완료

✨ 내용 요약

  • 사실적인 내용이 포함된 지하철 노선도는 오히려 승객들에게 불편함을 야기

    ⇒ 추상화를 통해 필요한 정보만 전달 ( 역과 역 사이의 순서, 환승 등등 )

  • 추상화란?

    • 좀더 명확한 정보를 이해하기 위해 특정 절차나 물체를 의도적으로 생략하거나 감춤으로써 복잡성을 극복
    • 추상화는 사물의 본질에 집중하여 복잡성을 극복
    • 첫 번째 차원 : 구체적인 사물들 간의 공통점은 취하고 차이점을 버리는 일반화하여 단순화
    • 두번째 차원 : 중요한 부분을 강조하기 위해 불필요한 세부 사항을 제거함으로써 단순화
  • 개념 이란?

    • 공통점을 기반으로 객체들을 묶기 위한 그릇
    • 객체를 여러 그룹으로 분류
    • 개념의 세가지 관점
      • 심볼 : 개념을 가리키는 간략한 이름 또는 명칭
      • 내연 : 개념의 완전한 정의, 객체가 개념에 속하는지 여부 파악
      • 외연 : 개념에 속하는 모든 객체의 집합
  • 객체의 분류

    • 분류란?
      • 객체의 특정한 개념을 적용하는 작업
      • 첫번째 차원 (일반화) : 하트 왕, 하트 왕비, 정원사, 병사 등을 트럼프라는 개념으로 묶어 객체 간의 차이를 무시하고 공통점만 취함
      • 두번째 차원 (불필요한 세부사항 제거) : 트럼프 객체 집합의 공통점 중에서도 중요한 특징은 몸이 납작하고 네모난 모서리에 팔 다리가 달려있다는 것이다. 그 외의 사항들은 문맥상 어떤 도움도 되지 않기 때문에 불필요한 세부 사항을 제거
  • 타입

    • 타입 = 개념
    • 데이터 타입
      • 메모리 내에 저장된 데이터의 종류를 분류하는데 사용하는 메모리 집합에 관한 메타데이터
      • 데이터에 대한 분류는 암시적으로 어떤 종류의 연산이 해당 데이터에 대해 수행될 수 있는지 결정
    • 객체의 타입
      • 어떤 객체가 어떤 타입에 속하는지 결정하는 것은 객체의 행동, 어떤 객체들이 동일한 행동을 수행할 수 있다면 동일 타입으로 분류
      • 객체의 내부적인 표현은 외부로부터 철저하게 감춰진다, 객체의 행동을 가장 효과적으로 수행할 수 있다면 객체 내부의 상태를 어떤 방식으로 표현해도 무방
  • 행동

    • 다형성 : 동일한 타입에 속한 객체는 내부의 데이터 표현 방식이 다르더라도 동일한 메시지를 수신하고 이를 처리할 수 있다. 다형성이란 동일한 요청에 대해 서로 다른 방식으로 응답할 수 있는 능력을 뜻한다. 동일한 메시지를 서로 다른 방식으로 처리하기 위해서는 객체들은 동일한 메시지를 수신할 수 있어야하기 때문에 결과적으로 다형적인 객체들은 동일한 타입(또는 타입 계층)에 속하게 된다.
    • 캡슐화 : 데이터 내부 표현 방식과 무관하게 행동만이 고려 대상이라는 사실은 외부에 행동만을 제공하고 데이터는 감춰야 한다는 것을 의미한다. 이 원칙을 흔히 캡슐화라고 한다. 공용 인터페이스 뒤로 데이터를 캡슐화하라는 격언은 객체를 행동에 따라 분류하기 위해 지켜야 하는 기본적인 원칙이다. 데이터가 캡슐의 벽을 뚫고 객체의 인터페이스를 오염시키는 순간 객체의 분류 체계는 급격히 위험에 노출되고 결과적으로 유연하지 못한 설계를 낳는다.
    • 책임 주도 설계(Responsibility-Driven Design) : 객체가 외부에 제공해야 하는 행동을 먼저 생각해야 한다. 이를 위해서는 객체가 외부에 제공해야 하는 책임을 결정하고 그 책임을 수행하는 데 적합한 데이터를 나중에 결정한 후, 데이터를 책임을 수행하는 데 필요한 외부 인터페이스 뒤로 캡슐화해야 한다. 이러한 설계 방법에 대해 프레임워크를 제공하는 것이 책임 주도 설계이다.
  • 타입의 계층

    • 일반화 : 포괄적인 의미를 내품는 일반적인 개념 ( 트럼프 )
    • 특수화 : 일반적인 개념보다 범위가 더 좁은 특수한 개념 ( 트럼프 인간 )
    • 슈퍼타입과 서브타입
      • 슈퍼타입 : 일반적인 타입
      • 서브타입 : 특수한 타입
  • 정적 모델

    • 타입은 시간에 따라 동적으로 변하는 객체의 상태를 시간과 무관한 정적인 모습으로 다룰 수 있게 해줌
    • 객체의 상태에 복잡성을 부과하는 시간이라는 요소를 제거하여 시간에 독립적인 정적 모습으로 객체를 생각할 수 있게 해줌
    • 타입은 시간에 따른 객체의 상태 변경이라는 복잡성을 단순화할 수 있는 효과적인 방법
  • 동적 모델과 정적모델

    • 동적모델 : 객체가 특정 시점에 구체적으로 어떤 상태를 가지는 지 표현하는 스냅샷처럼, 객체가 살아 움직이는 동안 상태가 어떻게 변하고 어떻게 행동하는지를 포착하는 것
    • 정적모델 ( 타입 모델 ) : 객체가 가질 수 있는 모든상태와 모든 행동을 시간에 독립적으로 표현하는 것
  • 클래스와 타입은 동일하지 않다

  • 타입은 객체 분류를 위해 사용되는 개념이고, 클래스는 타입을 구현할 수 있는 여러 매커니즘 중 하나일 뿐

📝 감상 및 리뷰

  • 내용이 점점 딥해진다고 생각 들음
  • 타입을 행동 중심으로 바라보며 클래스와의 차이를 명확히 이해할 수 있었.
  • 객체지향에서 설계의 근본을 다시 생각하게 해주었습니다

🛠️ 실무/프로젝트 적용

  • 행동 중심의 타입 설계를 통해 프로젝트의 유지보수를 용이하게 하고, 코드의 유연성과 확장성을 확보

02. 이상한 나라의 객체

리뷰기간: 2024년 10월 14일 → 2024년 10월 20일
태그: 1회독, 작성완료

✨ 내용 요약

  • 객객체지향 패러다임의 목적
    • 현실세계를 모방하는것이 아니라 현실 세계를 기반으로 새로운 세계를 창조
  • 상태 와 행동은 상호 의존적이다
    • 상태를 알아야 행동을 할 수 있음
    • 행동의 결과는 상태에 따라 달라짐
  • 서로 다른 객체가 협력을 하기 위해서는 연결(링크) 되어 있어야 함
  • 객체의 상태
    • 프로퍼티(property) : 변경되지 않는 고정되어 있는것 ⇒ 정적(Static)
      • 단순한 값인 속성과 다른 객체를 가리키는 링크의 조합으로 표현
      • 키, 음료의 양, 위치
    • 프로퍼티 값 (property value) : 변경될수 있는 “값” ⇒ 동적(Dynamic)
      • 175cm, 50ml, 대한민국
  • 캡슐화의 개념
    • 자신의 상태를 스스로 관리하는 자율적인 객체
    • 객체는 상태를 캡슐안에 감춰둔 채 외부로 노출하지 않음
    • 객체의 행동을 유발하는 것은 외부로부터 전달된 메시지 이지만, 상태를 변경할지 여부는 객체 스스로 결정
  • 객체와 값의 차이
    • 객체 : 고유 식별자를 이용해 동등함 판별
      • 객체의 상태가 달라도 식별자가 같으면 동등
    • 값 : 상태를 이용해 동등함 판별
      • 식별자가 다르더라도 상태가 같으면 동등
  • 블랙박스의 개념
    • 조회를 의미하는 “쿼리”
    • 행동을 지시하는 “명령”
    • 명령을 할때 쿼리를 바로 보여주지 않아야함, 각각 독립적으로 존재
  • 상태를 먼저 결정하고 행동을 나중에 결정하면 안된다
    • 상태를 먼저 결정할 경우 캡슐화 저해
    • 객체를 협력자가 아닌 고립된 섬으로 만듦
    • 객체의 재사용성 저하

📝 감상 및 리뷰

  • 이상한 나라의 앨리스의 이야기로 객체의 특성 ( 상태, 행동, 책임, 상태와 행동의 의존성, 링크 등등을 설명하여 이해가 잘됨 )
  • 상태와 행동은 상호 의존적임을 알게됨
  • 사이드이펙트(Side Effect) 라는 단어를 알게모르게 쓰고있었지만 정확이 어떤뜻인지는 몰랐는데 이번 장을 읽으며 어떤 현상인지 알게됨

🛠️ 실무/프로젝트 적용

  • 캡슐화 + 블랙박스 공부

      class Calcurator {
          private var first: Double = 0
          private var second: Double = 0
    
          private var number: Double = 0
    
          func add(_ first: Double, _ second: Double) {
              self.first = first
              self.second = second
    
              self.number = first + second
          }
    
          func queryNumber() -> Double {
              return self.number
          }
      }
    
      let calcurator = Calcurator()
    
      print(calcurator.queryNumber()) // 0.0
      calcurator.add(1, 2)
      print(calcurator.queryNumber()) // 3.0

    ⇒ first, second, number 는 외부에 노출될 수 없음

    ⇒ add 함수를 이용해 (요청) 행동 요청 → 행동 요청의 결과를 바로 볼 수 없음

    ⇒ queryNumber 함수를 통해서만 number의 상태 를 조회 할 수 있음

  • 객체와 값의 비교

      class Calcurator {
          private var number: Double = 0
    
          func queryNumber() -> Double {
              return self.number
          }
      }
    
      let calcurator = Calcurator()
      let calcurator2 = Calcurator()
    
      print(calcurator === calcurator2) 
      // false => "식별자"가 다름
    
      print(calcurator.queryNumber() == calcurator2.queryNumber())
      // true => "식별자"가 다르지만 "상태"가 같음
    

실무에서 적용된 예시

CustomPopup을 띄우기 위해 하기 코드를 작성 한 경험이 있음

// customPopup띄우는 한 객체의 일부 코드
class PopupView {
// ... BlackBox

func showPopupSingleButton(on view: UIView, message: String, confirmAction: @escaping () -> Void = { return }) {
        if singleButtonPopup == nil {
            singleButtonPopup = SingleButtonPopupView(frame: .zero)
        }

        singleButtonPopup?.messageLabelSetting(message: message)
        singleButtonPopup?.show(on: view)
        singleButtonPopup?.confirmAction = confirmAction
    }

  // ... BlackBox
}



// 사용하는 쪽
class MainViewController {
// ... BlackBox

PopupView.showPopupSingleButton(on: self?.view ?? UIView(), message: "다시 시도해 주세요.")

// ...BlackBox

}
  • MainViewController 는 함수(메세지)를 통해 popupView객체에게 “다시 시도해 주세요.” 라는 행동을 요청함
  • showPopupSingleButton() 함수는 메세지를 수신한 후 상태를 결정(팝업을 띄움) 함
  • 위의 과정에서 MainViewController는 “다시 시도해 주세요.” 문구의 팝업을 띄워줘 라고 요청을 한 후, 그에 대한 결과(상태)에 대해 관여를 하지 않음 PopupView는 요청메세지를 수신한 후 내부에서 자율적으로 자신의 상태를 변경하고 요청에 대한 상태를 결정함

🔍 추가 자료

  • 함수에서 리턴값으로 변경된 “상태”를 바로 읽어드리는 것은 올바른 객체지향적 설계가 아닌것인가?
  • RDD(책임-주도 설계_Responsibility-Driven Design)

01. 협력하는 객체들의 공동체

리뷰기간: 2024년 10월 14일 → 2024년 10월 20일
태그: 1회독, 작성완료

✨ 내용 요약

  • 일상생활에서 찾은 객체지향적 요소

  • 손님, 캐시어, 바리스타는 각각 역할 에 해당하며 각 역할은 책임 을 가져야 한다.

  • 요청과 응답의 상호작용으로 객체지향적 설계를 설명할 수 있다.

  • 캐시어는 1명일수도, 여러명일수도 있다 마찬가지로 바리스타도 1명일수도 여러명일수도 있다

    ⇒ 반드시 하나의 객체 만 동일한 역할책임지는것은 아니다.

  • 하나의 객체가 여러일을 수행 할 수 있다

    • 캐시어가 주문을 받고, 동시에 직접 커피를 제조해서 손님에게 전달할 수 있다 ⇒ 대체가능성
  • 책임을 수행하는 방법은 각 객체가 자율적으로 선택할 수 있다

    • 예를들어, 아이스아메리카노를 주문할때, 손님이 직접 “얼음을 먼저 넣으시구요 물을 반만 채운 다음 에스프레소를 넣고 나머지 물을 채워주세요” 와 같은 주문을 하지 않아도, 바리스타 스스로 얼음을 먼저 채울수도, 물을 먼저 채울수도, 에스프레소를 먼저 채울수 도 있다.
  • 역할, 책임, 그리고 협력

    • 각 객체는 메세지를 통해 소통하며, 메세지의 요구조건을 수행하는 것을 메서드라고 표현할 수 있다.
    • 객체들은 상호 협력적인 관계를 가져야한다, 또한 특정 역할을 수행하는 객체는 역할에 대한 책임을 가져야한다.
  • 객체의 자율성

    • 객체들은 서로 무엇을 수행하는지는 알수 있으나 어떻게 수행하는지는 몰라야한다.
  • 객체지향 ≠ 클래스

    • 클래스는 객체들의 협력 관계를 코드로 옮기는 도구에 불과
  • 객체지향의 본질 Point!

    1. 상호작용하는 자율적인 객체들의 공동체로 시스템을 바라보고, 객체를 이용해 시스템을 분할
    2. 자율적인 객체란 상태행위를 포함해 스스로 자기자신을 책임지는 객체를 의미
    3. 객체는 다른 객체와 협력하며, 각 객체는 협력 내에서 정해진 역할책임을 갖고 수행해야한다
    4. 객체들간의 협력은 메세지를 통해 이루어지고, 메세지를 수신한 객체는 메세지 처리를 위해 적합한 메서드를 자율적으로 선택한다.

📝 감상 및 리뷰

  • 객체지향 = 클래스 로 인식했던 과거를 반성하게 됨
  • 객체지향에 대한 이론적 지식이 잡혀있지 않은 상태로 개발을 해왔으나, 왜 그러한 구조로 개발을 수행되어야 하는지 이해가 되었음

🛠️ 실무/프로젝트 적용

  • 대략적인 공부

      class Calcurator {
          let a: Int
          let b: Int
    
          init(a: Int, b: Int) {
              self.a = a
              self.b = b
          }
    
          func add() -> Int {
              return a + b
          }
    
          func divide() -> Double {
              return a << b
          }
    
          func minus() -> Int {
              return ((a * 3) - (b * 3)) / 3
          }
      }
    
      class MainClass {
          let cal = Calcurator(a: 1, b: 3)
          print(cal.minus) // -2
      }

    코드를 보면, MainClass는 a-b 를 수행한다고 생각 할 수 있지만, 실제로 minus 함수를 보면 각 수에 3을 곱하고 뺀다음 3으로 나눈다, 하지만 아무 문제없다.

    → MainClass 입장에서 cal.minus는 a 에서 b 값을 뺀다(무엇) 는 것은 알수 있지만 어떻게 계산하는지는 모른다 ⇒ 객체의 자율성

+ Recent posts