Идиома 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.