模拟中“动态”对象的结构

问题描述:

我想创建一个或多或少的简单重力模拟,我目前正在对结构做出一些决定。我遇到了一个问题,我不知道如何解决“很好”。这是问题的一个简单的类图:模拟中“动态”对象的结构

The Problem

在你可能无法看到任何问题,但让我告诉你一个额外的其实是一个先来看看:如果一颗恒星达到一定的密度(史瓦西半径> Radius),它应该形成一个BlackHole。那么我怎么能告诉模拟交换一个BlackHole的实例?我可以将模拟实例添加到每个主体,然后他们自己可以修改主体数组,但必须有更好的方法!

另一种方法是让Planet,Star和BlackHole成为一个新类BodyType的子类,然后Body就会保存一个实例。

您认为如何?你会如何以一种漂亮干净的方式解决这个问题?任何帮助表示赞赏!

编辑:为了进一步说明,我提到了两种方法的here is an illustration。哪一个更好?还是有没有更好的,我没有提到?

也许这可能有帮助;在运行时改变行为的一种常见方法是战略模式;其思想是保持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更改它。 –