Protocol, Equatable , 조건적용 Protocol

Updated:

🔷 Protocol

프로토콜은 통신규약으로 일종의 약속이라고 보면됩니다. 이것을 쓰자고 서로 미리 정해 놓은것임

보통 delegate 패턴의 경우에서 **delegate 스위프트 언어 자체에서는 **ing, **able 등으로 프로토콜의 이름을 정합니다

프로토콜을 통해 맴버변수, 메소드등을 꼭 구현하도록 강제 시킴

protocol Naming {
	// 우리는 이런 변수를 가지고 있을겁니다 라고 약속하는것
	var name: String { get set }
	// 우리는 이런 메소드를 가지고 있을 겁니다라고 약속
	func getName() -> String
}

// Naming 프로토콜을 implement 했음
struct Friend : Naming {
	var name: String

	func getName() -> String {
		return "내 친구: " + self.name
	}
}

var myFriend = Friend(name: "Jacob")

myFriend.getName() // "내친구: Jacob"

// 이벤트를 처리한다던지, tableView, collectionView 를 처리할때, 쓰는 pattern 에서 delegate pattern 이라고 나올텐데 그때 미리 protocol 을 만들고, 이벤트를 받는식으로 사용을 많이 합니다

🔶 Protocol 상속

프로토콜 자체는 자신의 인스턴스는 만들지 못합니다.

프로토콜을 implement 하면 해당 프로토콜의 변수나, 메소드의 설정이 강제 됩니다

프로토콜도 상속해서 사용이 가능합니다

// 이름
protocol Naming {
	var name: String { get set }
	func getName() -> String
}

// 나이
protocol AgeCountable {
	var age : Int { get set }
}

// 이름, 나이 프로토콜을 상속하는 친구 프로토콜
protocol Friendable: Naming, AgeCountable {

}

// 친구 프로토콜을 implement 하는 myFriend struct
struct MyFriend : Friendable {
	var name: String
	var age: Int

	func getName() -> String {
		return self.name
	}
}

var myFriend = MyFriend(name: "Jacob", age: 40)
myFriend.getName() // "Jacob"
myFriend.age // 40

🔶 Protocol extension

프로토콜안에서 method 는 안에서는 body (logic) 을 넣을 수 없으나, extension 을 사용하면 method 를 사용할 수 있다

// 이름관련 프로토콜
protocol Naming {
	var lastName: String { get set }
	var firstName: String { get set }
	// 주의! 프로토콜에서는 로직을 추가 할 수 없음 (protocol 을 body 를 가질 수 없음)
	func getName() -> String
}

// extension 을 통해 프로토콜에 메소드 추가 (protocol 에서 넣을 수 없지만, extension 을 사용할 수 있음
extension Naming {
	func getFullName() -> String {
		return self.lastName + self.firstName
	}
}

// Naming 프로토콜을 implement 하는 struct
struct Friend : Naming {
	var lastName: String
	var firstName: String
	func getName() -> String {
		return self.firstName
	}
}

let myFriend = Friend(lastName: "Jacob", firstName: "Ko")

let name = myFriend.getName()
print("성: ", name) // 성: Ko

// friend 스트럭트에는 없지만
// 프로토콜 확장을 통해
// 메소드가 추가된 것을 확인할 수 있음
let fullName = myFriend.getFullName()
print("풀네임: \(fullName)") // 풀네임: JacobKo

🔶 Protocol associatedType

assiciatedtype 을 통해 제네릭으로 어떠한 자료등이든 가질 수 있습니다

protocol PetHavig {
	associatedtype T
	var pets: [T] { get set }
	// mutating 을 통해 스트럭트에서 맴버 변수 값 변경
	mutating func gotNewPet(newPet: T)
}

// extension 으로 프로토콜의 메소드 로직 구성
extension PetHavig {
	mutating func gotNewPet(newPet: T) {
		self.pets.append(newPet)
	}
}

enum Animal {
	case cat, dog, bird
}

// 애완동물 프로토콜을 implement 하는 친구 struct
struct Friend : PetHavig {
	// 배열의 자료형이 Animal enum 인걸로 사용가능
	var pets = [Animal]()
}

// 애완동물 프로토콜을 implement 하는 family struct
struct Family : PetHavig {
	// 배열의 자료형이 문자열
	var pets = [String]()
}

var myFriend = Friend()
myFriend.gotNewPet(newPet: Animal.cat)
myFriend.gotNewPet(newPet: Animal.dog)
print(myFriend.pets)

var myFamily = Family()
myFamily.gotNewPet(newPet: "거북이")
myFamily.gotNewPet(newPet: "토끼")
print(myFamily.pets) // ["거북이", "토끼"]

🔷 Equatable Protocol

Equatable Protocol 을 통해서 커스텀 비교 연산자를 만들 수 있습니다

struct PetKind {
	let name: String
}
struct Pet {
	let id: String
	let name: String
	let kind: PetKind
}


extension Pet: Equatable {
	public static func == (lhs: Pet, rhs: Pet) -> Bool {
		return lhs.id == rhs.id && lhs.kind.name == rhs.kind.name
	}
}

let catKind = PetKind(name: "고양이")
let dogKind = PetKind(name: "강아지")

let myPet1 = Pet(id: "01", name: "멍멍이", kind: dogKind)
let myPet2 = Pet(id: "01", name: "냐옹이", kind: catKind)
let myPet3 = Pet(id: "01", name: "코코", kind: dogKind)

if myPet1.id == myPet3.id && myPet1.kind.name == myPet3.kind.name {
	print("두 펫이 같다") // 두펫이 같다
} else {
	print("두 펫이 다르다")
}

// 커스텀 비교 연산자
print(myPet1 == myPet3) // true

🔷 Protocol 조건적용

// 이름 관련 프로토콜
protocol Naming {
	var name : String { get set }
}

// Cat 과 Friend 클래스가 프로토콜을 준수하고 있는 상황
class Cat: Naming {
	var name: String
	init(name: String) {
		self.name = name
	}
}
class Friend: Naming {
	var name: String
	init(_ name : String) {
		self.name = name
	}
}
// extension 으로 확장할때 준수하고 있는 튿정 클래스에만
// 정의가 가능합니다 (struct 은 안되고, 클래스 일때만 가능)
extension Naming where Self : Friend {
	func sayName() {
		print("난 \(self.name) 라고 해")
	}
}
let myFriend = Friend("철수")
myFriend.sayName() // 난 철수 라고 해

extension Naming where Self : Cat {
	func sayName() {
		print("\(self.name) : 야옹~~")
	}
}
let myCat = Cat(name: "개냥이")
myCat.sayName() // 개냥이 : 야옹~~

🔷 Protocol Optional

// 프로토콜 옵셔널
// 펫 관련 프로토콜
protocol PetDelegate {
	func sayHello()
	func saySomething()
}

// 프로토콜 익스텐션을 통해 메소드를
// 옵셔널로 설정 할 수 있습니다
// 즉 준수하지 않아도 됩니다
extension PetDelegate {
	func saySomething() {
	}
}

// 펫 델리겻을 준수하는 펫 클래스
// saySomething 은 옵셔널이라서 컴파일 에러가 안남
class Pet: PetDelegate {
	func sayHello() {
		print("야용~") // 야용~~
	}
}

🔷 CustomStringConvertible

CustomStringConvertible 을 통해 enum 이나 struct, class 객체 등의 설명 변경이 가능합니다

enum Fruit: CustomStringConvertible {
	case apple
	case melon
	// description 정의
	var description: String {
		switch self {
		case .apple: return "맛있는 사과가 왔습니다"
		case .melon: return "맛있는 멜론이 있습니다"
		default: return "알수 없음"
		}
	}
}

struct Pet: CustomStringConvertible {
	var name: String
	var description: String {
		return "우리집 강이지 \(name)"
	}
}

// 이처럼 print 시 desctiption 으로 설정한 내용이 찍힙니다
let myFruit = Fruit.apple
print("myFruit: ", myFruit) // myFruit:  맛있는 사과가 왔습니다
let myDog : Pet = .init(name: "똘똘이")
print("myDog:", myDog) // myDog: 우리집 강이지 똘똘이

Reference

꼼꼼한 재은 씨의 스위프트 문법편 - https://book.jacobko.info/#/book/1186710233

개발하는 정대리 스위프트 기초 문법 - 21일차 / Protocol - https://www.youtube.com/watch?v=8nPQKGrpbmY&list=PLgOlaPUIbynoqbQw_erl3L2w7vfOTCtFD&index=21

Categories:

Updated:

Leave a comment