【问题标题】:Structure for "dynamic" objects in a simulation模拟中“动态”对象的结构
【发布时间】:2017-12-12 07:15:18
【问题描述】:

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

乍一看你可能看不出任何问题,但让我告诉你一个额外的事实:如果一颗恒星达到一定的密度(Schwarzschildradius > Radius),它应该形成一个黑洞。那么我怎样才能告诉 Simulation 将 Star 实例与 BlackHole 交换呢?我可以为每个身体添加一个模拟实例,然后他们自己可以修改身体数组,但必须有更好的方法!

另一种方法是,让 Planet、Star 和 BlackHole 成为新类 BodyType 的子类,然后 Body 将包含一个实例。

你怎么看?你将如何以一种干净利落的方式解决这个问题?任何帮助表示赞赏!

编辑:为了进一步澄清,here is an illustration 我提到的两种方法。两者哪个更好?或者有没有更好的我没有提到的?

【问题讨论】:

    标签: java data-structures game-physics simulator


    【解决方案1】:

    也许这会有所帮助;在运行时改变行为的一种常用方法是策略模式;这个想法是保持 Body 类中所有身体类型的共同点,并将不同的行为分成不同的策略,这样当身体改变类型时,策略就可以交换。它们可以实现一个接口,比如 BodyBehavior,而不是让 BlackHole、Planet 和 Star 扩展 Body;

    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;
    }
    

    【讨论】:

    • 很有趣的图案,我喜欢!但我仍然有一个问题。因为 BodyBehaviour 实现了特定于类型的代码,它还需要决定何时更改为什么新的 BodyBehaviour。所以它仍然需要引用 Body,对吧?
    • 请听到你喜欢它!我进行了编辑,希望能澄清并封装 star->bh 转换的测试。你是对的,我们将自我引用(this)传递给策略方法,但它是持有对 BodyBehavior 引用的主体,而不是另一种方式。由于策略是无状态的(没有成员变量),我们只需要每种类型的一个实例,无论您的模拟中有多少物体。希望有帮助!
    • 我想我会采用这种方法,稍作修改:我将使用更通用的 needsTransition() 函数和第二个转换(Body body)而不是特定的 hasBecomeBlackHole() 函数) 功能。这样我就可以实现多个可能的转换,而无需数十个函数。因此,在 Body 类中,它检查每个更新是否需要转换,如果需要,它将通过转换(Body body)传递其引用,以便 BodyBehaviour 可以做必要的事情。或者第二个函数可以返回一个 BodyBehaviour,以便 Body 将其更改为那个。
    • 当然,如果您有其他过渡要处理,那么您更一般的需求Transition 方法听起来是正确的。
    • 谢谢!这确实大大改善了我的结构。我目前的方法可能是最糟糕的(使用静态方法访问模拟),这就是我重做很多东西的原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多