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