티스토리 뷰

Swift

Swift ) API Design Guidelines

Zedd0202 2018. 1. 25. 15:24
반응형

안녕하세요 :) Zedd입니다.

오늘은...!!! API Design Guidelines을 제대로 보려고 합니다!!!나름...Swift Convention을 잘 지키고 있다고 생각은 하는데.......

제대로 알고 있어야 할 것 같아서 :) 

여기에 가시면 Swift에 대한 모든것을 볼 수 있으니 참고해주세요 XD 

읽어보시면 알겠지만, 오늘 할 거는 Swift를 어떻게 사용하면 Swift처럼 잘 쓸 수 있는지!!!한마디로 Apple에서 제안하는하는거에요. 정말 한마디로 API Design Guidelines...


■ ●같은 기호들이 나올건데.. 

● : 최상위

○ : ●의 하위

■ : ○의 하위로 생각해주세요. 


그리고 Design Guidelines이니만큼..조금 딱딱하게 번역을 하도록 할게요!

그리고!!! 소스코드가 나올건데, 배경색상에 따라 이게 잘 짠 코드인지, 아닌지..배경이 연두색이면 권장사항대로 잘 짠 코드고, 배경이 빨간색이면 그렇지 않은걸로 보시면 됩니다 :) 그냥 하얀색상 배경 소스코드는 예시라고 보시면 됩니다XD



API Design Guidelines



Fundamentals

사용 시점에서의 명확성(Clarity)이 가장 중요한 목표(goal)입니다. 

메소드와 프로퍼티같은 엔티티는 한번만 선언되고 반복적으로 사용됩니다. 이러한 용도를 명확하고 간결하게 하기 위한 API설계를 하십시오.
설계를 평가할 때, 선언을 읽는 것만으로는 충분하지 않습니다. 항상 use case를 검사하여 문맥에서 명확하게 보이는지 확인하십시오.  



명료성(Clarity)은 간결성(brevity)보다 중요합니다.

Swift코드는 압축(compact)될 수 있지만(고차함수에서의 생략?같은 걸 말하는듯..그 외에도 많이 있겠죠?), 가장 적은 수의 문자로 가장 적은 코드를 사용하는것이 목표는 아닙니다. (한마디로 생략하면 코드가 그만큼 "적어"지죠? == 그렇게 막 생략하는것이 좋은것은 아니다..?라는 걸 말하는것 같음)

Swift코드에서 발견되는 간결함은, 강력한 타입 시스템의 부작용(side-effect)이며 자연스럽게 boilerplate(보일러 플레이트)를 줄이는 기능입니다.


( ※  boilerplate(보일러 플레이트) : 최소한의 변경으로 재사용 할 수 있는 것 / 적은 수정만으로 여러 곳에 활용가능한 코드, 문구)


모든 선언에 대한 문서 주석(코멘트)을 작성하십시오.

주석 작성으로 얻을 수 있는 통찰력(Insights)은 설계에 큰 영향을 미칠 수 있으므로 이를 미루지 마십시오.


API기능을 간단하게 설명하는데 어려움이 있는 경우, 잘못된 API를 설계했을 수 있습니다.



Swift의 dialect of Markdown을 사용하십시오


선언되는 엔티티를 설명하는 summary(요약)부터 시작합니다. API는 종종 선언문과 그 요약에서 완전히 이해 될 수 있습니다.


1
2
3
/// Returns a "view" of `self` containing the same elements in
/// reverse order.
func reversed() -> ReverseCollection
cs


요약에 초점을 맞추십시오. ; 이것은 가장 중요한 부분입니다. 훌륭한 문서 주석은 훌륭한 요약으로 구성되어 있습니다.


가능하다면 마침표로 끝나는 단일 문장 "조각"을 사용하십시오. 완전한 문장을 사용하지 마십시오.


함수 또는 메소드가 수행하는 작업과 리턴하는 작업, null을 생략하는 효과 및 void리턴작업을 설명하십시오.


1
2
3
4
5
6
7
8
9
10
// Inserts `newHead` at the beginning of `self`.
mutating func prepend(_ newHead: Int)
 
/// Returns a `List` containing `head` followed by the elements
/// of `self`.
func prepending(_ head: Element) -> List
 
/// Removes and returns the first element of `self` if non-empty;
/// returns `nil` otherwise.
mutating func popFirst() -> Element?
cs


