Unity 내일배움캠프 TIL 1002 | 옵저버 패턴 | SOLID 원칙

cheeseonrose·2023년 10월 2일
0

Unity 내일배움캠프

목록 보기
48/89
post-thumbnail

옵저버 패턴

개념

  • 객체 사이의 일대다(one-to-many) 의존성을 정의하는 패턴
  • 주체(Subject) 객체의 상태가 변경될 때, 이 주체에 의존하는 여러 개의 옵저버(Observer) 객체에 알림을 보내 업데이트할 수 있게 함
  • 구상 클래스(ConcreteSubject)는 여러 옵저버의 구상 클래스(ConcreteObserver)들을 목록에 추가(register), 삭제(remove) 할 수 있음
  • 주제의 데이터가 변경되면 목록의 옵저버들에게 알림(notify)을 보낼 수 있음
    이미지 출처

흐름

  1. 1개의 관찰 대상자(Subject)와 여러 개의 관찰자(Observer)로 구성
  2. 관찰 대상 Subject의 상태가 바뀌면 변경 사항을 옵저버한테 통보 (notifyObserver)
  3. 대상자로부터 통보를 받은 Observer는 값을 바꾸거나 삭제하는 등의 대응을 함 (update)
  4. Observer들은 언제든지 Subject의 그룹에서 추가/삭제될 수 있음 (구독/구독 해지)
    Subject 그룹에 추가되면 Subject로부터 정보를 전달받게 되고, 삭제되면 정보를 전달받지 못하게 됨

장점

  • 주제와 옵저버 간의 관계가 느슨하게 연결됨
    -> 주제는 옵저버를 알지만 구체적인 옵저버 클래스에 대한 정보는 모름
    -> 구체적인 구현 없이 새로운 옵저버를 쉽게 등록하거나 삭제할 수 있음
    -> 데이터 업데이트시 단순히 알림만 주면 된다!
  • 발행자의 코드를 변경하지 않고도 새 구독자 클래스를 도입할 수 있어 개방 폐쇄 원칙을 준수

단점

  • 구독자는 알림 순서를 제어할 수 없음
  • 너무 많은 옵저버 패턴은 코드의 복잡도를 증가시킴
  • 옵저버 객체를 등록 후 해지하지 않으면 메모리 누수 발생 가능

사용

  • 한정된 시간에 특정한 케이스에만 다른 객체를 관찰해야 하는 경우
  • 대상 객체의 상태가 변경될 때마다 다른 객체의 동작을 트리거해야 할 때
  • 한 객체의 상태가 변경되면 다른 객체도 변경해야 하지만 어떤 객체들이 변경되어야 하는지는 몰라도 될 때
  • MVC 패턴에서 사용
    • Model과 View의 관계는 Subject와 Observer의 역할
    • 하나의 Model에 복수의 View가 대응

출처



위에서 얘기가 나와서 잠깐 짚고 넘어가는 SOLID 원칙
출처는 전공 강의에서 배운 지식

SOLID Principle

  • 객체지향 개발 설계 원리
  • 좋은 객체지향 설계를 만들기 위한 기본 원칙들

