Compare areas before and after each completed setter call. Widen before multiplication so all products of two The history invariant is that
Int dimensions are represented exactly:class LoggedResizableFrame(h: Int, w: Int) extends ResizableFrame(h, w) {
private var increases = BigInt(0)
private def actualArea: Long = height.toLong * width.toLong
override def height_=(h: Int): Unit = {
val before = actualArea
super.height_=(h)
if (actualArea > before) increases += 1
}
override def width_=(w: Int): Unit = {
val before = actualArea
super.width_=(w)
if (actualArea > before) increases += 1
}
def getIncreases: BigInt = increases
}increases counts exactly the completed dimension updates whose new area strictly exceeds their immediately preceding area. The constructor's starting area is a baseline, not an increase. Each overriding setter invokes the superclass setter once and increments precisely when its comparison succeeds. Equal-area changes do not count. The comparison is on area, not merely on which dimension increased; this also handles zero or signed dimensions without adding an unstated input restriction. An arbitrary-precision integer avoids eventual counter overflow.One base-class implementation changes the private fields directly:The completed operation leaves the area unchanged. It calls no overriding setters, so the logging subclass's count stays unchanged and does not need an override. The invariant is about completed public updates, not transient internal states between the two assignments.
def flip(): Unit = {
val oldH = _h
_h = _w
_w = oldH
}A second implementation uses public setters:These setter calls use dynamic dispatch on a logging receiver. Starting at height two and width three, the first call increases the temporary area from six to nine and wrongly increments the count, while the final area returns to six. This version needs a subclass override. With the counter name from part (i), the following override discards the internal setter contributions:For this specified base implementation the two assignments complete normally and the final area equals the initial area, so restoring the saved count is correct. Another design could suppress logging during the compound operation. The difference between the two base implementations illustrates the fragile base class problem: an internal refactoring can change subclass behavior through newly introduced virtual calls.
def flip(): Unit = {
val oldH = height
val oldW = width
height = oldW
width = oldH
}override def flip(): Unit = {
val before = increases
super.flip()
increases = before
} Articles by others on the same topic
There are currently no matching articles.