Note : 드문 경우, 위 코드의  popFirst 같이 요약은 세미콜론으로 구분된 여러 문장으로 구성 됩니다.




 서브스크립트(subscript)가 접근(Accesses)하는것에 대해 설명하십시오. 


1
2
/// Accesses the `index`th element.
subscript(index: Int-> Element { get set }
cs



■ 이니셜라이저가 생성(Creates)하는 작업을 설명하십시오.


1
2
/// Creates an instance containing `n` repetitions of `x`.
init(count n: Int, repeatedElement x: Element)
cs



다른 모든 선언문의 경우(서브스크립트와 이니셜라이저를 제외한 다른 모든 선언문) 선언된 엔티티가 무엇인지 설명하십시오.


1
2
3
4
5
6
7
8
/// A collection that supports equally efficient insertion/removal
/// at any position.
struct List {
 
  /// The element at the beginning of `self`, or `nil` if self is
  /// empty.
  var first: Element?
  ...
cs


(볼드체한 것처럼 정말 "설명"하고있죠?)


○  선택적으로, 하나 이상의 단락 및 글머리 기호 항목으로 계속합니다. 단락은 빈 줄로 구분되고, 완전한 문장을 사용합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// Writes the textual representation of each    ← Summary
/// element of `items` to the standard output.
///                                              ← Blank line(빈줄로 구분한다고 그랬죠?)
/// The textual representation for each item `x` ← Additional discussion
/// is generated by the expression `String(x)`.
///
/// - Parameter separator: text to be printed    ⎫
///   between items.                             ⎟
/// - Parameter terminator: text to be printed   ⎬ Parameters section
///   at the end.                                ⎟
///                                              ⎭
/// - Note: To print without a trailing          ⎫
///   newline, pass `terminator: ""`             ⎟
///                                              ⎬ Symbol commands
/// - SeeAlso: `CustomDebugStringConvertible`,   ⎟
///   `CustomStringConvertible`, `debugPrint`.   ⎭
public func print(
  _ items: Any..., separator: String = " ", terminator: String = "\n")
cs


적절할 때, 인식된 symbol documentation markup 요소를 사용하여 요약 외의  정보를 추가하십시오.


 symbol command syntax으로 인식된 글 머리 기호 항목을 알고 사용하십시오.

Xcode와 같은 인기있는 개발도구는 다음 키워드로 시작하는 글 머리 기호항목에 특별한 대우를 합니다



Naming


Promote Clear Usage(명확한 사용법 홍보?..)



이름이 사용되는 코드를 읽을 때, 모호함을 방지하는 데 필요한 모든 단어를 포함한다.


예를들어, Collection내의 지정 된 위치에서 요소를 제거하는 메소드를 생각해보십시오.


extension List {
  public mutating func remove(at position: Index) -> Element
}
employees.remove(at: x)
이 메소드 서명에서 단어를 생략한다면, x를 사용하여 제거할 요소의 위치를 나타내는 것이 아니라, 
메소드가 x와 동일한 요소를 검색하여 제거한다는 것을 독자에게 암시 할 수 있습니다. 


employees.remove(x) // unclear: are we removing x?



불필요한 단어는 생략하십시오. 이름에 나오는 모든 단어는 사용 현장에서 중요한 정보를 전달해야합니다. 

의도를 명확하게 하거나, 의미를 명확하게 하기 위해, 더 많은 단어가 필요할 수 있지만, 독자가 이미 소유하고 있는 정보로 중복되는 단어는 생략해야합니다. 특히 타입 정보만 반복하는 단어는 생략하십시오. 


public mutating func removeElement(_ member: Element) -> Element?

allViews.removeElement(cancelButton)
이 경우, Element라는 단어는 호출 현장에서 두드러지는 것(salient)을 추가하지 않습니다.
(뭔말이냐 이게ㅡㅡ 하실 수도 있는데  Element라는 단어는 호출부분에서 핵심적인, 가장 중요한 역할?을 하지 않는다는 뜻) 위 코드 보다는 
public mutating func remove(_ member: Element) -> Element?

allViews.remove(cancelButton) // clearer
이 코드가 나을 것 입니다. 
흔히 모호성(ambiguity)을 피하기 위해 반복되는 타입 정보가 필요하지만, 일반적으로 매개 변수의 타입보다는 매개변수의 역할을 설명하는 단어를 사용하는 것이 좋습니다. 


타입 제약 조건 보다는 역할에 따라, 변수, 매개 변수 및 관련타입(associated types)을 지정하십시오.


var string = "Hello"
protocol ViewController {
  associatedtype ViewType : View
}
class ProductionLine {
  func restock(from widgetFactory: WidgetFactory)
}

이런식으로 타입이름을 다른 목적에 맞게 만드는 것은 명확함(clarity)과 표현력(expressivity)을 최적화하지 못합니다. (변수 이름이 string이면........안되겠죠..)

대신, 엔티티의 역할을 나타내는 이름을 선택하려고 노력하십시오.


var greeting = "Hello"
protocol ViewController {
  associatedtype ContentView : View
}
class ProductionLine {
  func restock(from supplier: WidgetFactory)
}

associated type이 프로토콜 제약 조건에 너무 밀접하게 결합되어 프로토콜 이름이 곧 역할인 경우, associated type 이름에 "Type"을 추가하여 충돌을 방지하십시오. 


1
2
3
protocol Sequence {
  associatedtype IteratorType : Iterator
}
cs


취약한 타입 정보를 보완하여, 파라미터 역할을 명확히 합니다. 

특히, 매개변수 타입이 특히 매개 변수 유형이 NSObject, Any, AnyObject 또는 Int 또는 String과 같은 기본 타입 인 경우,  사용시점에서 타입 정보와 컨텍스트가 의도를 완전히 전달하지 못 할 수도 있습니다. 이 예에서는 선언이 명확하지만 사용 현장이 모호합니다. 


func add(_ observer: NSObject, for keyPath: String)

grid.add(self, for: graphics) // vague(모호하다)


명료함을 복원하려면, 각 약식 매개변수( weakly typed parameter) 앞에 그 역할을 설명하는 명사를 붙힙니다. 


func addObserver(_ observer: NSObject, forKeyPath path: String)
grid.addObserver(self, forKeyPath: graphics) // clear


Strive for Fluent Usage(능수능란한 사용을 위한 노력)


사용 현장을 문법적인 영어 구 형식으로 만드는 방법 및 함수 이름을 선호하십시오. 
x.insert(y, at: z)          “x, insert y at z”
x.subViews(havingColor: y)  “x's subviews having color y”
x.capitalizingNouns()       “x, capitalizing nouns”
x.insert(y, position: z)
x.subViews(color: y)
x.nounCapitalize()


(번역을 확실하게 못하겠어서...원문을 올립니다.

It is acceptable for fluency to degrade after the first argument or two when those arguments are not central to the call’s meaning:)

(그러니까 호출 지점에서 핵심이 아닐 때, 첫번째 또는 두번째 전달인자 후에 유창성이 저하(degrade)될 수 있다는 말같아요!!)


1
2
3
AudioUnit.instantiate(
  with: description, 
  options: [.inProcess], completionHandler: stopProgressBar)
cs


"make"를 사용하여 팩토리 메소드의 이름을 시작하십시오. e.g. x.makeIterator()


이니셜라이저 및 팩토리 메소드의 호출의 첫번째 파라미터는 기본 이름으로 시작하는 구문을 형성해서는 안됩니다. e.g. x.makeWidget(cogCount: 47)

 예를들어, 아래 코드에 대한 첫번째 파라미터는 기본 이름과 동일한 구문의 일부로 읽지 않습니다. 

let foreground = Color(red: 32, green: 64, blue: 128)
let newPart = factory.makeWidget(gears: 42, spindles: 14)
let ref = Link(target: destination)

다음코드에서, API작성자는 첫번째 파라미터와 함께 문법적인 연속성을 만들려고 했습니다. 

let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)
let ref = Link(to: destination)

실제로, 이 지침은 파라미터 레이블에 대한 지침과 함께, 호출이 값 보존 형변환을 수행하지 않는 한 첫번째 파라미터에 레이블이 있음을 의미합니다. 


1
let rgbForeground = RGBColor(cmykForeground)
cs



부작용에 따라 함수 및 메소드 이름 지정 


○ 부작용이 없는 것들은 명사구로 읽어야 합니다. e.g. x.distance(to: y)i.successor().

○ 부작용이 있는 것들은 필수 동사구로 읽어야 합니다. e.g., print(x)x.sort()x.append(y).

○ Mutating/nonmutating메소드 쌍 이름을 일관되게 지정합니다. mutating메소드는 종종 유사한 의미를 같는 nonmutating메소드를 가지지만, 인스턴스를 현재 위치에서 업데이트 하는 대신 새로운 값을 반환합니다. 


■ 연산이 동사에 의해 자연스럽게 설명되는 경우에는 mutating메소드에 동사의 명령을 사용하고, "ed"또는 "ing"접미사를 적용하여 해당  nommutating이름을 지정하십시오. 

MutatingNonmutating
x.sort()z = x.sorted()
x.append(y)z = x.appending(y)

■  동사의 과거분사(대개 "ed"추가)를 사용하여 nommutating이름을 지정하는 것이 좋습니다. 


1
2
3
4
5
6
7
8
/// Reverses `self` in-place.
mutating func reverse()
 
/// Returns a reversed copy of `self`.
func reversed() -> Self
...
x.reverse()
let y = x.reversed()
cs


■ 동사에 직접적인 목적어가 있기 때문에, "ed"가 문법적이지 않을 경우, "ing"를 첨부하여 nonmutating이름을 지정하십시오.


1
2
3
4
5
6
7
8
/// Strips all the newlines from `self`
mutating func stripNewlines()
 
/// Returns a copy of `self` with all the newlines stripped.
func strippingNewlines() -> String
...
s.stripNewlines()
let oneLine = t.strippingNewlines()
cs


■ 연산이 자연스럽게 명사에 의해 설명되는 경우, 이 명사를 nonmutating메소드에 명사를 사용하고, "form"접두사를 사용하여 mutating이름을 지정하십시오.
NonmutatingMutating
x = y.union(z)y.formUnion(z)
j = c.successor(i)c.formSuccessor(&i)

● Bool메소드 및 프로퍼티의 사용은 수신자에 대한 주장(assertions)으로 해석해야합니다.  e.g. x.isEmptyline1.intersects(line2).

● "무엇"인지를 설명하는 프로토콜은 명사로 읽어야 합니다. (e.g. Collection).

● 기능을 설명하는 프로토콜은 "able", "ible", "ing"접미사를 사용하여 명명해야 합니다. 

● 다른 타입, 프로퍼티, 변수 및 상수의 이름은 명사로 읽어야 합니다. 



Use Terminology Well


Term of Art : 명사(noun) - 특정 분야 또는 직업 내에서 정확하고 특수한 의미를 지닌 단어 또는 구문.


  보다 일반적인 단어가 의미를 전달하는 경우, 모호한 용어는 사용하지 마십시오.

"skin(피부)"이 당신의 목적에 부합한다면, "epidermis(표피)"라고 말하지 마십시오. 용어는 필수적인 의사소통 도구이지만, 그렇지 않을 경우 손실될 수 있는 중요한 의미를 포착하는데에만 사용해야 합니다.


● Term of Art(위에서 설명했죠?)를 사용한다면,립된 의미에 충실하십시오. (정해진 의미를 고수하십시오.)


보다 일반적인 단어보다, 전문 용어를 사용하는 이유는 그렇지 않으면 모호하거나 불명확 할 수 있는 것을 정확하게 표현한다는 것입니다. 따라서, API는 수용된 의미에 따라 엄격하게 용어를 사용해야 합니다.


전문가를 놀라게 하지 마십시오 : 이미 그 용어에 익숙한 사람은 우리가 새로운 의미를 발명한 것처럼 보이면 놀라게 될 것이고, 분노를 느낄 것입니다.


초보자를 혼동하게 하지 마십시오 : 용어를 배우려는 사람은 누구나 웹검색을 하여 그 전통적인 의미를 발견하기 쉽습니다.


● 약어를 피하십시오. 약자, 특히 비표준 약자는 효과적으로 Term of Art입니다. 왜냐하면 정확하게 그 약어들을 축약하지 않은 형태로 바꾸는 것에 따라 이해가 달라지기 때문입니다. (의역을 조금 했는데, 핵심은 그 축약된 표현을 축약되지 않은 표현으로 "정확하게" 바꿔야만 이해를 할 수 있잖아요? 그러니까 이 축약된 표현을 정확하게 축약되지 않은 표현으로 잘 바꾸냐 못바꾸냐에 따라 이해가 달라질 수 있다는 것 입니다.)


● 선례를 받아들이십시오. 기존 문화를 따른다는 대가를 치르더라도, 초보자를 위한 용어를 최적화하지 마십시오. 

뭔소린지 모르겠지만, 설명이 나옵니다.


List,와 같은 단순화된 용어를 사용하는 것 보다, 연속적인 데이터 구조  Array 의 이름을 지정하는 것이 좋습니다. 초보자가  List 의 의미를 더 쉽게 이해 할 수도 있습니다. 배열은 현대 컴퓨팅의 기본 요소입니다. 따라서 모든 프로그래머느 배열이 무엇인지 곧 알게 될 것입니다. 대부분의 프로그래머가 익숙한 용어를 사용하면, 웹 검색 및 질문에 대한 보상이 제공됩니다. 


수학과 같은 특정 프로그래밍 도메인 내에서  sin(x) 와 같이 널리 선행된 용어는 asverticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x).와 같은 설명문구보다 바람직합니다.

