循環進口可以得到相當棘手,但行爲一致。關鍵點是:
use Some::Module
行爲就像BEGIN { require Some::Module; Some::Module->import }
- 當一個模塊被加載時,它被編譯並執行。
BEGIN
塊在解析周圍代碼時執行。
- 每個模塊只有
require
'd一次。當再次需要時,那require
是忽略。
知道了,我們可以將您的四個文件合併到一個單獨的文件中,該文件在BEGIN塊中包含require
d文件。
讓我們先從你的主文件:
use MyA;
use MyB;
MyB->new()->the_method();
我們可以改變use
到BEGIN { require ... }
和包括MyA
內容。爲了清楚起見,我將忽略在MyA
和MyB
上的任何->import
調用,因爲它們在這種情況下不相關。
BEGIN { # use MyA;
package MyA;
use Moo;
with ('MyRole');
sub the_method { die; }
}
BEGIN { # use MyB;
require MyB;
}
MyB->new()->the_method();
的with('MyRole')
也做了require MyRole
,我們可以明確的:
...
require MyRole;
with('MyRole ');
因此,讓我們展開:
BEGIN { # use MyA;
package MyA;
use Moo;
{ # require MyRole;
package MyRole;
use Moo::Role;
use MyB;
requires 'the_method';
before the_method => sub { die 'This has been correctly executed'; };
}
with ('MyRole');
sub the_method { die; }
}
BEGIN { # use MyB;
require MyB;
}
MyB->new()->the_method();
我們可以再擴大use MyB
,也擴大MYB的with('MyRole')
到require
:
BEGIN { # use MyA;
package MyA;
use Moo;
{ # require MyRole;
package MyRole;
use Moo::Role;
BEGIN { # use MyB;
package MyB;
use Moo;
require MyRole;
with ('MyRole');
sub the_method { die 'The code should have died before this point'; }
}
requires 'the_method';
before the_method => sub { die 'This has been correctly executed'; };
}
with ('MyRole');
sub the_method { die; }
}
BEGIN { # use MyB;
require MyB;
}
MyB->new()->the_method();
在MyB
之內,我們有一個require MyRole
,但該模塊已經被要求。因此,這沒有做任何事情。在執行過程中這一點上,MyRole
僅由這一點:
package MyRole;
use Moo::Role;
所以角色是空的。 requires 'the_method'; before the_method => sub { ... }
尚未編譯。
因此MyB
組成了一個空角色,它不影響the_method
。
這怎麼能避免?在這些情況下避免use
通常是有幫助的,因爲在當前模塊被初始化之前會中斷解析。這導致了不直觀的行爲。
當您模塊use
只是類,並且不影響您的源代碼如何解析(例如通過導入子例程),那麼您通常可以將require延遲到運行時。不僅是執行頂級代碼的模塊的運行時間,而且還包括主應用程序的運行時間。這意味着將require
粘貼到需要使用導入類的子例程中。由於即使所需模塊已經導入,require
仍然存在一些開銷,因此您可以保護state $require_once = require Some::Module
之類的要求。這樣,這個需求就沒有運行時間的開銷。
一般來說:通過在模塊的頂級代碼中儘可能少地進行初始化,可以避免許多問題。傾向於懶惰並推遲初始化。另一方面,這種懶惰也會使你的系統變得更加動態和難以預測:很難說出已經發生了什麼樣的初始化。
更一般地說,想想你的設計。爲什麼需要這種循環依賴?您應該決定採用分層體系結構,其中高級代碼依賴於低級代碼,或者在低級代碼依賴於高級接口的情況下使用依賴性反轉。將兩者混合會導致可怕的混亂(展品A:這個問題)。
我明白一些數據模型必須具有共遞歸類。在這種情況下,通過將相互依賴的類放置在單個文件中來手動分揀訂單可能是最清楚的。