“So, Interview Candidate, let’s look at the classes that we have.”

Continued from the previous post OO Interview - Classes Protect Invariants

1
2
3
4
5
6
7
8
9
class Vertebrate(object):
def __init__(self, sound):
self._sound = sound

def sound(self):
return self._sound

def vertebrate(self):
return True
1
2
3
4
5
6
7
8
9
class Invertebrate(object):
def __init__(self, sound):
self._sound = sound

def sound(self):
return self._sound

def vertebrate(self):
return False
1
2
3
class Cat(Vertebrate):
def __init__(self):
super(Cat, self).__init__('meow')
1
2
3
class Spider(Invertebrate):
def __init__(self):
super(Spider, self).__init__('*silence*')
1
2
3
4
5
6
7
8
9
cat = Cat()
spider = Spider()

animals = [cat, spider]
for animal in animals:
print animal.sound(), animal.vertebrate

# prints: meow True
# prints: *silence* False

“We’ve ensured that the ‘cat’ and ‘spider’ classes protect the invariant data that each contains, namely, the sound each animal makes. Now, do you see any refactoring opportunities?”

“… what’s refactoring?”

“Oh, ok. You’ll hear a lot of people throw that word around in the software development space. Quite often, you’ll find that developers and especially management will use the word ‘refactoring’ to mean changing the code. However, a really smart guy named Opdyke first used the word and I think his meaning has the most merit. Specifically, refactoring code means that you change the implementation of the code without altering the existing behavior. Remember, in object-oriented programming, behavior is king.”

“So, you want me to make this code better but without changing the way it works?”

“Precisely. And, usually, better means simpler.”

“Well, both the ‘vertebrate’ and ‘invertebrate’ classes do the same thing.”

“What do you mean?”

“They both take a sound value and return it. We can make it simpler by making a single class that handles that.” I nodded my assent and he went to work.

1
2
3
4
5
6
class Animal(object):
def __init__(self, sound):
self._sound = sound

def sound(self):
return self._sound
1
2
3
class Vertebrate(Animal):
def vertebrate(self):
return True
1
2
3
class Invertebrate(Animal):
def vertebrate(self):
return False

“I agree. That makes it simpler. Now, do you see anything else that we can simplify?”

He looked studiously at the board, concentration causing a slight tightening around his eyes. I directed my gaze out the window and thought some more about that falafel I had for lunch. It tasted better than normal, that day, but the hummus didn’t have enough tahini….

“No, I don’t see anything.”

“No problem. Would you agree that less code would mostly result in simpler code?”

“Yeah.”

“Ok, then I’d like to examine the way that we handle the sound in the ‘animal’ class. We’re working in Python, so we get a lot of expressive power that we won’t get in languages that have attributes resolved at compile time. Where does the ‘cat’ class keep its ‘meow’ sound?”

“In its initializer.”

“Right. Does it have to keep it there? Where else could it keep that piece of data?”

“In a field?”

“Right. Do that for me.”

1
2
3
4
class Cat(Vertebrate):
def __init__(self):
self._sound = 'meow'
super(Cat, self).__init__(self._sound)

“Nicely done, Interview Candidate. Now that you have set it in that variable, how can we change the ‘animal’ class’ implementation to take advantage of it?”

1
2
3
class Animal(object):
def sound(self):
return self._sound

“Like that?”

“Exactly. Now, I think we can simplify this a little more. What sound does a gray cat make?”

He looked at me oddly and replied, “Meow.”

“Right. Now, what sound does a torty make?”

“A what?!”

“Sorry, cat lover. What sound does an orange cat make?”

“Meow.”

“So, what sound does every cat in the world make that doesn’t suffer from laryngitis?”

“Meow.”

“Right again. Can you infer something from this small exercise of the Socratic method?”

Pause. “No.”

“No problem. In what kind of variable have you stored ‘meow?’”

“Um, a normal variable?”

“Yes, that’s true. Nothing special about it. However, you have stored it in an instance varaible. Which means that each instance of every cat in our system has its own special copy of it. Does that seem like a waste?”

“Yes, it does.”

“If we’ve encountered a waste, then that usually means we can write better code. Let me show you.”

1
2
class Cat(Vertebrate):
SOUND = 'meow'

“Now, this implies to the reader of the class definition that the fact that a cat makes a ‘meow’ sound belongs to all cats and not just each one individually. We can change the ‘animal’ class to take advantage of this and the ‘spider’ class to meet the expectations of the ‘animal’ class.”

1
2
3
class Animal(object):
def sound(self):
return self.SOUND
1
2
class Spider(Invertebrate):
SOUND = '*silence*'

“Not only have we simplified the code by removing lines that didn’t add value to the behavior, we’ve also created an implication in the use of the ‘animal’ class. Can you spot it?”

“Yeah. You can’t get the animal’s sound without having some class that provides that capital ‘SOUND’ variable.”

“Right-o. Now, when Jerry breaks the system by writing bad code, we can spot it more easily because he has not met the expectation of ‘animal.’ We have allowed behavior to govern the system. Nice work, Interview Candidate.”