views:

169

answers:

4

Note: in this question, I am talking about formal Interfaces (e.g. public interface IButton {...} ).

I'm an Actionscript developer, but I suspect my question will resonate in many languages that allow both formal Interfaces and Events.

I love Interfaces. I love the separation between interface and implementation. (I wish Actionscript forced every class to implement a formal Interface, the way Objective-C does.) All of my classes implement an interface (or several), and you'll never find me writing a public method than isn't referenced in an Interface.

But Actionscript is also heavily dependent on Events. My problem is this: every part of a class that communicates with external classes or processes is (or should be) part of its Interface. (I realize this is a matter of opinion. If you disagree, this question will probably be meaningless to you.)

There are several ways to muddy this clear picture, but most of them are avoidable:

  1. You can't list public properties in an Interface. Solution: don't use public properties in your classes. Use getters and setters instead (and I'm not all that crazy about them, either, but I use them when I must).

  2. If classes communicate by passing arguments to constructors, those messages bypass Interfaces, since you can't list a constructor function in an Interface. I avoid this problem by (almost) never passing parameters through constructors. I prefer init() methods that can be made explicit in Interfaces.

  3. Classes can have public methods that aren't listed in an Interface. I avoid this by not doing it. My classes implement Interfaces. Those Interfaces contain headers for ALL public methods.

  4. Many classes dispatch Events.

That last one is the killer.

Let's say I give you a class called Blah, and you ask me how to use it. I can tell you, "It implements the IBlah Interface. Just look at that and you'll see everything you can do with Blah."

Except Blah extends EventDispatcher, which implies that it dispatches Events.

"What Events does it dispatch?" You ask.

"Uh..."

To know, you have to check the JavaDoc or read through Blah's source code. You shouldn't have to do either! You should be able to know how to interact with a class by checking its Interface(s).

I wish Interfaces could look like this:

public interface IBlah
{
    function foo() : void;
    function bar() : Boolean;
    event BlahEvent;
}

But you can't specify events in an Interface.

Am I right that Events break Interfaces? To me, it's as if I gave you a car and said here's the manual. In the manual, it supposedly explains everything that's on the dashboard. But then, when you're driving the car, weird dohickies appear and strange sounds play -- Events that aren't mentioned in the manual.

If I'm wrong, please explain.

If I'm right, is there a good way to avoid the problem?

A: 

I think that what you are missing is that events are completely optional. There is no rule that says when you instantiate a class that you have to handle all events that it raises. You can (if your implementation calls for it) just completely ignore any events called raised by an object. Also, just definining and event says nothing about when or if it will actually be raised. So putting an event in an interface would be useless because there would be no way defining when or if the event was raised when the interface was implemented in a object.

Kibbee
It is true that objectA is not required to listen to objectB's events. But neither is objectA required to call all (or any) of objectB's public methods.My car's dashboard has many public properties and methods that I choose not to use. For instance, the cigarette lighter is optional. I don't smoke so I choose not to use it.But the point is that everything I can do in my car -- and everyting my car does (that might concern me) IS listed in the car's manual. Or it should be. If my car starts beeping, I might choose to ignore the beep, but I want the option of knowing what the beeping means.
Marcus Geduld
I also want to know that all cars that implement the IBeep interface sometimes beep.But if the beeping is dispatched by an Event, I have no way of knowing that (if Events aren't in the manual).My idea of putting Events in the Iterface is half-baked. Maybe that's not the way it should be. That's not my main point. My main point is that there should be some FORMAL way to specify that a class might dispatch certain events.Or am I wrong?
Marcus Geduld
A: 

My comment aside, I think you can declare function headers in the IBlah interface like dispatchMouseDownEvent (that takes required parameters, of course) and implement them in Blah to dispatch a mouse down event. Now instead of dispatching a mouse down event from a method, call dispatchMouseDownEvent from that method. Just make sure that you don't dispatch a mouse down event from anywhere else in the class.

Amarghosh
You can only declare headers of public functions in IBlah. So you shouldn't declare dispatchMouseDownEvent() unless you want it to be used outside the class.
grumblebee
I see your point, and I agree. But OP insists that "every part of a class that communicates with external classes or processes should be part of its `Interface`". This is all I could think of. May be we can add some additional parameter to the function (secret - private static in the `Blah` class) and throw an error if its not received correctly.
Amarghosh
A: 

EventDispatcher implements IEventDispatcher and interfaces can extend other interfaces. So you can just do this:

public interface IButton extends IEventDispatcher
{
}

Now the IEventDispatcher methods are available on IButton

Edit: Also, regarding point #2, the class wouldn't have to use a constructor if it were given a factory interface.

Edit: Regarding point #1, fields (no get/set) are data. Interfaces only describe behavior without describing implementation. Having a field on an interface flies in the face of this by requiring you to implement it as a field.

Richard Szalay
this only tells you that IButtons dispatch events. It doesn't tell you which events it dispatches, and there are an infinite number of event types (when you include custom events).
grumblebee
You can still mark your interface with [Event] (albeit without intellisense), but the problem you describe is a design limitation in the entire flash events system not just in how it relates to interfaces.
Richard Szalay
Curious, Richard: what is the design limitation you're referring to?
grumblebee
@grumblebee - "It doesn't tell you which events it dispatches, and there are an infinite number of event types"
Richard Szalay
A: 

Here's one way of doing it:

    package
    {
        public interface IMortal
        {
         function stabMe() : void;
         function dispatchDeathEvent() : void
        } 
    }

    package
    {
        import flash.events.EventDispatcher;
        import flash.events.Event;

        public class Person extends EventDispatcher implements IMortal
        {
         private var _dispatchesAllowed : Boolean = false;
         private var _lifeForce : Number = 10;

         public function stabMe() : void
         {
          if ( --_lifeForce == 0 ) iAmDead();
         }

         private function iAmDead()  : void
         {
          _dispatchesAllowed = true;
          dispatchDeathEvent();
         }

         public function dispatchDeathEvent() : void
         {
          if ( _dispatchesAllowed ) 
           dispatchEvent( new Event( Event.COMPLETE ) );
           _dispatchesAllowed = false;
         }
        }

    }

I like this, because (a) it lists the event in the Interface and (b) locking the event from outside (_dispatchesAllowed) is optional. That's an implementation detail.

I dislike it because it's a hack. It's weird for a public method to be callable but useless unless it's called by and instance of its host class.

grumblebee