이 경우, 선례가 약어를 피하기 위한 지침을 능가한다는 것에 주목하십시오. 완전한 단어가  sine,일지라도  

sin(x)

 는 수십년 동안 프로그래머와 수세기 동안 수학자 사이에서 공통적으로 사용되어 왔습니다. 

Conventions

General Conventions

O(1)이 아닌 연산프로퍼티의 복잡성을 문서화하십시오. 사람들은 프로퍼티를 정신모형(mental model ..진짜 mental model이라 되어있음....아 뭔지 알아봤더니 멘탈모델이라는게 "특정한 주제에 대한 사용자 행동의 친화도"라고 합니다...멘탈모델을 만든다는 것은 사람들이 하는 행동에 대해 그들과 이야기하고, 패턴을 찾고, 그 패턴을 모두 포괄하는 하나의 모델로 정리하는 일이라고 해요 )으로 저장했기 때문에, 프로퍼티 접근에 중요한 연산이 필요없다고 종종 가정합니다.
그 가정이 위반되었을 때 경고해야합니다.

free 함수(ree functions를 뭐라고 번역해야할지..)보다 메소드와 프로퍼티를 선호하십시오. Free functions은 다음과 같은 특수한 상황에서만 사용됩니다. 

1.  명백하게  self가 없을 때
1
min(x, y, z)
cs

