Is-A Duck

3 Ways to Reuse Code

Has-A Duck

Calls-A Duck

Janet Riley                 @janet_riley_net

Online Intro

These slides are from a lightning talk I gave to Boston PyLadies.  The examples are geared to Python, but the content  applies to languages that  have classes and modules.

 

Press the space bar to advance sequentially.

 

Use the blue arrows in the corner to dive into a section or skip around. 

 

Speaker notes are overlaid in grey.

3 Common Approaches

  • inheritance
  • composition
  • modules and libraries

Code
 Reuse

the eternal

dream

Is-A Duck

Inheritance

Classes

class Duck:

    def looks_like(self):
        print("You see here a duck.")

    def swim(self):
        print("Paddle, paddle, paddle")

    def quack(self):
        print("Quack!")

    

Subclasses

Just like the parent class, except a little different

Reason #1: 

specialization

add * extend * override

>>> plain_dict = dict()
>>> print( plain_dict['no_such_key']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'no_such_key'
KeyError: 'no_such_key'

Example

defaultdict inherits from dict.

It's just like its parent, with one difference:

>>> import collections
>>> my_default_dict = collections.defaultdict(int)
>>> print( my_default_dict['no_such_key'])
0

Python dict() is a key-value map.

It raises an error if we access a key that doesn't exist.

Reason #2:

Substitution

Dr. Liskov

look like a duck

swim like a duck

quack like a duck

✓ 

A substitute duck must:

✓ 

✓ 

The 'L' in the SOLID principle

have duck DNA

✓ 

And in some languages,

Has-A Duck

Composition

import RubberDucky

class Ernie:

    def __init__(self):
        self.ducky = RubberDucky()

    def sing(self):
        print("Rubber ducky, you're the one")
        print( self.ducky.quack())
        print( self.ducky.quack())
        print("You make bath time lots of fun")

Ernie has a duck.

Ernie is not a duck.

Why?

  • weaker relationship between elements
  • more configurable
  • less baggage
  • my implementation is nobody else's business

Calls-A Duck

good old modules

group related code into namespaces

which keeps them tidy

Modules

often without classes

import json

duck = json.loads('{"name":"Daffy Duck", \
                    "species":"Loony Tune"}')

How to Choose

when to ask questions

Composition

  • when possible
  • when nothing calls the object from the outside
  • when configurability is a plus

Inheritance

  • when necessary
  • when it's just like the parent with a small difference 

Modules

  • all static methods or constants
  • no instance data
  • useful across many kinds of things
  • "when your class has 2 methods, one of which is __init__"

Classes

  • there are distinct instances with data
  • inheritance is required

No Trogdors!

class Trogdor(Dragon, Man):

    def burninate(self):
        pass

    def shake_hands(self):
        pass

"Trogdor was a Man!

He was a Dragon Man!

Maybe he was just a dragon..."

 

Too Many Ancestors

Platypus

inherits bill, eggs from Duck

inherits fur, tail from Beaver

inherits poison spurs from Ninja

Lots of Imports

Learn More

The Art of Subclassing - Raymond Hettinger

 

Stop Writing Classes - Jack Diederich

 

Practical Object-Oriented Design in Ruby - Sandi Metz

An articulate and approachable guide to object design. If you know Python, you'll understand the Ruby well enough. 

 

Trogdor the Burninator - HomeStarRunner.com

See the social and domain forces that lead to strange inheritance trees.

Image Credits