Interfaces

Kyle Marek-Spartz

http://kyle.marek-spartz.org

February 13, 2014

Interfaces

Interfaces in Java

public interface CountFishInterface {
    public void one();
    public void two();
}

public class OurFish implements CountFishInterface {
    public void one() { ... }
    public void two() { ... }
}

Interfaces in Python

NotImplementedError

class CountFishInterface(object):
    def one(self, *args, **kwargs):
        raise NotImplementedError
    def two(self, *args, **kwargs):
        raise NotImplementedError

NotImplementedError, cont.

class OurFish(CountFishInterface):
    def one(self):
        return 1
    def two(self):
        return 2

A sidenote on composition over inheritance

Testing against an interface

class OurFishImplementsCountFishInterface(TestCase):
    def setUp(self):
        self.obj = OurFish()
    def test_one(self):
        try:
            self.obj.one()
        except NotImplementedError:
            self.fail(
                'OurFish does not implement one'
            )
    ...

Testing against an interface, cont.

class AbstractTestCountFishInterface(object):
    def test_one(self):
        try:
            self.obj.one()
        except NotImplementedError:
            self.fail(
                str(type(self.obj)) + 'does not implement one'
            )
    ...

class TestOurFish(AbstractTestCountFishInterface, ..., TestCase):
    def setUp(self):
        self.obj = OurFish()

AttributeError

Defining classes at run time

This:

class A(B, C):
    an_attribute = 1
    def a_method():
        return 2

is approximately equivalent to:

A = type('A', (B, C), {
    'an_attribute': 1,
    'a_method': lambda: 2
})

Defining interfaces at run time

def Interface(interface_name, method_names):
    def interface_helper(*args, **kwargs):
        raise NotImplementedError
    methods = {
        method_name: interface_helper
        for method_name
        in method_names
    }
    return type(interface_name, (object,), methods)

Defining interface tests at run time

def AbstractInterfaceTest(test_name, method_names):
    def abstract_interface_test_helper(method_name):
        def test_method(self):
            try:
                getattr(self.obj, method_name)()
            except NotImplementedError:
                self.fail(
                    type(self.obj).__name__ +
                    ' does not implement ' +
                    method_name
                )
        return test_method
    methods = {
        'test_' + method_name: abstract_interface_test_helper(method_name)
        for method_name
        in method_names
    }
    return type(test_name, (object,), methods)

Defining multiple interfaces (and abstract tests) at run time

interfaces = {
    'CountFish': ['one', 'two'],
    'ColorFish': ['red', 'blue']
}

for interface_name, methods in interfaces.iteritems():
    interface_name += 'Interface'
    globals()[interface_name] = Interface(interface_name, methods)
    test_name = 'AbstractTest' + interface_name
    globals()[test_name] = AbstractInterfaceTest(test_name, methods)

To do:

interface-mixins