2. 함수가 제한이 없는 generic일 때
1
print(x)
cs


3. 함수 구문이 설정된 도메인 표기법의 일부인경우
1
sin(x)
cs


case Convention을 따르십시오. 타입과 프로토콜의 이름은 UpperCamelCase입니다. 나머지는 모두 lowerCamelCase입니다.
미국영어에서 흔히 대문자로 표시되는 약어 및 이니셜은 case Convention에 따라 일률적으로 위 또는 아래에 있어야 합니다.(English should be uniformly up- or down-cased according to case conventions:)

1
2
3
var utf8Bytes: [UTF8.CodeUnit]
var isRepresentableAsASCII = true
var userSMTPServer: SecureSMTPServer
cs

다른 약어는 일반적인 단어로 취급해야 합니다.

1
2
var radarDetector: RadarScanner
var enjoysScubaDiving = true
cs

● 메소드는 동일한 기본 의미를 공유하거나, 고유한 도메인에서 작동 할 때, 이름을 공유 할 수 있습니다. 

예를들어, 메소드가 본질적으로 동일한 것을 하기 때문에 다음을 권장합니다.

extension Shape {
  /// Returns `true` iff `other` is within the area of `self`.
  func contains(_ other: Point) -> Bool { ... }

  /// Returns `true` iff `other` is entirely within the area of `self`.
  func contains(_ other: Shape) -> Bool { ... }

