본문 바로가기
Xcode/Swift - PlayGround

PlayGround) Method Chaining와 Optional Chaining이란!

by 후르륵짭짭 2021. 2. 13.
728x90
반응형

 

안녕하세요!! 후르륵짭짭 입니다!

이번에는 Method Chaining 이라는 것에 대해 알아보려고 합니다!

옵셔널 체이닝에 대해서 공부하다가 같이 알게 된 것인데, 정말 유용하게 사용 할 수 있을 겁니다!

 

** Chainning ** 

일단 Chaining이 무엇이냐 하면!

말 그대로 연결성을 가진 겁니다.

예를 들어 아래 처럼 사용 하는 것을 말 합니다 ㅎㅎ

let result = Array("name").map({String($0)}).filter({$0 == "n"})

전 이런 코드를 직접 만들어 보고 싶었습니다 ㅎㅎㅎ

저렇게 하나씩 쭉 연결해서 결과를 반환해주고 그걸 받아서 다시 결과를 반환하고,,,, 이런 코드가 멋있다고 생각 했었습니다.

 

** 전체 코드 ** 

//MARK : Optional Chaning

class Room{
    var number : Int
    
    init(number : Int){
        self.number = number
    }
    
    enum Kind{
        case negative
        case positive
        case zero
    }
    
    func kindOfnumber() -> Kind{
        switch number {
        
        case 0 :
            return .zero
        
        case let x where x % 2 == 0:
            print(x)
            return .negative
            
        default:
            return .positive
        }
    }
    
}

extension Room.Kind{

    var isRoomEmpty : Bool {
        
        switch self {
        case .negative:
            return false
        default:
            return true
        }
        
    }
    
}


class Building{
    var name : String
    var detail : String?
    var room : Room?
    
    init(name : String, detail : String?){
        self.name = name
        self.detail = detail
    }
    
    private func isRoomExist() -> Bool {
        return room == nil ? false : true
    }
    
    func resetRoom(number : Int) -> Room {
        
        let newRoom = Room(number: number)
        self.room = newRoom
        
        return self.room!
        
    }
    
    func makeRoom(number : Int) -> Room {
        
        if isRoomExist() {
            return self.room!
        }
        
        let newRoom = Room(number: number)
        self.room = newRoom
        
        return self.room!
        
    }
}

struct Address{
    
    var province : String
    var city : String
    var building : Building?
    
    init(province : String , city : String){
        self.province = province
        self.city = city
    }
    
    //이렇게 반환형을 특정 타입으로 해주면 연결형으로 사용 할 수 있다.! 개꿀!
   mutating func makeBuilding(name : String, detail : String?) -> Building{
        
        if self.building != nil {
            return self.building!
        }
        
        let newBuilding = Building(name: name, detail: detail)
        
        self.building = newBuilding
        
        return self.building!
    }
    
    func fullAddress() -> String? {
        
        //이 부분이 옶셔널 체이닝이다.
        //꾸준히 확인해서 존재하면 패스하고 그게 아니면 멈추게 된다.
        guard let buildingExist = building?.room else {return nil}
        
        return "\(province) + \(city) + \(building!.name) + \(buildingExist.number)"
        
    }
    
}

class Infomation{
    var name : String
    //이렇게 해서 연결형 구조를 만들 수 있다.
    var address : Address?
    
    init(name : String){
        self.name = name
    }
}

let chapchap : Infomation = Infomation(name: "ChapChap")

chapchap.address = Address(province: "대한민국", city: "서울")

chapchap.address?.makeBuilding(name: "은평뉴타운", detail: "629").makeRoom(number: 1202).number
chapchap.address?.makeBuilding(name: "청구아파트", detail: "420").resetRoom(number: 629).kindOfnumber().isRoomEmpty
chapchap.address?.fullAddress()
chapchap.address?.fullAddress()?.isEmpty


 

** 하나씩 분석하기 ** 

일단 결과 부터 보도록 하겠습니다.

//Infomation 인스턴스인 ChapChap 생성
let chapchap : Infomation = Infomation(name: "ChapChap")

//chapchap의 주소 인스턴스 생성
chapchap.address = Address(province: "대한민국", city: "서울")

//Chain 적용
chapchap.address?.makeBuilding(name: "은평뉴타운", detail: "629").makeRoom(number: 1202).number
//결과 : 1202

chapchap.address?.makeBuilding(name: "청구아파트", detail: "420").resetRoom(number: 629).kindOfnumber().isRoomEmpty
//결과 : true

chapchap.address?.fullAddress()
//결과 : "대한민국 + 서울 + 은평뉴타운 + 629"

chapchap.address?.fullAddress()?.isEmpty
//결과 : false

우리가 위에서 해보고 싶었던 체이닝을 만들 었습니다.

방법은 간단합니다.

인스턴스가 클래스던지 구조체인지 상관 없이, 그 인스턴스에 다른 인스턴스의 프로터티나 메소드가 있으면 됩니다.

말을 잘 설명 못 한 것 같은데,,, 아래 처럼 해주는 것을 의미합니다.

struct Address{
    
    var province : String
    var city : String
    var building : Building?
    
    init(province : String , city : String){
        self.province = province
        self.city = city
    }
    
    //이렇게 반환형을 특정 타입으로 해주면 연결형으로 사용 할 수 있다.! 개꿀!
   mutating func makeBuilding(name : String, detail : String?) -> Building{
        
        if self.building != nil {
            return self.building!
        }
        
        let newBuilding = Building(name: name, detail: detail)
        
        self.building = newBuilding
        
        return self.building!
    }
    
