Protocol과 Extention

고라니·2023년 7월 11일
0

TIL

목록 보기
7/67

프로토콜과 익스텐션은 스위프트 프로그래밍의 핵심 요소중 하나이다. 효율성과 유연성을 높이는 중요한 역할을 수행하고 있다. 그렇기 때문에 각각의 개념에 대해 알아보자

프로토콜?

프로토콜은 특정 역할을 수행하기 위해 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 제공한다.
구조체, 클래스, 열거형은 프로토콜을 채택해 그들의 요구사항을 구현할 수 있다.
프로토콜을 채택하고 그 요구사항을 만족하는 타입은 해당 프로토콜을 준수한다고 표현한다.

프로토콜의 구현과 채택

프로토콜은 protocol 키워드를 사용하여 정의한다.

protocol SomeProtocol {
	// 프로토콜 정의부
}

프로토콜의 채택하려면 상속과 동일하게 ":" 뒤에 프로토콜 이름을 작성한다.

class SomeClass: Someprotocol {
	// 클래스 정의부
}

프로토콜 요구사항

프로토콜은 특정한 기능을 수행하기 위한 필요한 요구사항들을 명시한다.
프로토콜이 요구하는 항목들은 프로퍼티, 메서드, 서브스크립트, 이니셜라이저 등이 있다.

프로퍼티 요구사항(Property Requirements)

프로퍼티 요구사항은 프로퍼티의 이름과 타입, 그리고 접근자의 종류를 명시한다.
프로퍼티는 항상 'var' 키워드로 선언되며, 클래스, 구조체, 열거형 모두에서 사용 가능하다.

protocol FullyNamed {
	var fullName: String { get }
}

struct Person: FullyNamed { // FullyNamed 포로토콜을 준수하는 구조체
	var fullName: String
}
let john = Person(fullName: "라니 고")

메서드 요구사항

프로토콜은 함수명과, 매개변수, 반환타입만 작성한다. 프로토콜이 요구하는 메서드는 반드시 구현해야 한다.
타입 메서드는 'static' 키워드를 사용해 선언한다.

protocol RandomNumberGenerator {
	func random() -> Doyble
}

class LinearCongruentialGenerator: RandomNumberGenerator { // RandomNumberGenerator 프로토콜을 준수하는 클래스
	var lastRandom = 42.0
    let m = 139968.0
    let a = 3877.0
    let c = 29573.0
    
    func random() -> Double { // 프로토콜에서 요구한 random() 메서드의 정의부를 구현한다.
    	lastRandom = ((lastRandom a + c).truncatingRemainder(dividingBy: m))
        return lastRandom / m
    }
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.3746499199817101"
print("And another one: \(generator.random())")
// Prints "And another one: 0.729023776863283"

초기자 요구사항

정의 없이 이니셜라이저 지정, 매개변수만 지정 가능

protocol SomeProtocol {
    init(someParameter: Int)
}

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // initializer implementation goes here
    }
}

프로토콜 상속

프로토콜은 하나 이상의 프로토콜을 상속하여 더 많은 요구사항 추가 가능
클래스의 상속 문법과 동일

protocol Hearable {
	func hear()
}

protocol Speakable: Hearable {
	func speak()
}

class SomeClass: Speakable {
	func hear() {
    	print("듣기")
    }
    
    func speak() {
    	print("말하기")
    }

익스텐션?

익스텐션은 기존 클래스, 구조체, 열거형 또는 프로토콜 타입에 새로운 기능을 추가할 수 있는 기능이다.
그러나 타입을 확장하여 추가 기능을 제공하는 것은, 기존 기능을 오버라이드 하는것은 아니다.

구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가할 수 있다.

해당 타입의 소스코드를 모르거나, 볼 수 없어도 타입만 알고있다면 확장 가능 (retroactive modeling)

스위프트의 익스텐션은 다음과 같은 기능들을 추가할 수 있다.

  • 연산타입프로퍼, 연산인스턴스프로퍼티 추가
  • 인스턴스 메서드와 타입 메서드 추가
  • 새로운 이니셜라이저 제공
  • 서브스크립트 정의
  • 중첩 타입의 선언과 사용
  • 특정 프로토콜을 준수하도록 기능 추가

외부라이브러리나 프레임워크를 사용할때 처럼 원본소스를 수정하지 못하는 경우 특히 유용(내부 코드 수정없이 확장 가능)

  • 익스텐션 문법
    extention 키워드를 사용하여 선언한다.
extention SomeType {
	// 새로운 기능 추가
}

익스텐션이 기존의 타입에 새로운 기능을 추가하더라도, 기존의 타입 인스턴스는 그 새로운 기능을 사용할 수 있다.

익스텐션 예제

연산프로퍼티 추가

연산 프로퍼티를 사용하여 기존 타입의 기능을 확장 가능

extension Double {
    var km: Double { return self 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}

let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// Prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters"

메서드 추가

인스턴스 메서드나 타입 메서드를 추가 가능하다.
다음은 Int 타입에 repetitions라는 인스턴스 메서드를 추가한 코드이다.

extension Int {
	func repetitions(task: () -> void) {
    	for _ in 0..<self {
        	task()
        }
    }
}

3.repetitions {
	print("Hello!")
}

// Hello!
// Hello!
// Hello!

여러 익스텐션 블록으로 나눠서 구현하는 것도 가능하다. 나눌때는 관련된 기능별로 묶어주는것이 좋다.

이니셜라이저 추가

익스텐션을 통해 이니셜라이저를 구현하면 타입 정의부에 이니셜라이저 생략 가능, 하지만 클래스의 지정이니셜라이저는 익스텐션으로 구현 불가, 편의이니셜라이저만 추가 가능

지정이니셜라이저는 반드시 오리지널 클래스의 구현부에 작성되어야 한다.

struct Size {
	var width = 0.0, height = 0.0
}
struct Point {
	var x = 0.0, y = 0.0
}
struct Rect {
	var origin = Point()
    var size = Size()
}

let defaultRect = Rect() // 기본적으로 제공되는 이니셜라이저를 사용해 초기화
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
	size: Size(width: 5.0, height: 5.0))
    
extension Rect { // Rect 구조체에 이니셜라이저를 추가하기 위해 확장
	init(center: Point, size: Size) {
    	let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

let centerRect = Rect(center: Point(x: 4.0, y: 4.0), // 확장한 이니셜 라이저 사용
                      size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)

프로토콜과 익스텐션을 알아보았다

이처럼, 스위프트의 프로토콜과 익스텐션은 프로그래밍을 보다 유연하게 하고, 코드의 재사용성을 높이는 등 많은 이점을 제공한다. 이 두 가지 개념을 잘 이해하고 활용하는 것은 Swift를 효과적으로 사용하는 데 중요한 요소이다.

profile
🍎 무럭무럭

0개의 댓글