Much obliged for your deference.

We would have to agree to disagree then, since I think I agree with you in
general terms, but you seem to disagree on this alleged agreement.

You say except for one specific case, the scenario I described is a hack.
Call it whatever you like. I might even agree to call it a hack, but that's
not the point.

The point is that, sometimes, rearchitechting is not possible, or it's
highly unpractical. It sucks, but that's life.

Changing an interface is generally not a problem if you have full control of
said interface and the code that consumes it. In most application code,
that's likely the case. Things change a bit when the code that declares that
interfaces is part of a library, rather than part of a specific app. It gets
worse if it's your library and you have other people using it (or maybe
you're using it in many other projects); or if you happen to be a user the
library, which was developed by a third party library.

The example you give about Sprite is not a problem because it's a native
class. Yes, some guy at Adobe forgot about adding an IDisplayObject. Bummer.
But the problem is not it's native. It's that it's code you don't have
control over.

Off the top of my head, I can remember a similar case that didn't involve
any native classes. I was using a CacheManager, that, as the name implies,
managed a cache of resources; I say resources because it was rather generic,
but my use case involved images (or actually, proxies to images, but I'll
try not to ramble too much); you could limit the max number of items in the
cache, specify a time-to-live, and other cool and handy features. The beauty
of it was that it worked really well and, as opposed to most similar APIs
you may find in the wild, it used strong references (a must for the
functionality ot provided), timers and whatnot, but cleaned up after itself
correctly (I memory-profiled it and stress-tested it before using it). Now,
coding such a manager is not terribly hard; but the code was availabel to
me, ready to use, properly debugged and optimized, and is the kind of code
where it's very easy to screw up (especially, memory-wise). This manager
accepted and returned some base CacheItem; a class, not an interface. An
interface would have been nicer, probably, but in any case, the author of
this lib couldn't have known in advance what I'd be putting there, so with
an interface maybe I could have wiped out the proxy and removed one level of
indirection (the object I wanted to cache already extended something else);
I still would have needed to cast the object when I retrieved from the
manager to do anything useful.

In such scenario, you can either: rewrite the beast; monkey-patch it
(assuming you have the source code, which is not always the case; even if
you have it, mokey-patching has problems of its own); adding this little
cast hack, which is a no-brainer that takes 5 seconds. Personally, I'd
choose the last option.

Now, I think more generally, there are two kinds of problems when changing
interfaces. (Again, this problem is minor if you have control of both the
lib and the app)

1) Source code compatibility.

This means any user of the lib will have to not only recompile their app,
but also change their code accordingly if they are using any method changed
in the library. By defintion a public interface should be stable (or at
least tend to be stable) and backwards compatible. (Ok, Facebook change
their mind about their API every other week, but they're big enough to get
away with it. If you don't appreciate how liberal they are about this, tough
luck. The client wants their app running smoothly and they --at least, most
of them-- don't know what API means or care the least about it, for that
matter).

As a user, I certainly appreciate a stable API that doesn't break my code
with every single release (and, oh so many times, gratuitously).

2) Binary compatibility

Ok, this is a less common case in Actionscript as it is in other languages,
but the consequences could be much worse than breaking source code
compatibility. I've actually worked on a project that had this problem.

