The Liskov Substitution Principle requires a subclass object to replace its superclass object without causing logic or runtime errors, while still producing meaningful results. section 7.2
CircularBuffer as a list
A CircularBuffer subclassing list inherits operations such as indexing and pop(). Client code can mutate the buffer through those operations, eventually causing an IndexError. Making CircularBuffer a standalone class and storing a private list prevents those inappropriate substitutions and operations. section 7.2
Listing 7.12 (Program 7.6 CircularBuffer-2): buffer.py (poorly designed)
class CircularBuffer(list): #1
def __init__(self, buffer_size):
self._buffer_size = buffer_size
self._head = 0
self._tail = 0
self._values_count = 0
for _ in range(buffer_size):
self.append(None) #2
@property
def values_count(self): return self._values_count
def add(self, value):
self[self._tail] = value #2
self._tail = (self._tail + 1)%self._buffer_size
self._values_count += 1
def remove(self):
value = self[self._head] #2
self._head = (self._head + 1)%self._buffer_size
self._values_count -= 1
return value
def __str__(self): #3
i = self._head
c = self._values_count
result = 'Contents:'
while c > 0:
result += f' {self[i]}' #2
i = (i + 1)%self._buffer_size
c -= 1
return result
Inspect the poorly designed CircularBuffer implementation to see how inherited list behavior exposes operations that conflict with the buffer abstraction. section 7.2




