2009-09-27 97 views
2

我猜我以前曾經問過幾個類似的問題,但我在叢林中跳動。我認爲這是我不能放鬆的真正問題。處理無法實例化對象的最佳方法?

我正在處理一個third party library,有一個對象無法創建自己b2Bodyb2World必須instantiate it。我個人不太喜歡這種設計模式;我認爲b2Body應該能夠獨立於世界而存在,然後在需要時加入世界。無論如何,我已將b2Body與我自己的班級Body包裝在一起,因爲無論如何我都需要添加一些額外的東西。同樣,我有一個World包裝。現在,我想我有3種選擇:

  1. 有無Body的構造函數需要一個指針來World,以便它可以被完全實例化(調用b2World::CreateBody裏面的地方) - 即有像Body *b = new Body(world_ptr)
  2. Body構造一些World::CreateBody方法就像庫已經這樣做 - 即Body *b = world.CreateBody(params);
  3. 複製b2Body中的所有數據,以便您可以使用它,然而,將它添加到世界後,它將「切換」到使用b2Body數據 - 我。。Body b及更高版本world.addBody(b)

(1)和(2)的意思是,你不能有一個Body沒有World,我可能不會需要,但它可能是不錯的有選項[這樣我可以用它作爲其他對象的模板等]。不知道還有什麼其他的優點和缺點。 (3)似乎更好,但實現起來還有很多工作要做,這意味着我必須複製b2Body中已包含的大部分數據。

你的想法是什麼?我會CW這只是沒有人煩惱。


我還是不能躺下來休息。這是每一個選項是什麼樣子:

選項1:(我喜歡)

World w; 
Body b; 
Fixture f; 
b.addFixture(f); 
w.addBody(b); 

選項2:(在中間的某個地方)

World w; 
Body b(w); 
Fixture f(b); 

選項3:(Box2D如何操作)

World *w = new World; 
Body *b = w->CreateBody(args); 
Fixture *f = b->CreateFixture(args); 

選項2和3沒有太大的不同,但它改變了誰在控制創建對象。

雖然我會如何實際實現選項3? World::CreateBody()必須致電b2World::CreateBody(args),該號碼調用b2Body::b2Body()並返回b2Body,但從未呼叫Body::Body(args)這是一個問題。 b2Body會得到完全初始化,但我的包裝沒有地方做它的事情...更具體地說,我會怎麼寫World::CreateBody(const BodyDef &bd)?假設BodyDef繼承自b2BodyDef,來自b2Body的Body,來自b2World的World等等。

+4

我認爲這是一個完全合法的設計問題,不需要CW。 – djna 2009-09-27 07:52:39

+0

哦,好吧......我比答案更感興趣的答案......我忘了答案也無法獲得代表。哎呀。 – mpen 2009-09-27 18:03:41

回答

6

我想,如果你要使用第三方庫,如果你有超過哦,我不一個更好的原因,你應該只打它的設計不喜歡那種設計模式很多。你的圖書館有辦法做事 - 顯然,通過使用工廠對象 - 以及可能大幅增加代碼複雜性的戰鬥。

+0

呃......比這稍微多了一點。我使用Qt和Box2D,而Qt按照我的建議來完成。這將保持整個項目的一致性。但通過採用Box2D的做法,突然間我有兩種不同的設計模式。我想這不是什麼大事,我不應該拒絕。 – mpen 2009-09-27 18:05:48

2

聽起來像b2World對象是b2Body的工廠,所以作者已經決定了b2Body在沒有它的世界的情況下沒有意義。

我的第一反應是,這是接口,所以住它。讓你的世界對象成爲你的身體的工廠。因此,除了沒有公共構造函數之外,接近方法(1)的World對象具有makeBody()方法。

你認爲沒有世界的身體是有意義的嗎?如果是這樣,或許你發現Body方法的一些子集在沒有World的情況下可能是有用的,我不清楚你是如何實現它們的 - 他們顯然不能被b2Body實現,因爲如果沒有b2World,他不可能存在。所以,一種可能性是,你有一組的配置信息

class Body { 
     int howBig; 
     String name; 
     Flavour flavour; 
     // and getter/setters 
} 

現在這些(或東bgetters)顯然可以使感覺有或沒有世界。考慮到這一點,我想你可能會發現你實際上有Body的兩個「狀態」,一個與世界無關,一個是時。實際能力是不同。因此你實際上有兩個接口。

所以有一個IndependentBody類和一個Body類。世界工廠方法可能有一個簽名

World { 

    Body makeBody(IndependentBody); 

} 
+0

我認爲這就像(2)一樣。 「IndependentBody」就像Box2D的b2BodyDef。我希望刪除一層抽象。 – mpen 2009-09-27 18:12:56

1

我同意你不應該打擊你正在使用的第三方庫的設計。走向這條道路可能會在未來造成很多問題。

通過查看「底下」並創建包裝器,您可能會鎖定第三方庫的行爲,使其與當前實現的行爲方式相關。

如果未來版本的API保持不變,但基礎語義更改會發生什麼?

突然,從包裝的角度來看,一切都被打破了。

只是我的0.02。

1

按照你的鏈接,我看到createBody不返回b2Body,但指針一個:

b2Body* b2World::CreateBody (const b2BodyDef* def);  

這可能是因爲b2World

  1. 管理b2Body壽命(,刪除它和它在B2World超出範圍時使用的內存/本身被刪除)或

  2. 因爲B2Wsorld需要保持指向b2Body的指針,例如來遍歷它們來完成一些B2World功能。

