It’s taken me a bit of time to wrap my head around the issues here, but I think 
I have it clear enough to articulate the issues I’m bumping into and possibly 
propose some solutions.

There are two different scenarios related to circulars; debug builds and 
release builds.

Debug Builds:
I’ve run into two situations where I’ve had errors in the debug build due to 
circularities:

1. I’m using PureMVC (which ports to FlexJS as-is without a hitch by including 
the source). PureMVC has a Facade class which is a Singleton that is used as a 
go-between to get Proxies and Mediators as well a notification hub. In a number 
of places in my code I had some code which looks like this: private static var 
myProxy:SomeProxy = 
ApplicationFacade.getInstance().retrieveProxy(SomeProxy.NAME) as SomeProxy; The 
class which calls this is being loaded before ApplicationFacade.js, so 
ApplicationFacade is undefined when the above code is run.

2. I have a situation where the goog.requires are such that three of my 
sub-classes are being loaded before their super-classes. I’m not completely 
sure on exactly which circularity is causing this (more on that later), but the 
symptom of this problem is that when goog.inherits(MySubClass, MySuperClass); 
is called, MySuperClass is not yet defined.

I resolved situation #1 (at least for the debug), by removing “static”. This 
moves the definition into the constructor, so it’s only evaluated when the 
class is instantiated — at which time, ApplicationFacade is already loaded.

If we only need to deal with debug builds, both of these issues could be 
handled with initialization functions. Instead of this:

/**
 * @constructor
 * @extends {Super}
 * @param {Holder} page
 */
Sub = function(page) {
  com.printui.model.vos.TextFrameVO._init_();
  //do stuff...
  Sub.base(this, 'constructor', page);
};

goog.inherits(Sub, Super);

Sub.proxy = 
org.apache.flex.utils.Language.as(ApplicationFacade.getInstance().retrieveProxy(SomeProxy.NAME),
 SomeProxy);

We could do this:
/**
 * @constructor
 * @extends {Super}
 * @param {Holder} page
 */
Sub = function(page) {
  Sub._init_();
  //do stuff...
  Sub.base(this, 'constructor', page);
};

Sub._init_ = function(){
  goog.inherits(Sub, Super);
  Sub.proxy = 
org.apache.flex.utils.Language.as(ApplicationFacade.getInstance().retrieveProxy(SomeProxy.NAME),
 SomeProxy);
  Sub._init_ = function(){}
}

This would cause class variables and inheritance setup to run the first time a 
class is instantiated and not when the script is first evaluated. Sub._init_ = 
function(){} at the end of the function redefines the function to do nothing 
after the first time it’s run, so class initialization would only happen once 
no matter how many times you instantiate the class.

After writing this, I realized that the above suggestion is actually flawed for 
static classes which are not instantiated because the init function would never 
be called. I think the goog.inherits should be fine because inheritance only 
matters on classes which are instantiated. However, a better solution would 
probably be to call init() on all classes after they are loaded.

As I understand it, the problem with my suggestion is that it does not address 
limitations in the Google Closure Compiler. This brings us to release builds.

Release Builds:
There’s many kinds of circularities, I’m I’m not clear on which ones cause 
problems. Alex wrote an explanation on the wiki,[1] which is a good starting 
point, but there’s still questions I have. I will list the ones that I’ve 
thought of:

1. A extends B and B extends A. This is not supported by ActionScript, so it’s 
a non-issue.
2. Super classes which have a reference to the sub class as mentioned in the 
wiki.
3. Classes which are unrelated in hierarchy, but reference each other.
4. A circular chain of class references. (i.e. a uses b which uses c which used 
d which uses a)
3. public static variables
4. private static variables
5. public variables
6. private variables
7. function parameters of all four types
8. function return types.
9. interfaces.

To make this a bit more concrete, I have a number of known circularities in my 
code:

