Search code examples
swiftclassinitializationinstance-variables

Setting requirements for class variables in Swift


I am new to swift. In a group of classes / subclasses, I would like a variable in the super class to be within a required range. For example, in the following code.

class Vehicle {
    var speed: Int
    var heading: Int

    init(speed: Int, heading: Int) {
        self.speed = speed
        self.heading = heading
    }

    func turnRight() {
        if 360 - heading < 90 {
            heading = heading - 270
        } else {
            heading = heading + 90
        }
    }
}

class Car : Vehicle {
    init(speed: Int, heading: Int) {
        super.init(speed: speed, heading: heading)
    }

    override func turnRight() {
        speed = speed / 2
        if 360 - heading < 90 {
            heading = heading - 270
        } else {
            heading = heading + 90
        }
    }
}

What I don't like about the above, is that if I'm creating more subclasses for car, I'll always be forced to check that heading is within 0 and 360. I know about getters and setters, but is there a way to use a setter and still support incrementing the variable?

Ideally, I want my turnRight functions to look like:

func turnRight() {
    heading = heading + 90
}

override func turnRight() {
    speed = speed / 2
    heading = heading + 90
}

...and for my variable set in the superclass to ensure that the heading is within 0 and 360.


Solution

  • what about move heading logic into separate structure?

    Demo code is shown above.

    struct Heading {
    
        private var heading = 0
        init(initialHeading: Int) {
            heading = initialHeading
        }
        mutating func turnRight(){
            if 360 - heading < 90 {
                heading = heading - 270
            } else {
                heading = heading + 90
            }
        }
    }
    
    class Vehicle {
        var speed: Int
        var heading: Heading
    
        init(speed: Int, heading: Int) {
            self.speed = speed
            self.heading = Heading(initialHeading: heading)
        }
    
        func turnRight() {
            heading.turnRight()
        }
    }
    
    class Car : Vehicle {
        override init(speed: Int, heading: Int) {
            super.init(speed: speed, heading: heading)
        }
    
        override func turnRight() {
            speed = speed / 2
            super.turnRight()
        }
    }
    

    EDIT: Update new solution with typealias and generic function turn by angle and enum values

    typealias Angle = Int
    
    enum Direction: Angle {
        case left = -90
        case right = 90 }
    
    struct Heading {
    
        private var heading : Angle
        init(initialHeading: Angle) {
            heading = initialHeading
        }
    
        mutating func turnBy(angle: Angle){
            if 360 - heading < angle {
                heading = heading - (360 - angle)
            } else {
                heading = heading + angle
            }
        } 
    }
    
    class Vehicle {
        var speed: Int
        var heading: Heading
    
        init(speed: Int, heading: Int) {
            self.speed = speed
            self.heading = Heading(initialHeading: heading)
        }
    
        func turnRight() {
            heading.turnBy(angle: Direction.right.rawValue)
        } 
    }
    
    class Car : Vehicle {
        override init(speed: Int, heading: Int) {
            super.init(speed: speed, heading: heading)
        }
    
        override func turnRight() {
            super.turnRight()
            speed = speed / 2
        } 
    }