Interfaces

Kyle Marek-Spartz

https://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