  /// Returns `true` iff `other` is within the area of `self`.
  func contains(_ other: LineSegment) -> Bool { ... }
}
기하타입과 collection은 별도의 도메인이기 때문에, 동일한 프로그램 안에서도 괜찮습니다.
extension Collection where Element : Equatable {
  /// Returns `true` iff `self` contains an element equal to
  /// `sought`.
  func contains(_ sought: Element) -> Bool { ... }
}
그러나 이러한  index  메소드는 다른 의미를 가지며, 다르게 이름을 지정해야합니다.

extension Database {
  /// Rebuilds the database's search index
  func index() { ... }

  /// Returns the `n`th row in the given table.
  func index(_ n: Int, inTable: TableID) -> TableRow { ... }
}

마지막으로, 타입 유추가 있을 때, 모호함을 초래하기 때문에, "리턴타입에 대한 오버로드"를 피하십시오. 


extension Box {
  /// Returns the `Int` stored in `self`, if any, and
  /// `nil` otherwise.
  func value() -> Int? { ... }

  /// Returns the `String` stored in `self`, if any, and
  /// `nil` otherwise.
  func value() -> String? { ... }
}


Parameters

1
func move(from start: Point, to end: Point)
cs


● 문서를 제공 할 때, 파라미터 이름을 선택하십시오. 파라미터 이름은, 함수 또는 메소드의 사용 시점에 나타나지 않지만 중요한 설명 역할을 합니다.

문서를 읽기 쉽게 만들려면, 이름을 선택하십시오. 예를들어, 이름을 사용하면 문서를 자연스럽게 읽을 수 있습니다.

/// Return an `Array` containing the elements of `self`
/// that satisfy `predicate`.
func filter(_ predicate: (Element) -> Bool) -> [Generator.Element]

/// Replace the given `subRange` of elements with `newElements`.
mutating func replaceRange(_ subRange: Range, with newElements: [E])
그러나 다음과 같은 것들은 문서를 어색하게 만듭니다.

/// Return an `Array` containing the elements of `self`
/// that satisfy `includedInResult`.
func filter(_ includedInResult: (Element) -> Bool) -> [Generator.Element]

/// Replace the range of elements indicated by `r` with
/// the contents of `with`.
mutating func replaceRange(_ r: Range, with: [E])

● 일반적인 사용을 단순화 할때, 기본 매개변수를 활용하십시오. 공통적으로 사용되는 값이 하나인 매개변수는 기본값의 후보입니다. 

기본 전달인자(Argument)는 관련없는 정보를 숨겨 가독성을 향상시킵니다. 예를들어,


let order = lastName.compare(
  royalFamilyName, options: [], range: nil, locale: nil)
위 코드는 훨씬 더 단순해 질 수 있습니다.

let order = lastName.compare(royalFamilyName)
기본 전달인자는 일반적으로 메소드 패밀리(method families)의 사용보다 선호됩니다. 왜냐하면 API를 이해하려고 하는 모든 사람에게 더 낮은 인지 부담(lower cognitive burden )을 주기 때문입니다. 

extension String {
  /// ...description...
  public func compare(
     _ other: String, options: CompareOptions = [],
     range: Range? = nil, locale: Locale? = nil
  ) -> Ordering
}
위의 내용은 간단하지는 않지만, 다음 코드보다 훨씬 간단합니다.

extension String {
  /// ...description 1...
  public func compare(_ other: String) -> Ordering
  /// ...description 2...
  public func compare(_ other: String, options: CompareOptions) -> Ordering
  /// ...description 3...
  public func compare(
     _ other: String, options: CompareOptions, range: Range) -> Ordering
  /// ...description 4...
  public func compare(
     _ other: String, options: StringCompareOptions,
     range: Range, locale: Locale) -> Ordering
}

메소드 패밀리의 모든 구성원은 사용자가 별도로 문서화하고 이해해야합니다. 그 중 하나를 결정하기 위해서는 사용자가 모든것을 이해 할 필요가 있습니다. 예를들어, foo(bar: nil) 과  foo() 가 항상 동의어가 아닌 경우가 종종 있습니다. 대부분이 사소한 차이점을 제거하는 지루한 과정입니다.  기본값이 있는 단일 메소드를 사용하면 훨씬 뛰어난 프로그래머 경험을 얻을 수 있습니다. 


● 매개변수(Parameter)목록의 끝 부분에, 기본값이 있는 매개변수를 찾으십시오. 기본값이 없는 매개변수는 일반적으로 메소드의 의미에 더 중요하며, 메소드를 호출 할 때, 안정적인 초기 사용 패턴을 제공합니다.


Argument Labels


1
2
func move(from start: Point, to end: Point)
x.move(from: x, to: y) 
cs



● 전달인자를 유용하게 구분 할 수 없는 경우, 모든 레이블을 생략하십시오. e.g.min(number1, number2)zip(sequence1, sequence2)



● 값 보존 형 변환을 수행하는 이니셜라이저에서 첫번째 인수 레이블을 생략하십시오. e.g. Int64(someUInt32)


첫번째 전달인자는 항상 변환의 소스(근원?)이어야 합니다.(다음 코드를 보시면 이해가 갑니다.)


1
2
3
4
5
6
7
8
9
10
extension String {
  // Convert `x` into its textual representation in the given radix
  init(_ x: BigInt, radix: Int = 10)   ← Note the initial underscore
}
 
text = "The value is: "
text += String(veryLargeNumber)
text += " and in hexadecimal, it's"
text += String(veryLargeNumber, radix: 16)
 
cs

(String으로 타입변환을 해줄 때, 항상 첫번째 인자가 String으로 변환할 어떠한 값? 위에서 말하는 Source가 됩니다.)


그러나 "narrowing"타입 변환에서는 narrowing을 나타내는 레이블을 사용하는것이 좋습니다.


1
2
3
4
5
6
7
8
9
10
extension UInt32 {
  /// Creates an instance having the specified `value`.
  init(_ value: Int16)            ← Widening, so no label
  /// Creates an instance having the lowest 32 bits of `source`.
  init(truncating source: UInt64)
  /// Creates an instance having the nearest representable
  /// approximation of `valueToApproximate`.
  init(saturating valueToApproximate: UInt64)
}
 
cs


(UInt64를 UInt32같이 이런 변환을 narrowing타입변환이라고 하는 것 같아요. 이럴때는 첫번째 전달인자가 Source임에도 불구하고 레이블을 지정해줬죠.)




참고 : 값 보존형 변환은 단일 형태(monomorphism)의 것으로, 즉 소스 값의 모든 차이에 따라 결과 값이 달라집니다. 예를들어, Int8에서 Int64로의 변환은 모든 고유 Int8값이 고유한 Int64값으로 변환되기 때문에 값보존입니다. 그러나 반대방향으로의 변환은 값을 유지할 수 없습니다. Int64는 Int8에서 표현할 수 있는 것보다 더 많은 값을 가질 수 있습니다. 

참고 : 원래 값을 가져오는(retrieve) 기능은 변환이 값 보존인지 여부에 영향을 주지 않습니다.


● 첫번째 인수가 전치사 구의 일부인 경우, 전달인자 레이블을 지정하십시오. 전달인자 레이블은 일반적으로 전치사에서 시작해야 합니다. e.g. x.removeBoxes(havingLength: 12)



처음 두 전달인자가 단일 추상화의 일부를 나타내는 경우, 예외가 발생합니다. (뭐라는거...야...)

a.move(toX: b, y: c)
a.fade(fromRed: b, green: c, blue: d)

이러한 경우, 추상화를 명확히 하기 위해, 전치사 다음에 전달인자 레이블을 붙히십시오.

(위 코드 같은 경우에는,  x는 "첫번째"전달인수기 때문에 전치사로 시작했고 다음 전달인수부터는 전치사가 붙지않죠. 이럴때는 다음과 같이 코드를 작성합니다.)


a.moveTo(x: b, y: c)
a.fadeFrom(red: b, green: c, blue: d)

● 그렇지 않고, 첫번째 전달인자가 문법적 구문의 일부를 구성하는경우 해당 레이블을 생략하고, 앞의 단어를 기본이름에 추가합니다.

e.g. x.addSubview(y)

(원래 x.add(Subview : y)라면, add와 Subview가 문법적 구문으로 말이 되니까? 이 Subview라는 레이블을 삭제하고 그냥 기본이름에 추가시키는 거죠)


이 지침은 첫번째 전달인자가 문법적 구문의 일부를 구성하지 않으면 레이블이 있어야만 함을 의미합니다.


view.dismiss(animated: false)
let text = words.split(maxSplits: 12)
let studentsByName = students.sorted(isOrderedBefore: Student.namePrecedes)

(dissmiss와 animated=>문법적 구문 x...이런식으로 판단하는 것 같습니다.)


어구가 올바른 의미를 전달하는 것이 중요합니다.

다음은 문법적이지만, 잘못된 것을 표현합니다.


view.dismiss(false)   Don't dismiss? Dismiss a Bool?
words.split(12)       Split the number 12?

dissmiss를 안한다는 건가? 

12번을 나눈다는건가???


기본값을 가진 전달인자는 생략 할 수 있으며, 이 경우 문법적 구문의 일부가 아니므로 항상 레이블을 가져야합니다.

● 다른 모든 전달인자에 레이블을 붙히십시오.




Special Instructions

● tuple멤버에게 레이블을 지정하고, API에 표시되는 클로져 매개변수 이름을 지정합니다.

