Now that the base decorator forwards transparently, we can subclass it to add behavior. A concrete decorator overrides read: it still calls self.wrapped.read() to get the underlying data, but transforms the result before handing it back.
Implement UpperCase, whose read() returns the wrapped text uppercased. Notice it does not care whether it is wrapping a plain Source or another decorator. It only relies on the shared read() interface. That is what makes decorators composable.
class Source:
def __init__(self, text):
self.text = text
def read(self):
return self.text
class Decorator:
def __init__(self, wrapped):
self.wrapped = wrapped
def read(self):
return self.wrapped.read()
class UpperCase(Decorator):
def read(self):
"""Return the wrapped text uppercased."""
# TODO:
# return self.wrapped.read().upper()
pass
The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.
Now that the base decorator forwards transparently, we can subclass it to add behavior. A concrete decorator overrides read: it still calls self.wrapped.read() to get the underlying data, but transforms the result before handing it back.
Implement UpperCase, whose read() returns the wrapped text uppercased. Notice it does not care whether it is wrapping a plain Source or another decorator. It only relies on the shared read() interface. That is what makes decorators composable.
Press Run Tests or ββ© to check your solution.