I realize I have written a rather longish reply by now, so I'll try to be
brief (and hope you haven't feel asleep at this point). Some basic context
first: this project was structured as a number of small modules (about 25 of
them). Those modules were mostly self contained, but shared a good number of
interfaces. Building the whole project from scratch was a hard as running a
build script. Deploying it wasn't that simple, alas.

For reasons that are boring to detail, going from the staging environment to
QA and then to production was slow and painful. "Full deploys" were only
reserved for special cases. A deploy caused modified files to be purged from
CDN caches, which is a very bad thing for apps with high traffic; this
site's traffic is huge, and though there are load balanced dedicated servers
running it, completely purging the cache is something the stresses the whole
infrasctructure, so the IT guys are rather reluctant to "full deploys".

Now, the thing is (and I learnt this the hard way; thankfully this problem
got caught in QA), interfaces work like classes in that if two swf use the
same interface, the first one loaded is the one used (if they're loaded in
the same app domain; loading it in a different domain kind of defeats the
purpose of using such interfaces in the first place, though). But they don't
work like classes in that they are verified by the player, at load time. If
you change anything in a interface, and a.swf references the newer version
while b.swf references the older one, the player will throw a VerifierError.
I'm not sure you can even catch that, but even if you could, b.swf is dead
code by that time; worse than that, it will take with it the rest of the
code in every other swf loaded at that point. Basically, everything is
broken; the only thing you can do is reload, but when you get to that point
again, it will naturally blow, again. I kid you not: any change you make to
an interface can cause this. Even adding an optional parameter to one
method. This can be worked around, so I concede it's not impossible to
rearchitech things. However, even for "full deploys", you have to plan ahead
(picking the right time, coordinating with different teams, etc; and there's
a time span of a few hours in which, by how the load balanced CDN work, you
can't guarantee that a user will get the same version of all swfs; so
basically, the site is half broken for that period). Again, I know this is
not the most common case, but binary compatibility could be a problem in
some situations and from my experience I've learnt that, sometimes, a minor
"hack" is a lesser evil.

Cheers
Juan Pablo Califano

2010/6/7 Steven Sacks <flash...@stevensacks.net>

> With due deference, what you just said I disagree with. The scenario you
> described is not a necessary evil, it's a hack.
>
> The only situation where that isn't a hack is if your class extends a
> native type such as Sprite, but there's no ISprite. That problem is either
> solved by adding the Sprite functions you need to your interface, or, if you
> need to addChild() your interface, then you cast as Sprite because there's
> no other way.
>
> Outside of that specific case, you should never run into the situation you
> described, and if you do, it's time for a quick refactor.
>
> If you want a function in SomeConcreteClass to be accessible outside the
> class/inheritance/package (i.e. public) and you have an interface for that
> class, then you should include that function in the interface.  Sometimes
> you may not put public methods in the interface because you want them to be
> public to other packages inside your app, but not to "the outside world",
> and you shouldn't be casting to the interface of that class in those cases.
> Why would you cast as the interface when you don't need the interface?
>
> Using interfaces incorrectly or inappropriately is the disease, not a
> symptom of strict typing. If anything, strict typing forces these issues to
> appear in your code before it's too late.
>
> No matter what, proper typing never leads to code smell. Improper typing or
> mixed typing like what you described is code smell in the sense that it's
> indicative of poor architecture decisions.
>
>
>
>
> On 6/7/2010 3:46 PM, Juan Pablo Califano wrote:
>
>> I agree with you, but I'add for the sake of completeness that, sometimes,
>> relaxing the rules a bit becomes a necessary evil, like for instance, when
>> doing a cast.
>>
>> When you do this:
>>
>> // bar is typed as ISomeInterface somewhere else
>> var foo:SomeConcreteClass = bar as SomeConcreteClass;  // or
>> SomeConcreteClass(bar)
>> foo.methodOnlyPresentInConcreteClass();
>>
>> You are basically bypassing the rules of type system. You're telling the
>> compiler that an object declared as ISomeInterface at compile time will be
>> an instance of SomeConcreteClass at runtime. You're asking the compiler to
>> trust you and not blow up when compiling your code. Some times this is
>> avoidable with some refactoring (and or a better design). Sometimes it's
>> not
>> possible or impractical (this also depends on how purist you are, I
>> guess).
>> Nevertheless, having too many casts is usually considered a code smell for
>> this very reason.
>>
>> Cheers
>> Juan Pablo Califano
>>
> _______________________________________________
> Flashcoders mailing list
> Flashcoders@chattyfig.figleaf.com
> http://chattyfig.figleaf.com/mailman/listinfo/flashcoders
>
_______________________________________________
Flashcoders mailing list
Flashcoders@chattyfig.figleaf.com
http://chattyfig.figleaf.com/mailman/listinfo/flashcoders

Reply via email to