Start learning free

LESSON 561 OF 574 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Thermostat in Swift: A private Property with Rules

Practice O(1) Read the full explanation

A thermostat keeps a target temperature, and the target must always stay between 10 and 30 degrees. If any code could change the target directly, nothing would stop it from setting 95. So the target is a private field: only the thermostat's own methods can change it, and every one of them keeps it between 10 and 30.

Implement the class Thermostat:

  • Thermostat() creates a thermostat whose target is 20.
  • setTarget(t) sets the target to t. A value below 10 sets it to 10, and a value above 30 sets it to 30.
  • warmer() raises the target by 1 and cooler() lowers it by 1, but neither ever takes it outside 10 to 30: at 30, warmer() leaves it at 30, and at 10, cooler() leaves it at 10.
  • getTarget() returns the target. It is the only way for other code to read it.

Keep the target private the way the Access Modifiers lesson does in your language: self._target in Python (private by convention), a #target field in JavaScript, and the private keyword in Java, C++, C#, TypeScript, Kotlin, Scala and Swift.

The input is a list of operations, applied in order to one new Thermostat. The output lists what the getTarget calls return, in order. The starter code spells these names the way your language does: set_target and get_target in Python, SetTarget, Warmer, Cooler and GetTarget in C#.

 

Example 1:

Input:
    getTarget()
    warmer()
    warmer()
    getTarget()
    cooler()
    getTarget()
Output: 20 22 21
Explanation: A new thermostat starts at 20. Two warmer() calls take it to 22, and cooler() brings it back to 21.

Example 2:

Input:
    setTarget(35)
    getTarget()
    warmer()
    getTarget()
    cooler()
    getTarget()
Output: 30 30 29
Explanation: 35 is above 30, so the target becomes 30. warmer() can't go past 30, so the target stays 30.
cooler() lowers it to 29.

Example 3:

Input:
    setTarget(4)
    getTarget()
    cooler()
    getTarget()
    setTarget(10)
    warmer()
    getTarget()
Output: 10 10 11
Explanation: 4 is below 10, so the target becomes 10, and cooler() can't go below 10. setTarget(10) keeps 10,
since 10 itself is allowed, and warmer() raises it to 11.

 

Constraints:

  • -1000 <= t <= 1000
  • A test case has from 1 to 1000 operations, and at least one of them is getTarget.
STEP 1 OF 8
    Ctrl + Enter to check

    SWIFT · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Thermostat in Swift, explained

    Problem overview

    A thermostat keeps a target temperature that must always stay between 10 and 30 degrees. If any code could change the target directly, nothing would stop it from setting 95. So the target is a private property: only the thermostat's own methods can change it, and every one of them keeps it between 10 and 30.

    The class Thermostat has these methods:

    • Thermostat() creates a thermostat whose target is 20;
    • setTarget(_ t:) sets the target to t, but a value below 10 becomes 10 and a value above 30 becomes 30;
    • warmer() raises the target by 1 and cooler() lowers it by 1, never leaving the range 10 to 30;
    • getTarget() returns the target. It is the only way for other code to read it.

    Approach

    The rule "stay between 10 and 30" is needed in three places: setTarget, warmer and cooler. Writing it three times invites mistakes. Instead, only setTarget contains the rule, and warmer and cooler call it with target + 1 and target - 1. At 30, warmer() asks for 31, and setTarget turns it back into 30. At 10, cooler() asks for 9 and gets 10.

    A value that is out of range is not refused: it is moved to the nearest allowed value. The limits 10 and 30 are themselves allowed. All methods run in O(1) time.

    Step by step

    1. Declare the property as private var target: Int. Code outside the class can't use it; it must go through the methods.
    2. Set it in init to 20, the starting target of every new thermostat.
    3. In setTarget, handle a low value: an if for t < 10 sets target to 10.
    4. Handle a high value: an else if for t > 30 sets target to 30.
    5. Keep everything else: the final else sets target to t. Values 10 and 30 end up here, unchanged.
    6. warmer calls setTarget(target + 1).
    7. cooler calls setTarget(target - 1).
    8. getTarget returns target.

    The solution in Swift

    class Thermostat {
        private var target: Int
    
        init() {
            target = 20
        }
    
        func setTarget(_ t: Int) {
            if t < 10 {
                target = 10
            } else if t > 30 {
                target = 30
            } else {
                target = t
            }
        }
    
        func warmer() {
            setTarget(target + 1)
        }
    
        func cooler() {
            setTarget(target - 1)
        }
    
        func getTarget() -> Int {
            return target
        }
    }
    

    A run that exercises both limits:

    let t = Thermostat()
    print(t.getTarget())
    t.warmer(); t.warmer()
    print(t.getTarget())
    t.cooler()
    print(t.getTarget())
    t.setTarget(35)
    print(t.getTarget())
    t.warmer()
    print(t.getTarget())
    t.cooler()
    print(t.getTarget())
    t.setTarget(4)
    print(t.getTarget())
    t.cooler()
    print(t.getTarget())
    t.setTarget(10)
    t.warmer()
    print(t.getTarget())
    
    20
    22
    21
    30
    30
    29
    10
    10
    11
    

    Swift details worth knowing

    • private is checked by the compiler. Outside the class, t.target = 95 doesn't compile: 'target' is inaccessible due to 'private' protection level. The invalid temperature can never be created.
    • Methods of the class can use it freely. warmer reads and passes target without any extra syntax.
    • init() must set every property. The property has no default value here, so the assignment in init is what makes the object valid.
    • There is no setter on the outside. Other code can ask for the target with getTarget() and change it only with setTarget, warmer or cooler, so every change passes through the rule.

    Edge cases and common mistakes

    • Getting the boundaries wrong. The limits 10 and 30 are allowed values, so the checks are t < 10 and t > 30. Both limits flow into the final else and are stored as they are.
    • Forgetting the limit in warmer. Writing target += 1 directly lets the target reach 31 and above. Going through setTarget keeps the rule in one place.
    • Refusing instead of clamping. setTarget(35) doesn't leave the target alone: it makes it 30.
    • Making the property public would pass the numeric tests but defeat the purpose: other code could assign 95.
    • Big or negative values. setTarget(-1000) gives 10 and setTarget(1000) gives 30.

    Another way to write setTarget

    The three branches can be replaced by one line that clamps the value: target = min(30, max(10, t)). max(10, t) lifts a low value to 10, and min(30, ...) cuts a high value down to 30. Run on 35, -1000, 1000, 10, 30 and 17, it gives 30, 10, 30, 10, 30 and 17, the same as the if chain, and a following warmer() on 17 gives 18. The chain in the lesson is the clearer version for a first class; the one-liner is the shorter version to know.

    Why the rule lives inside the class

    The point of the lesson is not the arithmetic but where the rule is kept. If the property were public, every piece of code that touches the target would have to remember the limits, and one forgotten check would break the guarantee. With private, the class is the only place that can break the rule, so it is the only place to read when something goes wrong. Looking at one small class tells you that the target is always between 10 and 30.

    Complexity

    Each method compares a number or adds 1 and stores the result. That is O(1) time, and the class holds one integer, so O(1) space.

    • New thermostat, getTarget(): 20.
    • setTarget(35) then warmer(): the target is 30, then still 30.
    • setTarget(4) then cooler(): the target is 10, then still 10.
    • setTarget(10) then warmer(): the target is 11.
    Keep going with Swift500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free