ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift4: Protocol Oriented Programming - 7.스위프트에서 디자인 패턴 적용 - (3) 행위 패턴
    POP 2021. 2. 24. 21:06

    행위 패턴

    타입 간에 상호작용이 어떻게 이뤄지는 지 설명. 어떠한 일을 하기 위해 어떻게 서로 다른 타입의 인스턴스 간에 메세지를 보내는지 설명.

    • 책임 연쇄

      다른 핸들러에 위임돼 있을지 모르는 다양한 요청을 처리

    • 커맨드

      나중에 다른 컴포넌트에 의해 실행될 수 있게 행동이나 매개변수를 캡슐화한 객체를 생성

    • 이터레이터

      근본적인 구조는 노출시키지 않고, 객체의 요소에 연속적으로 접근할 수 있도록 한다.

    • 미디에이터

      서로 정보를 전달하는 타입 간의 결합도를 줄인다.

    • 메멘토

      객체의 현재 상태를 캡쳐하고 나중에 복구할 수 있게 객체를 얼마동안 저장

    • 옵저버

      객체의 변경 상태를 알린다. 다른 객체는 이러한 변경 사항에 대한 알림을 받기 위해 이를 구독할 수 있다.

    • 스테이트

      내부 상태가 변경될 경우 객체의 행동을 변경하기 위해 사용

    • 스트래티지

      런타입에 알고리즘 계열 중 하나를 선택하게 함

    • 비지터

      객체 구조로부터 알고리즘을 분리

    커맨드 디자인 패턴

    사용자에게 나중에 실행할 수 있는 행동을 정의하도록 요구. 나중에 호출하거나 행동을 해야하는 모든 정보를 캡슐화한다. 실행하는 커맨드를 런타임에 설정 가능하며, 애플리케이션 생애 동안 커맨드를 프로토콜을 따르는 다른 구현체로 변경할 수 있다.

    • 문제

      커맨드 실행과 호출자를 서로 분리하는 경우. 여러 행동 중 하나를 수행해야만 하는 타입이나 사용해야하는 행동을 선택하는 시점이 런타임에 일어나는 경우.

    • 해결

      다양한 행동에 대한 로직을 커맨드 프로토콜을 따르는 분리된 타입으로 캡슐화한다. 호출자에게 커맨드 타입의 인스턴스를 제공, 필요한 행동을 실행하기 위해 프로토콜에서 제공하는 인터페이스를 사용한다.

    protocol MathCommand {
        func exec(num1: Double, num2: Double) -> Double
    }
    
    // MathCommand를 채택하는 타입
    struct AdditionCommand: MathCommand {
        func exec(num1: Double, num2: Double) -> Double {
            return num1 + num2
        }
    }
    
    // 호출자 
    struct Calculator {
        func performCalculation(num1: Double, num2: Double, command: MathCommand) -> Double {
            return command.exec(num1: num1, num2: num2)
        }
    }
    
    var calc = Calculator()
    var startValue = calc.performCalculation(num1: 25, num2: 10, command: SubtractionCommand())

    스트래티지 패턴

    호출하는 타입으로부터 자세한 구현 사항 분리, 런타입에서 구현체 교체 가능. 커맨드 패턴과 차이점은 알고리즘을 캡슐화하는 경항이 있다는 것이다. 스트래티지 패턴에서 알고리즘을 바꿔 객체가 같은 기능을 다른 방법으로 수행한다면, 커맨드 패턴은 커맨드를 바꾸면 객체가 객체의 기능을 바꾼다.

    • 문제

      애플리케이션에서 백엔드 알고리즘을 변경하는 경우. 같은 작업을 수행하는 다른 여러 알고리즘을 가진 타입이 있고, 알고리즘의 선택이 런타입에 이뤄지는 경우.

    • 해결

      스트래티지 패턴을 따르는 타입에 있는 알고리즘을 캡슐화. 호출자에게 사용하고자 하는 스트래티지 타입의 인스턴스를 제공.

    protocol CompressionStrategy {
        func compressFiles(filePaths: [String])
    }
    
    struct ZipCompressionStrategy: CompressionStrategy {
        func compressFiles(filePaths: [String]) {
            print("Using Zip Compression")
        }
    }
    
    struct CompressContent {
        var strategy: CompressionStrategy
    
        func compressFiles(filePaths: [String]) {
            strategy.compressFiles(filePaths: filePaths)
        }
    }
    
    var compress = CompressContent(strategy: zip)
    compress.compressFiles(filePaths: filePaths)
    
    compress.strategy = rar
    compress.compressFiles(filePaths: filePaths)

    옵저버 패턴

    다른 타입에서 이벤트가 발생하는 경우 옵저버 타입이 알림을 받는 경우와 같이 광범위한 이벤트 처리를 구현한다.객체 그룹 사이의 의존성을 거의 갖지 않으면서 서로 협력 가능하게 한다.

    • 문제

      이벤트 발생, 코드의 한 부분 혹은 그 이상의 부분에서 행동을 수행해야 하는 경우. 대부분의 모던 UI 프레임워크

    • 해결

      이벤트를 전달 받기 위한 옵저버를 등록. 이벤트가 발생하면 이벤트를 등록한 인스턴스는 알림을 받는다.

    1. NotificationCenter 이용

      알림을 등록하거나 발신 또는 수신 제공. 코코아 혹은 코코아 터치 기반의 애플리케이션은 기본적으로 알림 센터를 하나 가진다. 알림의 이름을 제공해야 한다. 되도록 정적 상수로 이름을 정의하자.

       class PostType {
           let nc = NotificationCenter.default
      
           func post() {
               nc.post(name: Notification.Name(NCNAME), object: nil)
           }
       }
      
       class ObserverType {
           let nc = NotificationCenter.default
      
           init() {
               nc.addObserver(self, selector: #selector(receiveNotification(notification:)), name: Notification.Name(NCNAME), object: nil)
           }
      
           @objc func receiveNotification(notification: Notification) {
               print("Notification Received")
           }
       }
      
       postType.post()
    2. 프로토콜을 따르는 타입의 인스턴스를 등록

      단일 옵저버에서 사용된다. 다수의 옵저버가 필요한 경우 옵저버를 배열로 가지고 순회하며 함수를 실행해야 하는데 비효율적이다.

       protocol ZombieObserver {
           func turnLeft()
           func turnRight()
           func seesUs()
       }
      
       class MyObserver: ZombieObserver {
           func turnLeft() {
               print("Zombie turned left, we move right")
           }
      
           func turnRight() {
               print("Zombie turned right, we move left")
           }
      
           func seesUs() {
               print("Zombie sees us, RUN!!!!")
           }
       }
      
       struct Zombie {
           var observer: ZombieObserver
      
           // 보통 observer의 함수를 호출할 때에는 새로운 쓰레드에서 한다!
           func turnZombieLeft() {
               // 왼쪽으로 돌고
               // 옵저버에게 알린다.
               observer.turnLeft()
           }
      
           func turnZombieRight() {
               // 오른쪽으로 돌고
               // 옵저버에게 알린다.
               observer.turnRight()
           }
      
           func spotHuman() {
               // 사람을 추적
               // 옵저버에게 알린다.
               observer.seesUs()
           }
       }

     

    코드

    https://github.com/A-by-alimelon/Swift-ProtocolOrientedProgramming/blob/main/ProtocolOrientedProgramming/ProtocolOrientedProgramming/DesignPattern-Behavioral.swift

     

    A-by-alimelon/Swift-ProtocolOrientedProgramming

    Contribute to A-by-alimelon/Swift-ProtocolOrientedProgramming development by creating an account on GitHub.

    github.com

    참고 문헌

    https://book.naver.com/bookdb/book_detail.nhn?bid=14107671

     

    스위프트 4

    스위프트가 해마다 발전하면서 프로토콜지향 프로그래밍 역시 계속해서 변화하고 있다. 스위프트 2에서 처음으로 언급된 프로토콜지향 프로그래밍은 스위프트가 3, 4로 업데이트 되면서 클래스

    book.naver.com

     

    댓글

Designed by Tistory.