Каковы некоторые типичные варианты использования кортежей в Swift или ООП в целом?

Около 4 месяцев назад я начал новый проект iOS на Swift, чтобы выучить язык. Пока я его разрабатывал, я почти никогда не использовал кортежи во всей программе. Это потому, что я действительно привык к Java и Objective-C, в которых нет кортежей.

Я слышал несколько замечательных вещей о кортежах, и Apple, похоже, была очень рада добавить их в Swift, но, похоже, мне трудно найти действительно полезный случай для них.

Я понимаю и использую их пока только для одного: заменитель быстрого класса. Вместо того, чтобы создавать новый класс с единственной целью — сохранить вместе несколько свойств, используйте кортеж. Но даже тогда я использовал кортеж для этого только один раз, потому что много раз эти свойства влияют друг на друга, поэтому те методы, которые изменяют эти свойства, лучше содержать в классе с этими свойствами. Так это действительно преимущество?

Но какие другие действительно полезные шаблоны проектирования, используемые в ООП (или Swift), о которых должны знать люди, плохо знакомые с кортежами?

Спасибо!


person datWooWoo    schedule 16.04.2015    source источник


Ответы (2)


Может помочь пара примеров из стандартной библиотеки.

Распространенной потребностью является возврат не одного значения функции, а двух разных значений. Так, например, в протоколе _IntegerArithmeticType есть:

static func addWithOverflow(lhs: Self, _ rhs: Self) -> (Self, overflow: Bool)

То есть он возвращает новое значение плюс логический индикатор того, было ли переполнение:

let i: UInt8 = 0xFF
let (j, overflow) = UInt8.addWithOverflow(i, 1)
if overflow { println("Oh noes!") }

Без кортежа вам пришлось бы прибегнуть к inout для одного из результатов или вернуть другую структуру.

Коллекция Dictionary содержит два типа элементов: ключ и значение. Они часто связаны вместе, поэтому Dictionary определяет тип, Element как кортеж из двух:

struct Dictionary<Key : Hashable, Value> : CollectionType, DictionaryLiteralConvertible {
    typealias Element = (Key, Value)
}

Затем, когда вы перебираете словарь, каждый элемент, который вы получаете, представляет собой эту пару, а встроенная деструктуризация кортежей позволяет вам удобно разделить их на два:

// either treat them as a pair:
for element in myDictionary { }

// or destructure them:
for (k, v) in myDictionary { }

Теоретически оба этих примера можно было бы реализовать, определив новые структуры. Так, например, в случае переполнения:

struct ResultWithOverflow<T: _IntegerArithmeticType> {
    let value: T
    let overflow: Bool
}

static func addWithOverflow(lhs: Self, _ rhs: Self) -> ResultWithOverflow<Self>

Однако важно понимать, что здесь кортежи используются не просто потому, что определение этой дополнительной структуры утомительно (хотя это и правда). Это больше потому, что эта дополнительная структура беспорядочна — она мешает чтению функции. Если вы хотите знать, что сделал addWithOverflow, вам нужно найти, что такое ResultWithOverflow. Вместо этого с кортежем в определении функции совершенно ясно, что возвращается.

Точно так же, если бы существовала структура DictionaryElement, которую словарь содержал в качестве своего элемента, это также было бы дополнительным усложнением, которое могло бы помешать пониманию того, как работают словари.

Очевидно, что это компромисс, и если вы везде будете использовать кортежи вместо определения правильных структур, вы зайдете слишком далеко и сделаете свой код нечитаемым. Но в тех случаях, когда использование одноразовое и легковесное, их и легче писать, и понятнее читать.

person Airspeed Velocity    schedule 16.04.2015
comment
Отличный ответ. Я никогда не думал использовать их таким образом. Теперь я могу вернуться и просмотреть свой код и посмотреть, где я могу реализовать эти шаблоны проектирования. Спасибо! - person datWooWoo; 16.04.2015

@Airspeed Velocity дает действительно хороший ответ! Я просто хотел добавить, насколько полезными могут быть кортежи при использовании с операторами switch. Например:

let point = (x: 10, y: 5)

switch point {
    case (10, 5):
        println("Point at (10, 5)")

    case (-10...10, 5):
        println("Point where y = 5 and x lies in the range [-10, 10]")

    case (_, 5) :
        println("Point with y = 5")

    case (let x, _) where x % 2 == 0:
        println("Point with x that is a multiple of 2 (x = \(x))")

    default:
        println("A Point")
}

Очевидно, что это не тот пример, который вы действительно использовали бы, но я думаю, что он демонстрирует некоторые возможности кортежей.

Язык программирования Swift от Apple подробнее обсуждает кортежи. Вот ссылка, если вы ее еще не видели: https://developer.apple.com/library/prerelease/ios/документация/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID309

person ABakerSmith    schedule 16.04.2015