2017-07-07 84 views
1

我想創建一個或多或少的簡單重力模擬,我目前正在對結構做出一些決定。我遇到了一個問題,我不知道如何解決「很好」。這是問題的一個簡單的類圖:模擬中「動態」對象的結構

The Problem

在你可能無法看到任何問題,但讓我告訴你一個額外的其實是一個先來看看:如果一顆恆星達到一定的密度(史瓦西半徑> Radius),它應該形成一個BlackHole。那麼我怎麼能告訴模擬交換一個BlackHole的實例?我可以將模擬實例添加到每個主體,然後他們自己可以修改主體數組,但必須有更好的方法!

另一種方法是讓Planet,Star和BlackHole成爲一個新類BodyType的子類,然後Body就會保存一個實例。

您認爲如何?你會如何以一種漂亮乾淨的方式解決這個問題?任何幫助表示讚賞!

編輯:爲了進一步說明,我提到了兩種方法的here is an illustration。哪一個更好?還是有沒有更好的,我沒有提到?

回答

0

也許這可能有幫助;在運行時改變行爲的一種常見方法是戰略模式;其思想是保持Body類中所有體型的共同點,並將不同的行爲分解爲不同的策略,這樣,只要身體改變類型,策略就可以交換。而不是讓BlackHole,Planet和Star擴展Body,他們可以實現一個接口,比如BodyBehavior;

public interface BodyBehaviour { 
    String getType(); 
    Double getLuminosity (Body body); 
    // anything else you need that's type specific.... 
} 

public class PlanetBodyBehavior implements BodyBehaviour { 
    public static final String TYPE = "Planet" 
    @Override 
    public String getType() { 
     return TYPE; 
    } 

    @Override 
    public Double getLuminosity(Body body) { 
     // planet specific luminosity calculation - probably 0 
     return calculatedLuminosity; 
    } 
} 

而你的身體類會看起來像這樣;

public class Body { 
    private Double mass; 

    // coordinates,radius and other state variables 

    private BodyBehavior bodyBehaviour; 

    public void setBodyBehaviour (BodyBehaviour behaviour) { 
     this.bodyBehaviour = behaviour; 
    } 

    public String getBodyType() { 
     bodyBehaviour.getType(); 
    } 

    public Double getLuminosity() { 
     bodyBehavior.getLuminosity(this); 
    } 
} 

而對於你的模擬,你可以有一些沿線的東西;

// init - note we keep our behaviors stateless so we only need one instance of each 
public class Simulation() { 
    BodyBehaviour planetBehavior = new PlanetBodyBehavior(); 
    BodyBehaviour starBehavior = new StarBodyBehavior() 
    BodyBehaviour blackHoleBehavior = new BlackHoleBodyBehavior() 
    // Just showing initialisation for a single star... 
    Body body = new Body(initilising params....) 
    body.setBehaviour (starBehavior); 

     // iterations.... 
    while (!finished) { 
     // assume we have many bodies and loop over them 
     for (Body body : allBodies) { 
      // update body positions etc. 
      // check it's still a star - don't want to be changing it every iteration... 
      if (body.hasBecomeBlackHole() { 
       // and this is the point of it all.... 
       body.setBodyBehaviour(blackHoleBehavior); 
      } 
     } 
    } 
} 

快樂模擬!

注意在這種情況下,它是調用行爲更改的頂級模擬循環,但可以將其添加到您的BodyBehavior;

boolean hasBecomeBlackHole (Body body); 

和身體,你可以做類似

public boolean hasBecomeBlackHole() { 
    bodyBehaviour.hasBecomeBlackHole(this); 
} 

對於StarBodyBehavior;

public boolean hasBecomeBlackHole (final Body body) { 
    return doSomeCalculations(body.getMass(), body.getRadius()); 
} 

而對於行星和黑洞

public boolean hasBecomeBlackHole (final Body body) { 
    return false; 
} 
+0

非常有趣的模式,我喜歡它!但我仍然有一個問題。由於BodyBehaviour實現了類型特定的代碼,因此它還需要決定何時更改爲新的BodyBehaviour。所以它仍然需要對Body的引用,對吧? –

+0

請聽你喜歡它!我已經編輯過希望澄清和封裝star-> bh轉換的測試。您正確的是我們將自引用(this)傳遞給策略方法,但是它是BodyBehavior的引用的主體,而不是另一種方式。由於策略是無狀態的(沒有成員變量),我們只需要每種類型的一個實例,而不管模擬中有多少個實體。希望有所幫助! – GrumpyWelshGit

+1

我想我會採用這種方法,稍作修改:而不是特定的hasBecomeBlackHole()函數,我將使用更通用的needsTransition()函數和第二個轉換(Body body)函數。這樣我可以實現多個可能的轉換而不需要幾十個函數。所以在Body類中它會檢查每個更新是否需要轉換,如果是,它將通過轉換(Body body)傳遞其引用,以便BodyBehaviour可以完成必要的工作。或者第二個函數可以返回BodyBehaviour,以便Body更改它。 –