단일 책임 원칙(Single Responsibility Principle

  • 하나의 클래스를 설계할 때 그 클래스가 하나의 책임을 지도록 해야 한다.
    -> 클래스의 독립성
  • 하나의 클래스가 가지는 데이터와 메서드의 범위는 하나의 변경 이유만을 가져야 한다.
    • 변경 이유 : 프로그램을 다 작성한 뒤에 이 클래스를 고쳐야 하는 이유가 오직 하나만 있어야 함
  • 가장 기본적이지만 직접 구현하기는 가장 어려운 원칙
  • 적용 방법
방법Case특징 및 예시
Extract Class한 클래스 안에 이의 구조를 변경하도록 하는 이유가 둘 이상 존재하는 경우ex: 학생 클래스 - 성적 정보, 재학 및 졸업 정보, 등록금 납입 정보
각 정보마다 따로 만들고 한 클래스 안에 다른 클래스들을 필드로 갖게 함 (== 구성)
(상속보다는 구성을 사용하는 것이 좋음)
Extract Superclass클래스를 나누고 보니 유사한 책임을 나눠 맡고 있는 경우 상위 클래스 추출ex: 대학 성적 정보와 대학원생 성적 정보 클래스 -> 성적 정보 클래스를 상속하도록 수정
두 정보 클래스의 구조가 비슷한데 따로 구현하게 되면 성적 관리 시스템은 둘 다 불러서 사용하게 됨
-> 하나의 변경 이유로 두 개의 클래스를 변경해야 함 (ex: 성적 카테고리 추가)
Shotgun Surgery흩어진 메서드들과 필드를 한 클래스로 합침
필요하다면 새 클래스를 추가
많은 비용을 요구하므로 최후의 수단으로 사용
(각각의 클래스에 의존하고 있는 다른 클래스들에도 변경이 전파되기 때문)

개방폐쇄의 원칙(OCP : Open Close Principle)

  • 코드를 고치지 않고 그 클래스의 기능을 확장할 수 있어야 한다.
  • 무엇에 대해 Open : 확장 (새로운 기능 추가)
  • 무엇에 대해 Close : 변경 (코드 변경 X)
  • 요구사항 변경이 발생했을 때, 기존 코드를 수정하는 대신 새 클래스를 만들어 붙이거나 상속 등을 통해 클래스를 재사용
  • 변경(확장) 대상과 불변 대상을 명확하게 구분 (클래스를 나눈다)
  • 변경 대상과 불변 대상 사이 인터페이스를 정의
  • 구상 클래스 대신 인터페이스를 통해 코드 작성
    ex: Strategy Pattern

리스코프 치환 원칙(LSP : The Liskov Substitution Principle)

  • 어떤 변수의 타입을 어떤 클래스로 선언했을 때, 이 클래스의 부모 클래스 형식을 가지고 있는 다른 모든 객체로 치환될 수 있어야 한다.
  • 모든 자식 클래스는 부모 클래스 대신 사용될 수 있어야 함
  • Programming in Interface : 구상 타입을 사용하지 말고 언제나(가능하다면) 인터페이스를 사용해서 프로그래밍해라
  • 적용 방법
    • 복수 객체가 같은 일을 한다면 둘을 하나의 클래스로 묶고 이들을 구분할 수 있는 필드 생성 (ex: 직사각형과 정사각형)
    • 같은 연산을 약간씩 다르게 한다면 공통 인터페이스를 만들고 이를 구현 (ex: 사각형과 원)
    • 두 개체가 공통 연산 외에 약간의 차이를 가진다면 상속을 통해 구현 (ex: 가득 찬 원과 빈 원)
  • 주의 : 부모 클래스의 메서드를 Override로 재정의할 때, 그 양상이 완전히 달라진다면 (LSP가 깨질 정도)
    -> 인터페이스를 이용해야 했거나
    -> 사실 부모-자식 사이가 아니었거나

인터페이스 분리의 원칙(ISP : Interface Segregation Principle)

  • 인터페이스를 만들 때 지나치게 커지지 않도록 해야 한다.
  • 불필요한 메서드를 강요하면 안 된다.
  • 자신이 사용하지 않을 인터페이스는 구현하지 말아라
    • 응집성과 연관됨
  • 하나의 일반적인 인터페이스보다는 여러 개의 구체적인 인터페이스가 낫다.
    • 인터페이스 단일 책임
    • 예 : Animal 인터페이스보다 Barkable, Walkable, Eatable 인터페이스가 낫다.
  • 적용 방법
    • 클래스 상속을 통한 인터페이스 분리
    • 상속 대신 위임을 사용
      예 : 퍼사드 패턴(Facade Pattern)

의존성 역전의 원칙(DIP : Dependency Inversion Principle)

  • 상위 개념 모듈이 하위 개념 모듈에 의존해서는 안 된다.
    (하위 개념이 바뀌어도 상위 개념은 변하지 않아야 함)
  • 할리우드 원칙, Inversion of Control, Dependency Injection 이라고도 함
  • Caller가 Callee에 의존 vs Callee가 Caller에 의존
    예 : 할리우드 배우(Callee)와 캐스팅 디렉터(Caller)가 있을 때, 디렉터가 교체되면 배우가 변경되어야 하는가? 혹은 배우가 바뀌면 디렉터가 교체되어야 하는가?
  • 하위 모듈은 상위 모듈의 변경을 요구해서는 안 됨
    예 : Audio 모듈이 Speaker 모듈의 speak() 메서드를 사용
    -> Speaker 모듈이 Headphone 모듈로 바뀌려면 Audio의 변경이 발생함



그럼 오늘도 야근하러 이만

끗 -

0개의 댓글