object.enable() anti-pattern

Steven D'Aprano steve+comp.lang.python at pearwood.info
Fri May 10 07:00:49 EDT 2013


On Fri, 10 May 2013 06:22:31 +0000, Dan Sommers wrote:

> On Fri, 10 May 2013 05:03:10 +0000, Steven D'Aprano wrote:
> 
>>>>> There is no sensible use-case for creating a file OBJECT unless it
>>>>> initially wraps an open file pointer.
> 
>> So far the only counter-examples given aren't counter-examples ...
> 
> Well, sure, if you discount operations like "create this file" and
> queries like "could I delete this file if I wanted to?" [0] as methods
> of the file system rather than of a hypothetical file object.
> 
> What about a distributed system?  Suppose I want to create a file object
> in one place, and send that object to the another place for the file to
> be read from or written to [1]?  Suppose that for security reasons, I
> have to do it that way, because the first place can only create the
> objects, and the second place can only access the underly file contents
> through an existing object?

Unless you have re-implemented the file I/O system, it doesn't matter. If 
your file objects are based on C I/O, then even if the first server 
cannot read or write to the files it still creates file objects in an 
open state, because that is how C works.

Or maybe the first server only creates some sort of proxy to the real 
underlying file object. Or maybe you're re-implemented the I/O system, 
and aren't following C's design. Since you're just making this up as a 
thought experiment, anything is possible.

But either way, that's fine. You've found an object where it does make 
sense to have an explicit "make it go" method: first one entity has 
permission to construct the object, but not to open the underlying file. 
Another entity has permission to open the underlying file, but not to 
create the object. I have no idea whether this is a reasonable security 
design or not, it actually sounds a bit rubbish to me but what do I know? 
So let's treat it as a reasonable design. 

As I've said, repeatedly, that's not what I'm talking about.

When you DON'T have useful things that can be done with the object before 
calling "enable", then it is an anti-pattern to require a separate call 
to "enable" method, and the enable functionality should be moved into the 
object constructor. If you DO have useful things that can be done, like 
pass the object to another entity, for security, then that's a whole 
'nuther story.

Really, what I'm describing is *normal* behaviour for most objects. We 
don't usually design APIs like this:

n = int("42")
n.enable()
m = n + 1
m.enable()
x = m*2 + n*3
print x - 1  # oops, raises because I forgot to call x.enable()

That's a rubbish API, and for simple data-like objects, we all agree it 
is a rubbish API. So why accept the same rubbish API just because the 
object is more complicated? If you don't have a good, reasonable, non-
contrived use-case for a separate "make it go" method, don't use one.


For my next controversial opinion, I'm going to argue that we should do 
arithmetic using numbers rather than by inserting lists inside other 
lists:

# Do this:

count = 0
count += 1

# Not this:

count = []
count.insert(0, [])


*wink*


-- 
Steven



More information about the Python-list mailing list