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 tot, but a value below 10 becomes 10 and a value above 30 becomes 30;warmer()raises the target by 1 andcooler()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
- Declare the property as
private var target: Int. Code outside the class can't use it; it must go through the methods. - Set it in
initto 20, the starting target of every new thermostat. - In
setTarget, handle a low value: aniffort < 10setstargetto 10. - Handle a high value: an
else iffort > 30setstargetto 30. - Keep everything else: the final
elsesetstargettot. Values 10 and 30 end up here, unchanged. warmercallssetTarget(target + 1).coolercallssetTarget(target - 1).getTargetreturnstarget.
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
privateis checked by the compiler. Outside the class,t.target = 95doesn'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.
warmerreads and passestargetwithout any extra syntax. init()must set every property. The property has no default value here, so the assignment ininitis 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 withsetTarget,warmerorcooler, 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 < 10andt > 30. Both limits flow into the finalelseand are stored as they are. - Forgetting the limit in
warmer. Writingtarget += 1directly lets the target reach 31 and above. Going throughsetTargetkeeps 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 andsetTarget(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)thenwarmer(): the target is 30, then still 30.setTarget(4)thencooler(): the target is 10, then still 10.setTarget(10)thenwarmer(): the target is 11.