我也注意到了所有的需要(除b2World)創建b2Body是一個指向b2BodyDef

所以,如果你想要一個b2Body不附加到b2World,但可以在稍後被附加到一個,爲什麼不傳遞b2BodyDefs或指向他們?

I 可能爲b2BodyDef創建薄包裝,例如,:

class b2BodyDefWrapper { 
    public const b2BodyDef& b2bodyDef; 
    public b2BodyDefWrapper(const b2BodyDef& bodydef) : b2bodyDef(bodydef) {} 
    public const b2Body* reifyOn(b2World& world) const { 
    return world.CreateBody(b2bodyDef) ; 
    } 
} 

注意,我會這樣b2BodyDefWrapper附加到多個世界,或者同一個世界一次以上。

現在,您可以對b2Body做些事情,而您無法對b2BodyDef做任何事情,並且傳遞(可能包裝的)b2BodyDefs不適合您的目的。在這種情況下,我可能會使用命令模式到功能列表「附加」的b2BodyDefWrapper,那將是對每一個具體化的b2Body「重播」:

class b2BodyDefWrapper { 
    private std::vector<Command&> commandStack; 
    public const b2BodyDef& b2bodyDef; 
    public b2BodyDefWrapper(const b2BodyDef& bodydef) : b2bodyDef(bodydef) {} 
    public const b2Body* reify(b2World& world) const { 
    b2body* ret = world.CreateBody(&b2bodyDef) ; 
    for (int i=0; i< commandStack.size(); i++) { 
     v[i].applyTo(ret) ; 
    } 
    return ret; 
    } 

    public void addCommand(const Command& command) { 
     commandStack.push_back(command); 
    } 
} 

哪裏Command是仿函數的抽象基類,像這樣:

class Command { 
    virtual ~Command() {} 
    virtual void applyTo(b2Body* body) = 0 ; 
    } 

與具體的子類:

class ApplyForce : public Command { 
    private const b2Vec2& force; 
    private const b2Vec2& point; 
    ApplyForce(const b2Vec2& f, const b2Vec2& p) : force(f), point(p) {} 
    virtual void applyTo(b2Body* body) { 
     body->ApplyForce(force, point) ; 
    } 
} 

然後,我可以用我的包裝是這樣的:

extern b2BodyDef& makeb2BodyDef(); 
b2BodyDefWrapper w(makeb2BodyDef() ) ; 
ApplyForce a(..., ...); 
w.addCommand(a) ; 
... 
b2World myworld; 
b2World hisWorld; 
w.reifyOn(myWorld) ; 
w.reifyOn(hisWorld) ; 

請注意,我省略了一些細節,主要是關於對象所有權和內存管理,以及誰在CommandStacks上調用delete;我在這些課程的草圖中也沒有遵循三的規則。你可以隨意填寫。

我也忽略了任何規定來調用,從一個命令,b2Body函數返回非空值並返回這些值;你可以通過ApplyTo返回某種聯合來覆蓋這個(如果你需要的話)。

更重要的是,我沒有介紹一個具體的Command如何將其返回值(如果有)提供給另一個具體的Command。一個完整的解決方案應該是沒有一個命令矢量,但是它們的一個樹,其中首先應用子命令,並且它們的返回值(如果有的話)被提供給它們的父命令。無論你是需要這樣的複雜性是一個我顯然無法回答的問題。 (我已經給出了一個非常詳細的答案,認爲我既沒有爲此付費,也沒有獲得聲望點,因爲你的社區維基是這個問題。)

+0

嗯,它是#2。其餘的我將不得不吸收......我不確定我對你的命令結構有什麼看法。我試圖簡化的東西:) – mpen 2009-09-27 18:30:11

1

box2D使用bodyDef對象的一個​​原因構造b2Body對象是爲了讓您可以重新使用def來創建多個body。代碼如:

b2BodyDef myDef; 
// fill out def 

for (int i=0; i < 100; ++i) { 
    for (int j=0; j < 100; ++j) { 
     myDef.x = i; 
     myDef.y = j 
     b2Body* body = world.CreateBody(myDef) 
    } 
} 

是一個非常有效和緊湊的方式創建具有相同特徵的許多對象。它不一定處於同一個循環中,您可以將def對象保留爲元數據,並根據需要從它們創建主體。

不要因爲它出於某種原因而與之對抗。

+0

你可以做到這一點沒有身體防禦,只是通過克隆和修改一些屬性。 – mpen 2009-10-08 23:08:20

+0

不知道這是否屬實。b2Body必須存在於一個世界中,因此克隆它並修改屬性可能會在身體被克隆的世界內產生副作用。它可能會在原始位置產生碰撞/接觸事件,而不是在屬性設置後的位置? 此外,這並沒有解決作爲一個模板保持周圍。將一個實際的b2Body作爲一個模板來克隆從而變得不那麼有意義。它在什麼世界?如果有什麼東西碰撞它會怎麼樣?它在模擬嗎? – 2009-10-12 16:26:27

+0

好吧,我建議你在這種情況下根本不會把它添加到世界上。這恰恰是一個擁有一個不屬於一個世界的身體的情況。另外,假設沒有任何多線程shannanigans正在進行,可能你會在下一個物理步驟運行之前完成更新,因此碰撞不會成爲問題。 – mpen 2009-10-16 06:23:52

相關問題