Идиома Swift Struct для замены шаблона наследования типа ООП?

Допустим, у меня есть программа, которая работает с прямоугольниками (это упрощенный пример моей реальной проблемы), которую я моделирую как

struct Rectangle {
    let left:Int
    let right:Int
    let top:Int
    let bottom:Int

    func rationalized() -> Rectangle {
        return Rectangle(left: min(self.left, self, right), right: max(self.left, self.right), top: min(self.top, self.bottom)
    }
}

Рационализированный прямоугольник, в основном, имеет положительную ширину и высоту. Для многих операций (например, масштабирование, преобразование, объединение, пересечение и т. Д.) Полезно гарантировать, что прямоугольник рационализирован. На самом деле, можно было бы стажировать логику рационализации в init(...) так что вы никогда не сможете создать иррациональное. Но в некоторых случаях вы хотели бы поддерживать иррациональные конструкции, по крайней мере временно, например, для редактирования во время перетаскивания, например

      func replacing(left newValue:Int) -> Rectangle {
          return Rectangle(left: newValue, right: self.right, top: self.top, bottom: self.bottom)
      }

Желание сделать это сделало бы так, что помещение логики рационализации в init() имело бы обратный эффект.

Но альтернатива - засорять мой код почти везде (кроме сайтов перетаскивания) rationalized() звонки. Я пытаюсь определить, смогу ли я сделать это с помощью структур и типов.

Если бы я использовал классы, я мог бы иметь суперкласс Rectangle и подкласс RationalizedRectangle, где RationalizedRectangle переопределяет init для выполнения работы. Тогда я обычно мог бы работать с RationalizedRectangles (даже указывать это как тип, когда это уместно), но разрешать редактировать их, используя Rectangle, и конвертировать в RationalizedRectangle в конце.

Но структуры Swift не поддерживают наследование. Так что я в недоумении, как идиоматически это сделать. Я мог бы добавить isRationalizing:Boolean к структуре и перейти на него, но это просто кажется глупым.

Есть ли здесь основанная на структуре идиома, которая бы работала здесь?

1 ответ

Решение

Вы должны быть в состоянии сделать это с помощью протоколов. Вы бы переместили общую логику в протокол, а затем создали бы два класса, которые бы соответствовали этому протоколу с разными инициализаторами. Тогда, когда вы будете ссылаться на конкретный тип объекта, вы можете вместо этого ссылаться на протокол.

protocol RectangleProtocol {
    var left:Int {get}
    var right:Int {get}
    var top:Int {get}
    var bottom:Int {get}
}

struct Rectangle: RectangleProtocol {
    let left: Int
    let right: Int
    let top: Int
    let bottom: Int

    init(leftValue:Int, rightValue:Int, topValue:Int, bottomValue:Int) {
        self.left = leftValue
        self.right = rightValue
        self.top = topValue
        self.bottom = bottomValue
    }
}

struct RationalRectangle: RectangleProtocol {
    let left: Int
    let right: Int
    let top: Int
    let bottom: Int

    init(leftValue:Int, rightValue:Int, topValue:Int, bottomValue:Int) {
        self.left = min(leftValue, rightValue)
        self.right = max(leftValue, rightValue)
        self.top = min(topValue, bottomValue)
        self.bottom = max(topValue, bottomValue)
    }
}

let rectangle: RectangleProtocol = Rectangle(leftValue: 4, rightValue 4, topValue: 8, bottomValue: 8)
let rationalRectangle: RectangleProtocol = RationalRectangle(leftValue: 4, rightValue:8, topValue: 7, bottomValue: 4)

// Now both of these represent a struct that conforms to the RectangleProtocol.
Другие вопросы по тегам