    func fullAddress() -> String? {
        
        //이 부분이 옶셔널 체이닝이다.
        //꾸준히 확인해서 존재하면 패스하고 그게 아니면 멈추게 된다.
        guard let buildingExist = building?.room else {return nil}
        
        return "\(province) + \(city) + \(building!.name) + \(buildingExist.number)"
        
    }
    
}

이 Address 구조체에 Building 인스턴스를 가지는 building 프로퍼티가 있고 

Building을 반환하는 makeBuilding() 메소드와 String을 반환하는 fullAddress()가 있습니다.

이렇게 특정 타입으로 반환하거나 프로퍼티에 접근하면 

그 타입으로 이동하게 되고 거기에 있는 기능을 모두 사용 할 수 있게 됩니다.

 

class Infomation{
    var name : String
    //이렇게 해서 연결형 구조를 만들 수 있다.
    var address : Address?
    
    init(name : String){
        self.name = name
    }
}

이렇게 Infomation이 있을 때, 아래 처럼 Infomation을 생성하고 Address도 생성해줍니다.

let chapchap : Infomation = Infomation(name: "ChapChap")

chapchap.address = Address(province: "대한민국", city: "서울")

그러면 이제 Infomation => Address로 접근이 가능해 집니다.

결국 이런 방법으로 Chain을 연결 시키면 Chain형 구조를 만들 수 있게 됩니다.

 

** 중첩 구조와 체이닝**

class Room{
    var number : Int
    
    init(number : Int){
        self.number = number
    }
    
    enum Kind{
        case negative
        case positive
        case zero
    }
    
    func kindOfnumber() -> Kind{
        switch number {
        
        case 0 :
            return .zero
        
        case let x where x % 2 == 0:
            print(x)
            return .negative
            
        default:
            return .positive
        }
    }
    
}

extension Room.Kind{

    var isRoomEmpty : Bool {
        
        switch self {
        case .negative:
            return false
        default:
            return true
        }
        
    }
    
}

이렇게 Room 클래스에 Enum이 들어 있는 것을 알 수 있습니다.

이런 것을 중첩구조라고 부릅니다.

그런데 여기서 중첩 구조일 뿐만 아니라 채이닝 구조로 작성 되어 

kindOfNumber() 함수가 Enum인 Kind를 반환하기 때문에 

우리는 Kind Enum이 가지는 기능들에 접근이 가능해 집니다.

그리고 Extension을 이용해서 Room 클래스 내부에 Kind Enum을 확장 해줍니다.

extension Room.Kind{

    var isRoomEmpty : Bool {
        
        switch self {
        case .negative:
            return false
        default:
            return true
        }
        
    }
    
}

이렇게 해주면 중첩 구조면서 채이닝 구조를 가지도록 만들 수 있게 됩니다.

chapchap.address?.makeBuilding(name: "청구아파트", detail: "420").resetRoom(number: 629).kindOfnumber().isRoomEmpty

이렇게 사용 할 수 있게 되는 겁니다.

 

** 옵셔널 채이닝 **

옵셔널 채이닝이란, 하나씩 옵셔널을 바인딩 해주는 것이 아니라 한번에 바인딩해서 목표 지점 까지 가는 것을 의미합니다.

struct Address{
    
    var province : String
    var city : String
    var building : Building?
    
	... 생략 ...
    
    func fullAddress() -> String? {
        
        //이 부분이 옶셔널 체이닝이다.
        //꾸준히 확인해서 존재하면 패스하고 그게 아니면 멈추게 된다.
        guard let buildingExist = building?.room else {return nil}
        
        return "\(province) + \(city) + \(building!.name) + \(buildingExist.number)"
        
    }
    
}

이렇게 building이 존재하고 거기에 room도 모두 존재하니??? 의미를 가지는 것을 옵셔널 채이닝이라 합니다.

중간에 값이 없을 경우에는 nil을 반환하게 됩니다.

 - 중간에 값이 없을 경우

let chapchap : Infomation = Infomation(name: "ChapChap")

chapchap.address = Address(province: "대한민국", city: "서울")

if let exist = chapchap.address?.building?.room {
    print(exist)
}
else{
    print("중간에 값이 없는 것이 있습니다.")
}

//결과 : 중간에 값이 없는 것이 있습니다.

- 모두 값이 존재 할 경우

let chapchap : Infomation = Infomation(name: "ChapChap")

chapchap.address = Address(province: "대한민국", city: "서울")
chapchap.address?.makeBuilding(name: "은평뉴타운", detail: "629")

if let exist = chapchap.address?.building{
    print(exist.name)
}
else{
    print("중간에 값이 없는 것이 있습니다.")
}

 

참고 사이트 : 

- Method Chaning :

minsone.github.io/mac/ios/method-chaining-in-swift

 

[Swift]Method Chaining

Method Chaining 가끔 코드를 작성하다 보면 같은 대상에 대해 추가 또는 변경, 삭제 등에 대해 여러 번 해야 하는 경우가 있습니다. 아직 다른 언어들을 많이 다루지는 않았지만 javascript는 다음과 같

minsone.github.io

- 중첩 구조를 enum을 사용해서 :

stackoverflow.com/questions/45502698/how-to-declare-enums-in-swift-of-a-particular-class-type

 

How to declare enums in Swift of a particular class type?

I am trying to create a class and use that class as the type for my new enum like shown below. class Abc{ var age = 25 var name = "Abhi" } enum TestEnum : Abc { case firstCase case

stackoverflow.com

 

728x90
반응형

댓글