SOPT-HIG-WWDC / WWDC

WWDC 스터디
0 stars 0 forks source link

Swift의 디자인 프로토콜 인터페이스 #11

Open kimscastle opened 1 year ago

kimscastle commented 1 year ago
스크린샷 2023-04-28 오후 1 33 44

우선 위의 사진과같은 프로토콜들과 그 프로토콜을 채택하고 있는 구조체들이있다고 가정해보면 기본적으로 Animal프로토콜을 채택하고 있는 구조체는 무조건 Food프로토콜을 채택한 구조체를 리턴하는 함수를 동일하게 가지고있기때문에 이를 Animal 프로토콜에 구현해줄 수 있다

스크린샷 2023-04-28 오후 1 41 19

그러면 이렇게 나타낼 수 있는데 프로토콜에서 제네릭을 사용해야할 이유는 Egg나 Milk모두 Foold를 채택한 구조체이기때문이다 그렇기때문에 제네릭을 사용할수있다는 생각이들고 이를 protocol에서 사용하기때문에 associatedtype을 사용하면된다

그렇게되면 이 Animal프로토콜은 Food라는 프로토콜을 채택한 타입을 반환하는 함수를 구현해줘야한다는 규칙을 가지게된다

전체적인 구조의 다이어그램은 아래와같고

스크린샷 2023-04-28 오후 1 43 47

Chicken과 Cow의 다이어그램은 아래와같다

스크린샷 2023-04-28 오후 1 45 26 스크린샷 2023-04-28 오후 1 45 37

에를들어서 이런 Animal들이 여러종류있는 Farm이 있다고 하면 Farm은 Animal들의 list를 가지고있을텐데 잠깐 저번 wwdc영상을 복습하자면 여러개를 표현할 수 있는 키워드에는 someany 가 있었는데 some은 타입이 하나로 고정되는 반면 any는 타입이 어려개일수있다 즉, some 을 사용하면 Chicken만 들어있다거나 Cow만 들어있을수있지만 any 는 여러개가 들어있을수있다는 뜻이다

그래서 구현을 해보면 여러개의 animal이 들어가있고 이 animal들을 maping해서 각각의 fool타입을 채택하는 구조체를 반환한 list를 리턴하는 함수를 구현할 수 있다

스크린샷 2023-04-28 오후 1 53 49

associatedtype 을 리턴으로 하는 경우

any Food 타입은 associatedtype CommodityType의 '상한타입'이라고부르는데

  1. 상한타입 : 구체적이지않은 포괄적인 타입
  2. 하위타입 : 구체적인 타입

연관타입을 반환하는 함수를 호출하면 컴파일러는 타입소거를 사용하여 호출의 결과타입을 결정한다 CommodityType이라는 연관타입을 반환하는 함수인 produce를 실행하면 타입소거를 사용하여서 결과타입이 결정된다 any Animal은 컴파일 시점에서 구체적인 결과 타입을 알수없지만 상한타입의 하위타입이 들어간다는건 알 수가 있다 이때 Cow라면 반환될 Milk라는게 당연히 상한타입의 CommodityType인 any Food에 저장될수있다 any Animal의 하위타입은(구체적인타입은) 항상 CommodityType의 any Food의 하위타입을 반환하기때문에 모든 타입에 대해 안전하다는걸 알 수 있다

associatedtype 을 input으로 하는 경우

하지만 반대로 연관타입을 메소드나 생성자의 매개변수의 목록에 넣는경우는 어떤지 살펴보자

스크린샷 2023-04-28 오후 2 17 47

이 경우에는 문제가 조금 발생하는데 변환이 반대방향으로 진행되기때문에 타입소거를 할 수 없게된다

스크린샷 2023-04-28 오후 2 19 58

Cow 를 저장하고있는 상한타입인 any Animal이있고 만약에 Cow에게 맞는 타입이 Hay라는 FeedType을 넣는다고 했을때 Animal프로토콜의 연관 FeedType의 상한타입은 any AnimalFeed이지만 임의의 any AnimalFeed가 주어질때 구체적인 Hay가 저장된다는걸 보장받을 수 없다. 그렇기때문에 함수의 input으로 연관타입을 사용하는것을 하용하지 않는다

이번엔 animal프로토콜에 배고픔을 알려주는 Bool값을 저장하는 저장속성을 하나만들고

스크린샷 2023-04-28 오후 2 41 49

예를들어서 아까만들었던 farm에서 아래와같은 익스텐션을 추가해준다

스크린샷 2023-04-28 오후 2 41 17

근데 애초에 밥을 줄때 배고픈 동물들에게만 밥을 줘야하니 배고픈 동물들을 filter로 뽑는 계산속성을 하나 만들어주고

feedAnimals 라는 함수를 실행시키면 계산속성을 가지고 반환된 list를 for문을 돌리게된다 하지만 이때 이 리스트를 한번만 쓰고 버리는 비효율적인 실행이 발생하기때문에 지연계산컬렉션기능 을 사용하면 된다

스크린샷 2023-04-28 오후 2 46 03

근데 사용자입장에서는 LazyFilterSequence 라는 너무 복잡한 타입으로 선언되었기때문에 이렇게 할필요가없고 이것도 결국 하나의 collection타입으로 구체화 할수있기 때문에 some Collection 으로 선언해줄 수 있다

스크린샷 2023-04-28 오후 3 26 06

하지만 이런 너무나 불분명한 타입은 애초에 저 animal이라는 element가 어떤 메서드를 가지고있는지조차 알수없을만큼 불분명한타입이라는 문제점이 발생한다

스크린샷 2023-04-28 오후 3 28 17

이런 너무나 범용적인, 불분명한 some Collection을 조금더 구체화할 수 있는방법은 이런 프로토콜 뒤에 <>로 타입을 명시해주는것이다

스크린샷 2023-04-28 오후 3 30 37

이렇게 프로토콜 이름 뒤에 괄호로 묶인 하나 이상의 연관 타입을 넣으면, 기본 연관 타입을 가진 고유한 프로토콜을 선언할 수 있다

아까전상황으로 돌아가서 input으로 연관타입이들어가야하는 조금더 복잡한 상황을 보면

스크린샷 2023-04-28 오후 3 42 13 스크린샷 2023-04-28 오후 3 42 22

위의 코드를 보면 AnimalFeed는 Crop과 연관이있고 반대로 Crop은 AnimalFeed와 연관이있기때문에 이를 protocol로 만들면

스크린샷 2023-04-28 오후 3 49 43

이 연관관계를 보면 AnimalFeedCrop 을 가지고 다시 CropAnimalFeed 를 가지는 무한루프가 발생하게된다

스크린샷 2023-04-28 오후 3 55 13

해석을 해보면 우선 Animal 프로토콜을 채택하고 있는 인스턴스가 input으로 들어오면 그 인스턴스의 typeAlias 값에 접근해서 Hey라는 구조체 "타입"을 받아오고 Hey라는 타입의 타입메서드를 실행하면 Alfafa라는 Crop타입이 리턴된다

스크린샷 2023-04-28 오후 4 20 46

하지만 eat 메소드가 (someAnimal).FeedType .CropType.FeedType이 아닌 (some Animal).FeedType를 요구했기 때문에 실행되지 않는다

이런경우를 해결하기위해 where로 조건을 달아줘야한다 중첩된타입이 자기자신의 타입이라는걸 명시해주면 된다

스크린샷 2023-04-28 오후 4 27 55

그렇게하면

스크린샷 2023-04-28 오후 4 46 32

이렇게 할수있다