Sipping the Big Gulp: 2 Ways to Narrow an Interface

Kent Beck
2 min readMay 10, 2019

--

The following is from my book-in-progress: Tidy First?: A Daily Practice of Empirical Software Design.

How often have you seen this: a test builds a megabyte of objects, passes them into code under test, that code takes one measly integer out of the objects, does something with it, and returns a result?

Narrowing the Interface tidies situations like this by only passing into the code exactly what it needs, not the whole giganto object complex. Interestingly, there are two reasonable, and reasonably equivalent paths, to narrowing an interface.

So, we start with code like this:

def foo(company)
…company.id…

And here’s where we fork the road, either using this interface as a temporary adapter or adding a temporary parallel parameter.

Adapter

Option 1 has us leaving the current interface alone for the moment and changing the implementation, then changing the implementation later (remember, we only get to tidy either the interface or the implementation but never both. NEVER!)

We create a function that takes the id:

def foo’(company_id)
…company_id…

Call it from the original function:

def foo(company)
return foo’(company.id)

Now we can go to all the callers and call the new function instead:

foo(company) -> foo’(company.id)

When they are all gone, we delete the original function and rename foo’ to foo. Done.

Parallel Parameter

The second path to the same destination has us adding a second parameter to our function (this works particularly well in languages with default parameter values and/or automated refactoring):

def foo(company, company_id = null)
…company.id…

Now we go to all callers and fill in the actual parameter value:

…foo(company, company.id)…

Now we replace company.id with company_id in the original function and delete the default parameter value:

def foo(company, company_id)
…company_id…

Now we delete the actual parameter from all callers (I can’t think of a way of doing this incrementally without tool support. Any thoughts?)

…foo(company.id)…

def foo(company_id)
…company_id…

Conclusion

We get to the same code both ways. Try them both. See which works best for your language/tool/spirit.

(BTW we can also tidy in the opposite direction, to pass a whole object instead of deconstructing the object before calling a function. We’ll take that up in future.)

--

--

Kent Beck

Kent is a long-time programmer who also sings, plays guitar, plays poker, and makes cheese. He works at Gusto, the small business people platform.