-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Design protocol interfaces in Swift #10
Comments
*본 글은 WWDC 를 보고, 번역 및 요약 그리고 실행해보는 스터디 프로젝트의 일환입니다. 해당 세션에는 세 가지 주요 주제가 있습니다.
Undestand type erasure
associated type 을 사용하면 특정 타입의 Animal 의 경우 produce() 메서드를 호출하면 특정 Animal 에 대한 특정 Food 가 반환됩니다.(다이어그램 참고)
Chicken, Cow 타입은 CommodityType(각각 Egg 와 Milk) 을 가진 Animal 프로토콜을 준수하고 있습니다. 이제
existential type 에서 associated type 을 리턴하는 메서드를 호출할 때( concret Animal 타입과 associated CommodityType 을 any Animal 과 any Food 로 대체하여 관계를 지웠습니다. any Food 타입은 associated CommodityType 의 Swift 5.7 의 새로운 기능인 프로토콜 메서드의 result type 에 나타나는 associated type 은 produce() 메서드를 any Animal 에서 호출할 때 우리는 컴파일 타임에는 concrete result type 을 알지 못합니다. 하지만, upper bound 의 subtype 은 알 수 있습니다.(즉, any Food 인 것은 알 수 있습니다.) 위의 예제에서는 런타임에서는 any Animal 이 Cow 인 것을 알 수 있습니다. produce() 메서드는 Milk 를 반환합니다. Milk 는 associated CommodityType 의 upper bound 인 any Food 에 저장될 수 있습니다.(아까 이것은 Animal 프로토콜을 준수하는 모든 concrete types 에게 항상 안전하게 적용됩니다. 다른쪽으로 associated type 이 메서드나 생성자의 파라미터에 등장하는 것을 생각해봅시다.
eat 메서드를 호출하려면 값을 파라미터로 넘겨주어야 합니다. 변환이 반대방향으로 진행되기 때문에 type erasure 를 수행할 수 없습니다. associated type 의 upper bound 는 concrete type 을 알 수 없기 때문에 안전하게 변환되지 않습니다. Animal 프로토콜과 관련된 FeedType 의 upper bound 는 any AnimalFeed 입니다. 그러나 위의 상황처럼 any AnimalFeed 가 주어지면 Hay concrete type 을 정적으로 저장한다는 것을 보장할 방법이 없습니다. 다음 처럼요! type erasure 는 consuming position 에 있는 associated types 으로 작업하는 것을 허용하지 않습니다. 대신 opaque associated types 의 type erasure 동작은 실제로 Swift 5.6 에서 볼 수 있는 기존 기능과 유사합니다. cloning reference type 을 위한 프로토콜을 고려해보자. 아래의 프로토콜은 Self 를 반환하는 clone() 메서드를 정의합니다. any Cloneable 타입의 값에 대해 clone() 메서드를 호출하면 리턴 타입 Self 가 upper bound 까지 type erase 됩니다. Self 의 upper bound 는 항상 프로토콜 자체이므로 any Cloneable 타입의 새 타입을 반환합니다. 요약하자면 이것은 associated types 가 있는 프로토콜에서도 동작합니다. producing postion 에서 associated type 이 있는 프로토콜 메서드를 호출할 때 associated type 은 upper bound 까지 type erase 됩니다. Hide implementation detailsconcrete types 에 대한 추상화는 함수 input 뿐만 아니라 oupt 에서도 유용하므로 concrete types 는 구현에서만 볼 수 있습니다. concrete result types 를 추상화하여 구현 세부 정보에서 코드의 필수 인터페이스를 분리하여 더 모듈화 되고, 강력하게 만드는 방법을 살펴보겠습니다. 동물에게 먹이를 줄 수 있도록 Animal 프로토콜을 일반화해보겟습니다. 동물은 배고프면 먹어야 합니다. hungryAnimals 의 반복을 통해서 먹이를 줄 것 입니다. 이때 feedAnimals() 메서드가 한번만 반복하기 때문에 hungryAnimals 의 수가 많은 경우 비효율적입니다. 그래서 이제, hungryAnimals 타입의 프로퍼티는 concrete type 보다 복잡한 이것은 불필요한 구현 세부사항을 노출합니다. 클라이언트인 feedAnimals() 는 이제 클라이언트는 collection 프로토콜을 준수하는 구체적인 유형을 얻고 있다는 것만 알지 concrete type 을 알지 못합니다. 그러나 작성된 대로 이것은 실제로 클라이언트에서 너무 많은 정적인 정보를 숨깁니다. hungryAnimals 가 collection 을 준수하는 concrete type 을 출력한다고 선언하고 있지만, 이 collection 의 element 타입에 대해서는 아무 것도 모릅니다. element type 이 any Animal 이라는 지식 없이는 할 수 있는 것은 전달하는 것 뿐입니다. 즉, Animal 프로토콜 의 메서드를 호출할 수 없습니다. opaque result type
이것은 프로토콜 이름 뒤에 꺾쇠 괄호 안에 type arguments 를 적용하여 작성하면 됩니다. Collection 프로토콜에는 단일 type argument 인 Element type 이 있습니다. 이제 hungryAnimals 가 constrained opaque result type 으로 선언되면 이것이 우리가 원하는 인터페이스입니다.
hungryAnimals 을 lazily 또는 eagerly 하게 계산할지 옵션을 갖기를 원하는 경우 opaque Collection 을 사용하면 두 가지 다른 underlying types 를 반환한다는 오류가 발생합니다. 대신 opaque types 를 사용하여 일반적인 code 를 작성하려면 abstract type relationships 에 의존해야 합니다. 관련 프로토콜을 사용하여 여러 abstract types 간에 필요한 타입 관계를 식별하고 보장하는 방법에 대해 논의해 보겠습니다. Identify type relationships앞서 만든 Animal 프로토콜에 AniamlFeed 타입의 새로운 associatedtype 과 동물에게 FeedType 을 먹도록 지시하는 eat() 메서드를 추가할 것입니다. 흥미롭게 하기 위해서 동물에게 먹이를 주기 전에 적절한 타입의 작물을 재배하고 수확하여 사료를 생산하는 복잡성을 추가해 보겠습니다. 다음은 두 가지 concrete type 입니다. 위의 두가지 concrete types 에 대해 추상화해보겠습니다. 이를 통해 feedAnimal() 메서드를 한 번만 구현하면 cow, chicken 및 다른 새로운 타입의 동물을 먹일 수 있습니다. feedAnimal() 메서드는 Animal 프로토콜의 associated type 이 consuming postion 에 있는 eat() 메서드와 함께 작동하기 때문에 파라미터 타입으로 시작하기 위해서 AnimalFeed 와 Crop 프로토콜을 선언하겠습니다. 다이어그램을 통해 보듯이 AnimalFeed 와 Crop 사이에서 무한 중첩과 함께 영원히 계속 됩니다. AnimalFeed 프로토콜에서 시작했지만, Crop 프로토콜을 사용해도 비슷한 상황이 발생합니다. 단지 하나만 이동했을 뿐입니다. 이러한 프로토콜이 concrete types 간의 관계를 올바르게 모딜링하는지 살펴봅시다. 동물에게 먹이를 주기(eat)전 작물(crop)을 재배(harvest)해야 사료(animal feed)로 가공할 수 있습니다. Animal 을 준수하는 타입을 얻을 수 있고, Animal 은 associated type 으로 AnimalFeed 를 준수하는 FeedType 을 가지고 있습니다. 이 타입은 메서드 grow() 를 호출하는 기반으로 사용할 수 있습니다. grow() 메서드는 AnimalFeed 의 CropType 을 리턴합니다. CropType 은 Crop 을 준수한다는 것을 알고 있으므로 harvest() 메서드를 호출할 수 있습니다. 이를 통해 우리는 무엇을 돌려받을 수 있을까요? 바로 FeedType 입니다. 불행하게도 이것은 잘못된 타입입니다. eat() 메서드는 concrete types 간의 관계를 정의하기에 프로토콜의 선언이 너무 일반적이었습니다. 즉, 원하는 관계를 정확하게 모델링하지 못하고 있습니다. 그 이유를 이해하기 위해서 Hay 와 Alfalfa 타입을 살펴보겠습니다. hay 를 기르면 alfalfa 를, alfalfa 를 수확하면 hay 를 얻는 식입니다. 코드를 리펙토링하고 있는데 실수로 Alfalfa 에서 harvest() 메서드의 리턴값을 변경하여 hay 대신 Scratch 를 반환한다고 상상해 볼까요? 이런 유연한 변경 후에도 concrete type 은 여전히 AnimalFeed 및 Crop 프로토콜의 요구사항을 충족합니다. 작물을 재배하고, 수확하면 우리가 시작한 것과 동일한 animal feed 가 생상된다는 우리가 원하는 규칙을 위반하더라도 말입니다. AnimalFeed 프로토콜을 다시 살펴보겠습니다. 여기서 진짜 문제는 너무 많은 별개의 associated type 가 있다는 것입니다. 이러한 associated Types 중 두 개가 실제로 동일한 concrete type 이라는 것을 기록해야 합니다.(즉, crop 을 재배하고, 수확해서 feed 가 되는데 crop 과 feed 는 결국 같다. 이것을 별개의 associated type 로 가지고 있다.) where
same-type requirement 은 중첩될 수 있는 associated types 이 실제로 동일한 concrete type 이어야 한다는 정적 보장을 나타냅니다. AnimalFeed 를 준수하는 concrete type 에 CropType 이 있고, 이것의 FeedType 은 original AnimalFeed 타입의 concrete type 입니다. 중첩된 구조 대신 단일 쌍으로 축소했습니다. Crop 프로토콜은 어떨까요? (위와 같이 where 절을 사용해서 아까 우려되었던 harvest 를 통해서 기르는 작물과 먹이가 동일하지 않은 경우를 방지할 수 있습니다.) 이 두 프로토콜이 same-type requirements 를 갖추었으므로 이제는 또 다른 associated type 을 얻는 대신 animal 이 기대하는 정확한 animal feed 타입을 eat() 할 수 있습니다. 마지막으로, 지금까지 본 모든 것을 하나로 묶는 Animal 프로토콜의 associated type 다이어그램을 살펴보겠습니다. Cow 와 chicken 이 있습니다. 각 관계를 어떻게 모델링하는지 주목해서 살펴보겠습니다. 다른 중첩된 associated types 의 동등성을 정의할 수 있었습니다. 그러면 일반 코드는 프로토콜 요구사항에 대해 여러 호출을 함께 연결할 때 이러한 관게에 의존할 수 있습니다. 이번 세션에서 우리는 type erasure 가 언제 안전한지, type relationships 가 보장되는 context 가 있어야 할 때를 탐구했습니다. 그런 다음 opaque result types 와 existential types 모두에 사용할 수 있는 primary associated types 를 사용해서 풍부한 타입의 정보를 보존과 구현 상세 정보를 숨기는 것 사이의 균형을 유지하는 방법에 대해 논의했습니다. 마지막으로 관련 타입의 집합을 나타내는 프로토콜 전체에서 same-type relationships 를 사용하여 concrete types 간의 관계를 식별하고 보장하는 방법을 보았습니다. |
The text was updated successfully, but these errors were encountered: