субота, 12 липня 2014 р.

2 названия для параметра функции. Чтоб жизнь раем не казалась :) SWIFT

В Objective-C название параметров было частью названия метода. Ну или каждый параметр метода (иногда кроме первого) имел как бы два имени. Первое, описательное, (внешнее: external) использовалось при объявлении метода и вызове, второе (внутренние: internal) название использовалось внутри метода для доступа к нему. Этот концепт был создан для удобства читания кода и приближения его к natural language. Получалось честно говоря, не очень, как по мне. Вместе с положительным моментом большей легкости чтения кода приходилось слишком много печатать, так как добавлялись вспомогательные слова такоие как: With, Using, And, etc.. Так как это отдельное слово - они часто писались с большой буквы и отвлекали. Это помимо необходимости их печатать и соблюдать единый стиль еще и в их использовании. В примере ниже вызов алерта, где внешние название параметра выделено жирным:
[[[UIAlertView alloc] initWithTitle:@"A Message"
  message: @"Hello World"
  delegate:nil
  cancelButtonTitle:@"OK"
  otherButtonTitles:nil] show];
Swift "наследует потрясное удобство чтения от Objective-C" (runtime кстати тоже) поэтому два названия параметров мутода/функции есть и здесь :) Но поскольку свифт похож на нормальный язк программирования (каким являются все производные от С) внешнее названия параметров здесь смотрятся дикова-то и более того: логика их использования не являются целостной!
Я попытаюсь привести иллюстрирующие примеры:
Начнем с функций (не методов класса/структуры). По умолчанию, внешние параметры отсутствуют. За исключением дурацкого способа указания типа параметра (печатать на один символ больше и неправильного порядка) все выглоядит очень даже привычно и можно даже сказать прилично:
func printInts (i: Int, j: Int) {
  println("i=\(i) j=\(j)")
}
printInts(10, 20)
Пока все хорошо. Если хочется (или придерживаемся coding agreement) используем внешние названия параметров:
func printInts(intParam1 i: Int, intParam2 j: Int) {
  println("i=\(i) j=\(j)")
}
printInts(intParam1: 10, intParam2: 20)
printInts(10, 20) //Это не скомпилится, парни!
Здесь в декларации функции объявлено внешнее имя параметра "firstParam" и "i" его внутреннее имя. Одного и того же параметра. В случае, если внутреннее имя параметра достаточно описательно и может использоваться как внешнее имя есть специальная конструкция уменьшающее количество букв, которые надо печатать.
Неявное указание внешнего имени:
func printInts(#i: Int, #j: Int) {
  println("i=\(i) j=\(j)")
}

printInts(i: 10, j: 20)
А теперь неприятные чудеса: внешние параметры могут быть только у некоторых парамеров функции! 
func printInts(intParam1 i: Int, j: Int) {} //капец!
Как сделать чтобы такой код НЕ заливался в svn, а из монитора, при попытке, высовывалась рука и шлепала программиста по лбу? :)
Идем дальше, классы и их методы. Здесь уже немного по другому! Life is not easy!
Для методов класса, внешние имена параметров являются обязательными, для всех кроме первого! WTF!
Более того, по умолчанию, компилятор включает неявное указание имени внешнего параметра для всех параметров метода кроме первого. Бац!
class Rect {
 var width: Int = 0
 var height: Int = 0
 
 func resize(w:Int, h:Int) {
   width = w
   height = h 
 }
//идентичной будет запись:
//func resize(w:Int, #h:Int) {

}

var r = Rect()
r.resize(10, h:10) //упс, что это?
r.resize(w:10, h:10) //Ошибка. У первого параметра нет внешнего имени!
r.resize(10, 10) //И здесь ошибка. нет внешнего имени для второго параметра
Читается код не фонтан. Почему? Мы неправильно именуем функцию - мы думаем что все еще кодируем на С. Рекомендуется добавлять название первого внешнего параметра к имени функции. Например вот это уже немного лучше:
func resizeWidth(width:Int, height:Int) {
 self.width = width
 self.height = height
}
var r = Rect()
r.resizeWidth(10, height:10) //Читается лучше, если привыкнуть, что название метода продолжается среди параметров
Для методов класса/структуры можно явно указать внешние имена. Это позволяет указать внешнее имя для первого параметра. Фух - теперь еще привычнее.
func resize(width w:Int, height h:Int) {
 self.width = w
 self.height = h
}

var r = Rect()
r.resize(width: 10, height:10)
Эппл рекомендует первый способ, если вы в вашем проекте хотите использовать второй подход - это дополнительный параграф в coding convention. Использование двух стилей в одном проекте очень сильно усложняет жизнь. И без того не легкую! :)

Но различия в именованиях параметров между функциями и методами еще не закончились!
Для конструктора класса сделано еще одно исключение: компилятор для первого параметра токже устанавливает неявное внешнее имя!
class Rect {
  var width: Int = 0
  var height: Int = 0
 
 init(w:Int,h:Int) {
   width = w
   height = h
 }
}
var r = Rect(w:10, h:23)
var r = Rect(10, h:23) //не компилится
var r = Rect(10, 23) //не компилится
К счастью, для конструктора так же можно указать явно внешние имена параметров
init(width w:Int,height h:Int) {
 self.width = w
 self.height = h
}
var r = Rect(width:10, height:23)
А что вы думаете об этом?
В целом swift интересный язык, мне очень импонирует отсутствие неинициализированных переменных, правильная обработка nullable типов - никаких больше null reference exception и нормальный вызов методов вместо отсылки сообщений.

Вот здесь толковая серия видео про свифт:
https://www.youtube.com/channel/UCuD-wbMZDn2C2_GwcMqterg

Вот еще немного общего чтива
http://coconata.livejournal.com/12259.html

Немає коментарів: