2 + 35
Introduction to Software Engineering (CSSE 1001)
Interface Versus Implementation
Here the method area is the interface.
rectangle(1.0, 4.0).area()4.0
circle(1.0).area() 3.14159
Notice both shapes have the same interface (area) despite requiring different implementations of the area calculation.
How the area was calculated is (mostly) irrelevant at the user-level.
The word polymorphism means to take on many forms.
In computer science, an interface that works over different underlying data-types is called polymorphic.
The plus operator is polymorphic – it works on numbers, strings, lists, and any other class that has implemented __add__.
2 + 35
"Drop" + " Bear"'Drop Bear'
[1, 2] + [3, 4][1, 2, 3, 4]
Point(1,2) + Point(3,4) # Supposing point was implementedPoint(4, 6)
Exercise 1 What is another example polymorphic function that we have encountered that is not arithmetic?
Consider a Cat and Dog class that provide the same methods (i.e. have the same interface).
cat = Cat("Maru", 14)
dog = Dog("Bluey", 6)cat.speak() 'Meow'
dog.speak()'Bark'
cat.info()'Maru the cat is 14 years old'
dog.info()'Bluey the dog is 6 years old'
Implement the Cat and Dog class so that it provides this interface.
class Cat():
def __init__(self, name: str, age: int) -> None:
self._name, self._age = name, age
def speak(self) -> str:
return "Meow" # not the same as printing
def info(self) -> str:
return f"{self._name} the cat is {self._age} years old"class Dog():
def __init__(self, name: str, age: int) -> None:
self._name, self._age = name, age
def speak(self) -> str:
return "Bark"
def info(self) -> str:
return f"{self._name} the dog is {self._age} years old"There are only differences in the Dog and Cat class are what sound is returned by speak and the species of the animal in the info return string.
Notice both of these classes have very similar implementations of their interfaces. It would be best to reuse the code by having generic methods instead.
For instance, Cat and Dog have identical __init__ methods and therefore it is overkill to copy-paste this for them and every other animal we intend to instantiate.
Objects are already grouped by their classes. For instance, we could instantiate many Dog objects from the class Dog.
In maths say: \[
{\rm Bluey} \in Dog.
\] In CS we say: \[
{\rm Bluey} \text{ is a } Dog
\] We can also group classes themselves into super-classes \[
{\rm Dog} \text{ is a } Animal
\] \[
{\rm Cat} \text{ is a } Animal
\] and have Animal define the generic interface that Dog and Cat share.
But notice both Cat and Dog require Speak, albeit a custom one. We can express this in Animal by implementing a Speak method that throws the NotImplementedError.
class Animal():
def speak(self) -> str:
raise NotImplementedErrorThis abstract class is essentially a blueprint for what subclasses of Animal must implement in order to share the common Animal-interface.
class Turtle(Animal):
passyertle = Turtle("Yertle", 101)yertle.info()'Yertle the turtle is 101 years old'
yertle.speak() Error: NotImplementedError
class Fox(Animal):
def speak(self) -> str:
return "Ring-ding-ding-ding-dingeringeding!"kyuubi = Fox("Kyuubi", 1432)kyuubi.info() 'Kyuubi the fox is 1432 years old'
kyuubi.speak() 'Ring-ding-ding-ding-dingeringeding!'
We are able to overwrite methods by declaring them (as we normally would).
class Fox(Animal):
def speak(self) -> str:
return "Ring-ding-ding-ding-dingeringeding!"
def info(self) -> str:
return "Foxes are better than cats and dogs."
fox.info()
'Foxes are better than cats and dogs'We are also able to extend methods.
class Fox(Animal):
def __init__(self, name: str, age: int, nationality: str) -> None:
super().__init__(name, age) # call init from the super-class
self._nationality = nationality # extra attribute
kyuubi = Fox("Kyuubi", 1432, "Japanese")
kyuubi._name
'Kyuubi'
kyuubi._age
1432
kyuubi._nationality
'Japanese'A unified modelling language diagram is the standard way of illustrating inheritance relationships among classes.
For instance, for our Animal class we have…
Consider the following code snippet (from the course notes).
class A(object): # 'object' is the universal class
def __init__(self, x):
self.x = x
def f(self):
return self.x
def g(self):
return 2 * self.x
def fg(self):
return self.f() - self.g()a = A(3)a.x3
a.f()3
a.g()6
a.fg()-3
class B(A):
def __init__(self, x): # inherit from A
self.x = x
def f(self): # inherit from A
return self.x
def g(self): # overwrite
return self.x ** 2
def fg(self): # inherit from A
return self.f() - self.g()class B(A): # inherit from A
def g(self): # overwrite
return self.x ** 2b = B(7)b.x7
b.f()7
b.g()49
b.fg()-42
class C(B):
def __init__(self, x, y): # extend from B from A
super().__init__(x) # Super is B
self.y = y
def fg(self): # extend from B from A
return super().fg() * self.yclass C(B):
def __init__(self, x, y): # extend from B from A
super().__init__(x)
self.y = y
def f(self): # inherit from B from A
return self.x
def g(self): # inherit from B
return self.x ** 2
def fg(self): # extend from B from A
return super().fg() * self.yc = C(3,5)c.x3
c.y5
c.f()3
c.g()9
c.fg()-30
class D(A): # inherit from A
def f(self): # overwrite
return -2 * self.g()class D(A): # inherit from A
def __init__(self, x): # inherit from A
self.x = x
def f(self): # overwrite
return -2 * self.g()
def g(self): # inherit from A
return 2 * self.x
def fg(self): # inherit from A
return self.f() - self.g()d = D(3)
d.x
3
d.f()
-12
d.g()
6
d.fg()
-18
A, B, C, D class example.
Practice Assignment UML 
Dotted arrows indicate has-a relationships and solid ones denote is-a relationships.
Exercise 2 Implement the following classes that inherit from Shape.
class Shape():
pass
class Rectangle(Shape):
pass
class Square(Shape):
passWhich provides the following interface.
r = Rectangle(10, 5)
s = Square(10)
r < sFalse
Classes are further categorized into super-classes which provide abstract-methods. This abstract methods serve as blueprints or outright implementations for the interfaces that all sub-classes must provide.