Step 9 of 9
Challenge: program to an interface
In professional code, the most valuable use of virtual functions isn't game units: it's dependency injection. A class depends on an abstract interface instead of a concrete service, and whoever creates it passes in the real thing.
Why that matters: code that calls the system clock, a database or the network directly is hard to test. Tests would have to wait for real time to pass or set up a real server. If the code only talks to an interface, a test can pass in a fake it fully controls.
#include <iostream>
class Clock {
public:
virtual ~Clock() = default;
virtual long now() const = 0;
};
class FakeClock : public Clock { // a test double: time moves only when told
public:
long now() const override { return t_; }
void advance(long s) { t_ += s; }
private:
long t_ = 0;
};
class Coupon {
public:
Coupon(const Clock& clock, long valid_for) : clock_(clock), expires_(clock.now() + valid_for) {}
bool valid() const { return clock_.now() < expires_; }
private:
const Clock& clock_; // depends on the interface only
long expires_;
};
int main() {
FakeClock clock;
Coupon c(clock, 60);
std::cout << c.valid();
clock.advance(59);
std::cout << c.valid();
clock.advance(1);
std::cout << c.valid() << "\n";
}
110
The test checks a 60 second expiry instantly, with exact control over the boundary. In production you'd pass a SystemClock whose now() reads the real time, and Coupon wouldn't change at all.
Designing the rate limiter
Store the times of recent allowed calls in a std::deque<long>, oldest at the front. In allow():
- Get
now()once. - Pop from the front every time
twherenow >= t + window_: those calls have left the window. - If fewer than
limit_remain, pushnowat the back and returntrue; otherwise returnfalse.
Store the clock as a const Clock& member, as Coupon does. The object that owns the real clock must outlive the limiter.
Your turn: write class RateLimiter that takes a const Clock& and allows at most limit calls to allow() in any window of window seconds. allow() returns true and records the call if under the limit, otherwise false. A call at time t falls out of the window once now() >= t + window.