1. I have a sub class which calls a super method to take care of most if the 
object setup. This setup is identical for all sub classes with the exception of 
on piece which should be not done for a specific sub, so in the super class I 
have a line of code if(this is Sub){//don’t do it}. Getting rid of this line of 
code is doable, but would require indirection which I’d rather avoid. (but not 
too big a deal)
2. I have a “page” class which keeps track of all objects that it contains. The 
object classes each have a reference to their containing pages which is needed 
to do all kinds of calculations. I think this is a totally valid design, and 
doing it any other way would be very messy and make it very difficult to follow 
the code flow. This exact situation is mentioned in the closure lib Google 
Group.[2]
3. I probably have lots of chained dependencies — especially with regard to 
ApplicationFacade.

I don’t know the best way to fix my problems with things as they stand. There 
were two suggestions to problem #2 on the Google Group.[3] One way to put the 
classes in the same file, and the second was to use interfaces. My question is 
whether interfaces would solve the problem:

public interface IPage{
  function get backgroundObject():PageItem;
}
public class Page implements IPage{
  var pageItems:Vector.<PageItem> = new Vector.<PageItem>();
  pageItems.push(new PageItem());
  pageItems.push(new PageItem());
  public function get backgroundObject():PageItem{
    return pageItems[0];
  }
}
public class PageItem{
  private var _page:IPage;
  public function PageItem(page:IPage){
    _page = page;
  }
  public function get background():PageItem{
    return _page.backgroundObject;
  }
}

Page needs the interface IPage (or does it?) which needs PageItem, which needs 
IPage which needs PageItem ad infinitum.

A bigger question I have is related to the first suggestion (i.e. including 
them in the same file). That makes me question whether we need good.provide and 
goog.require at all for the release build. For debug, we definitely want the 
files separated to identify code during debug. But for release, it seems to me 
that Falcon can simply combine all the required files into a single monolithic 
file containing everything. That should eliminate the need to use goog.provide 
and goog.require being that they are only used (AIUI) for goog to know which 
files to load. I see two ways this could be accomplished:
1. It could strip out all good.provides and good.requires fro the debug build 
and combine the files.
2. It could rebuild from source without goo.provide and goog.require at all. 
(It is possible that I’m not understanding what goog.provide does and it’s 
still needed, but I’m pretty sure I’m right about goog.require.)

Wow. This was a long email, but I think this is an important topic.

Thoughts?
Harbs


[1] https://cwiki.apache.org/confluence/display/FLEX/Circular+Dependencies
[2]https://groups.google.com/d/msg/closure-library-discuss/ZfC1xKIs-NA/vjnhA4llbZsJ
[3]https://groups.google.com/d/msg/closure-library-discuss/ZfC1xKIs-NA/BtEzF7Ow4R4J

On Jul 18, 2016, at 9:30 PM, Alex Harui <aha...@adobe.com> wrote:

> 
> 
> On 7/17/16, 11:45 PM, "Harbs" <harbs.li...@gmail.com> wrote:
> 
>> The issue should be clear.
>> 
>> If MySuperClass.js has not yet been loaded when MySubClass.js is loaded,
>> goog.inherits(MySubClass, MySuperClass); which is in MySubClass.js will
>> cause an error.
> 
> Harbs, if our code normally had problems loading subclasses before base
> classes, I doubt any of our examples would run.  There should be a
> goog.require for the base class at the top of the subclass's file and that
> should load the base class before the goog.inherit runs.  There is
> something unique about your scenario.  We need a test case in order to
> determine what it is.
> 
>> 
>> This code is being added to the JS file right after the constructor is
>> being defined, but it’s not wrapped in an initializer function so it’s
>> evaluated as soon as the JS file is loaded.
>> 
>> If it would be wrapped in an init function, it could be evaluated only
>> after all files are loaded, so all classes would be defined.
>> 
>> We’ve been spending the better part of the last day trying to distill
>> this down to a test case which causes the compiler to load the classes in
>> the wrong order. We’re getting closer, but we’re not there yet. Even if
>> we find the specific root cause in my case for the files to be loaded in
>> the wrong order, I still think it’s a design flaw which should be fixed.
>> The order of loading files should not matter.
> 
> The order of loading files currently matters.  IMO, it would not be worth
> it to make it not matter.  Google thinks it should matter otherwise they
> wouldn't have created the goog.require system and deps.js.
> 
> Thanks,
> -Alex
> 

Reply via email to