[Swift] 내 맘대로 정리하는 API Design Guidelines
들어가기 전에
주말동안 인프런을 통해 API Design Guidelines 를 공부했따.
여러 내용들이 있었지만, 개인적으로 놓치기 쉬운 부분들을 까먹지 않도록 원문을 보면서 정리하기로 했다.
그런데 쓰다보니 왜 이렇게 익숙하나 했더니 삼년 전에 스터디했던 내용이었따 ㅋㅎ,, 또 까먹어서 이러고 있다니,,, 역시 내 기억력 ㄹㅈㄷ,,
머,, 지금이 그때보다 내용이 많아진거 같긴하다,,! 아닌가? ㅎ 튼 복습겸 이번엔 까먹질 않길 바라며 시작!
Naming
Strive for Fluent Usage
영어 문법으로 자연스럽게 읽히는 메소드가 좋음. (전치사 등 이용 가능)
x.insert(y, position: z) ❌
x.insert(y, at: z) ✅x.subViews(color: y) ❌
x.subViews(havingColor: y) ✅x.nounCapitalize() ❌
x.capitalizingNouns() ✅
하지만 처음 한 두개의 argument 뒤에 위치한, 핵심이 아닌 argument 의 경우 이러한 문법적 유창성이 떨어져도 됨.
AudioUnit.instantiate(
with: description,
options: [.inProcess],
completionHandler: stopProgressBar)
예시를 보면 options
나 completionHandler
같은 경우 그냥 명사로 박아버림
factory method 의 이름은 make
로 시작
x.makeIterator()
initializer 및 factory method 의 첫번째 argument 는 함수 이름과 이어져서 구문을 형성하면 안됨.
let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128) ❌
let foreground = Color(red: 32, green: 64, blue: 128) ✅let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14) ❌
let newPart = factory.makeWidget(gears: 42, spindles: 14) ✅let ref = Link(to: destination) ❌
let ref = Link(target: destination) ✅
마지막 예시인 Link(to: destination)
같은 경우는 "link to destination" 이라고 영문법으로 자연스럽게 읽힘. 하지만 이 함수가 이니셜라이저나 factory 메소드라면 이렇게 자연스럽게 읽히면 안되고, Link(target: destination)
로 설정하는게 더 적합
side-effect 여부에 따라 함수 이름 짓기
Side effect 없는 경우는 명사로 읽힘
x.distance(to: y)
i.successor()
Side effect 있는 경우는 명령형 동사로 읽힘
print(x) // 프린트 해라
x.sort() // x를 sort 해라
x.append(y) // x에 y를 append 해라
인스턴스를 갱신하는 mutating 메서드는 갱신 대신 새로운 값을 반환하는 nonmutating 메서드 쌍을 가질 수 있는데 이때 메서드 쌍을 일관되게 네이밍해야함
작업이 동사로 자연스럽게 읽힐 때는 mutating method에 명령형 동사를 사용하고 “ed” 또는 “ing” 접미사를 적용하여 nonmutating 쌍의 이름을 지정함. 주로 “ed” 를 붙이는것을 선호하지만 동사에 직접 목적어가 있어서 “ed”를 추가하는 것이 문법적으로 맞지 않을 때는 “ing”을 추가.
x.sort() // Muatating
z = x.sorted() // Nonmutatingx.append(y) // Muatating
z = x.appending(y) // Nonmutating
작업이 명사로 자연스럽게 읽힐 때는 nonmutating method에 명사를 사용하고, “form” 접두사를 적용하여 mutating 쌍의 이름을 지정함
x = y.union(z) // Nonmutating
y.formUnion(z) // Mutatingj = c.successor(i) // Nonmutating
c.formSuccessor(&i) // Mutating
Conventions
General Conventions
타입이나 프로토콜은 UpperCamelCase
, 나머지는 lowerCamelCase
다만 일반적으로 영어에서 모두 대문자로 표기하는 단어(ex. UTF
, ASCII
, SMTP
) 같은 경우는, 규칙에 따라 균일하게 up or down case 를 적용해야함
var utf8Bytes: [UTF8.CodeUnit]
var isRepresentableAsASCII = true
var userSMTPServer: SecureSMTPServer
utf8Bytes
같은 경우는 변수 이름이기 때문에 소문자로 시작해야함. 그래서 u
가 소문자로 시작했고 나머지 tf
도 이를 따라 모두 소문자로 표기. ASCII
나 SMTP
같은 경우는 변수 이름의 첫 문자가 아니기 때문에 camel case 에 따라 대문자로 시작. 이를 따라 나머지 문자도 모두 대문자로 표기.
동일한 동작을 하는 경우 parameter 타입에 따른 overload 는 허용되지만, return type 에 따른 overrload 는 모호성을 유발하므로 피해야함.
✅
extension Shape {
func contains(_ other: Point) -> Bool { ... }
func contains(_ other: Shape) -> Bool { ... }
}❌
extension Box {
func value() -> Int? { ... }
func value() -> String? { ... }
}
Argument Labels
값은 보존하되 type 변환만을 수행하는 initializer에서는 첫번째 argument label 을 생략함. 이때 첫번째 argument 는 언제나 변환의 source 가 되어야함
Int64(someUInt32)
String(veryLargeNumber)
String(veryLargeNumber, radix: 16)
다만 값을 축소(narrowing)하는 타입 변환의 경우, 축소를 설명하는 label 이 있는게 적합함
extension UInt32 {
init(_ value: Int16)
init(truncating source: UInt64)
}
UInt32
에서 init(_ value: Int16)
같은 경우는 Int16
-> UInt32
로 값의 범위가 커지기 때문에 argument label 을 생략할 수 있음. 하지만 init(truncating source: UInt64)
은 UInt64
-> UInt32
로 값의 범위가 좁아지기 때문에 truncating
이라는 argument label 이 있는게 더 적합
첫 번째 augument가 전치사구 일부를 형성할 때 argument label을 지정. argument label은 일반적으로 전치사로 시작해야함.
x.removeBoxes(havingLength: 12)
하지만 처음 두개의 argument 가 추상화 수준이 같을 경우에는, 추상화를 명확하게 유지하기 위해 아예 함수 이름에서부터 전치사를 붙이고 argument label 시작.
a.move(toX: b, y: c) ❌
a.moveTo(x: b, y: c) ✅a.fade(fromRed: b, green: c, blue: d) ❌
a.fadeFrom(red: b, green: c, blue: d) ✅
첫번째 argument 가 문법 구(grammatical phrase)의 일부를 형성하는 경우 레이블 제거.
하지만 첫번째 argument가 grammatical phrase를 형성하지 않는다면 레이블을 가져야함
// argument label 제거
x.addSubview(y)// argument label 필요
view.dismiss(animated: false)
let text = words.split(maxSplits: 12)
let studentsByName = students.sorted(isOrderedBefore: Student.namePrecedes)