이 이름들은 설명력을 가지며, 문서 주석으로 부터 참조될 수 있고, tuple멤버들에 대한 표현적인 접근을 제공합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// Ensure that we hold uniquely-referenced storage for at least
/// `requestedCapacity` elements.
///
/// If more storage is needed, `allocate` is called with
/// `byteCount` equal to the number of maximally-aligned
/// bytes to allocate.
///
/// - Returns:
///   - reallocated: `true` iff a new block of memory
///     was allocated.
///   - capacityChanged: `true` iff `capacity` was updated.
mutating func ensureUniqueStorage(
  minimumCapacity requestedCapacity: Int
  allocate: (_ byteCountInt-> UnsafePointer<Void>
-> (reallocated: BoolcapacityChangedBool)
 
cs


클로저 매개변수에 사용되는 이름은 최상위 수준 함수의 매개변수 이름과 같이 선택해야합니다.

호출 지점에서 나타나는 클로저 전달인자에 대한 레이블은 지원되지 않습니다.


● overload Set에서 모호함을 방지하기 위해, 제약없는 다형성(polymorphism)에 각별히 주의하십시오.

(예 :  Any,AnyObject, 그리고 제약없는 generic 매개변수(unconstrained generic parameters))


예를들어 다음 overload Set을 고려하십시오.


struct Array {
  /// Inserts `newElement` at `self.endIndex`.
  public mutating func append(_ newElement: Element)

  /// Inserts the contents of `newElements`, in order, at
  /// `self.endIndex`.
  public mutating func append(_ newElements: S)
    where S.Generator.Element == Element
}

이러한 메소드는 semantic 패밀리를 형성하며, 전달인자 타입은 처음에는 크게 구분됩니다. 그러나  Element 가 Any,인경우 단일 요소는 일련의 요소와 동일한 타입을 가질 수 있습니다. var values: [Any] = [1, "a"]

values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?
(위 코드에서 배열을 집어넣었으면, [1,"a"]라는 배열에 또 배열인 [2,3,4]를 추가한건지 아니면 그냥 2,3,4를 추가한건지 모호하다는거죠. 
모호성을 제거하려면 두번째 overload를 보다 명확하게 명명하십시오. 

struct Array {
  /// Inserts `newElement` at `self.endIndex`.
  public mutating func append(_ newElement: Element)

  /// Inserts the contents of `newElements`, in order, at
  /// `self.endIndex`.
  public mutating func append(contentsOf newElements: S)
    where S.Generator.Element == Element
}





진짜....진짜로 읽기 싫게? 번역을 한 것 같네요ㅠㅠㅠㅠ

위 글을 모두!!!!모두 요약하자면


1. 코드를 짤 때, 무조건!!! 무조건 "명확"하게 짜는 것이 중요. 이 명확함이 간결성(코드 축약같은거)보다 더 중요함ㅇㅇ

2. 그리고 꼭 주석을 달아라!!!!!!!!!!!!!주석은 여기를 참고해서 달도록 해 ㅇㅇ 

3. 이름은 일단 코드를 읽었을 때, 모호함을 방지하는데 필요한 모든 단어를 포함하도록 이름을 짓도록해, 근데!!!근데 불필요한 단어는 생략해야해 (항상 코드는 명료해야한다는 걸 명심해)

이  naming부분이 상당히 긴데(Parameter, argument label...)이 부분은 읽어보시는 걸..추천......


제가 어떤 말을 들었는데,

"코드를 읽을 때, 코드를 읽는 것 처럼 느끼도록 하면 안된다. 문서를 읽는 것 처럼 읽혀야 한다"

이게 뭐 사람에 따라? 좀 말도 안되는 말이라고 생각하실 수도 있는데 저는 개인적으로 굉장히 공감해요 :)

이런 Convention들을 잘 익혀놓으면 정말 코드를 "문서화"하는데 도움이 될 것 같아요. 코드에 주석을 다는거랑은 다르게요!!!!

아무튼 도움이 되었으면...좋겠습니다...ㅎㅎ..



반응형

'Swift' 카테고리의 다른 글

Swift ) ComparisonResult살펴보기  (0) 2018.03.24
Swift4.1 ) flatMap -> compactMap  (2) 2018.03.11
Swift ) DateDecoding(Encoding)Strategy  (1) 2018.01.23
Swift ) 왕초보를 위한 Codable - CodingKey  (5) 2018.01.23
Swift ) NSCountedSet  (2) 2018.01.22