基于UML接口与抽象类聚合的太阳系模拟Applet实现技术问询
嘿,既然你已经搞定了太阳系Applet的动画效果,那离满分就差把代码对齐指定UML规范这一步啦!我来帮你梳理下怎么精准实现这个层级结构:
核心UML结构对应实现拆解
1. 先明确接口与类的层级关系
按照你提到的UML要求,核心约束是:
- Sun必须实现Orbit接口,同时作为顶层天体,持有通过
Planet构造函数创建的OrbitingBody实例(也就是行星) - Planet作为OrbitingBody的子类/实现类,自身也要持有通过卫星构造函数创建的
OrbitingBody实例(比如Moon) - 所有参与轨道运动的天体(Sun、Planet、Moon)都要遵循Orbit接口的规范
2. 代码实现的关键步骤
第一步:定义Orbit接口
先把接口的核心方法定下来,确保所有天体都遵循统一的动画逻辑规范:
public interface Orbit { // 每帧更新位置(轨道运动计算) void update(long deltaTime); // 绘制自身及下属天体 void draw(Graphics g); }
第二步:实现Sun类
Sun作为中心天体,实现Orbit接口,同时管理所有行星:
import java.util.ArrayList; import java.util.List; import java.awt.*; public class Sun implements Orbit { private List<OrbitingBody> planets; private int centerX, centerY; private final int SIZE = 40; public Sun(int x, int y) { this.centerX = x; this.centerY = y; planets = new ArrayList<>(); // 通过Planet构造函数添加行星,比如地球 planets.add(new Planet(this, 120, 0.002)); } @Override public void update(long deltaTime) { // 太阳自身固定不动,只更新所有行星的位置 for (OrbitingBody planet : planets) { planet.update(deltaTime); } } @Override public void draw(Graphics g) { // 绘制太阳 g.setColor(Color.YELLOW); g.fillOval(centerX - SIZE/2, centerY - SIZE/2, SIZE, SIZE); // 绘制所有行星及其卫星 for (OrbitingBody planet : planets) { planet.draw(g); } } // 提供给行星获取太阳位置的方法 public int getCenterX() { return centerX; } public int getCenterY() { return centerY; } }
第三步:抽象OrbitingBody类
这个类作为行星和卫星的父类,封装通用的轨道运动逻辑,避免重复代码:
import java.awt.*; public abstract class OrbitingBody implements Orbit { protected Orbit parentBody; protected int orbitRadius; protected double currentAngle; protected double orbitSpeed; protected int currentX, currentY; public OrbitingBody(Orbit parent, int radius, double speed) { this.parentBody = parent; this.orbitRadius = radius; this.orbitSpeed = speed; this.currentAngle = 0; } @Override public void update(long deltaTime) { // 计算当前轨道角度 currentAngle += orbitSpeed * deltaTime; // 根据父天体位置计算自身坐标 Point parentPos = getParentPosition(); currentX = parentPos.x + (int)(Math.cos(currentAngle) * orbitRadius); currentY = parentPos.y + (int)(Math.sin(currentAngle) * orbitRadius); // 更新下属天体(比如行星的卫星) updateChildBodies(deltaTime); } // 子类必须实现的方法:获取父天体的位置 protected abstract Point getParentPosition(); // 子类实现:更新自己的子天体 protected abstract void updateChildBodies(long deltaTime); }
第四步:实现Planet类
Planet继承OrbitingBody,同时管理自己的卫星:
import java.util.ArrayList; import java.util.List; import java.awt.*; public class Planet extends OrbitingBody { private List<OrbitingBody> moons; private final int SIZE = 20; private Color planetColor; public Planet(Orbit parent, int radius, double speed) { super(parent, radius, speed); moons = new ArrayList<>(); this.planetColor = Color.BLUE; // 通过Moon构造函数添加卫星,比如月球 moons.add(new Moon(this, 30, 0.005)); } @Override protected Point getParentPosition() { // 父天体是Sun,获取太阳的中心坐标 if (parentBody instanceof Sun) { Sun sun = (Sun) parentBody; return new Point(sun.getCenterX(), sun.getCenterY()); } return new Point(0, 0); } @Override protected void updateChildBodies(long deltaTime) { for (OrbitingBody moon : moons) { moon.update(deltaTime); } } @Override public void draw(Graphics g) { // 绘制行星 g.setColor(planetColor); g.fillOval(currentX - SIZE/2, currentY - SIZE/2, SIZE, SIZE); // 绘制所有卫星 for (OrbitingBody moon : moons) { moon.draw(g); } } // 提供给卫星获取行星位置的方法 public int getCurrentX() { return currentX; } public int getCurrentY() { return currentY; } }
第五步:实现Moon类
Moon继承OrbitingBody,作为最底层的天体:
import java.awt.*; public class Moon extends OrbitingBody { private final int SIZE = 10; public Moon(Orbit parent, int radius, double speed) { super(parent, radius, speed); } @Override protected Point getParentPosition() { // 父天体是Planet,获取行星的当前坐标 if (parentBody instanceof Planet) { Planet planet = (Planet) parentBody; return new Point(planet.getCurrentX(), planet.getCurrentY()); } return new Point(0, 0); } @Override protected void updateChildBodies(long deltaTime) { // 月球没有下属天体,空实现即可 } @Override public void draw(Graphics g) { // 绘制月球 g.setColor(Color.GRAY); g.fillOval(currentX - SIZE/2, currentY - SIZE/2, SIZE, SIZE); } }
3. 最后调整适配你的Applet
把这些类整合到你的Applet中,在paint()或者run()方法里调用Sun的update()和draw()方法就行,比如:
import java.applet.Applet; import java.awt.*; public class SolarSystemApplet extends Applet implements Runnable { private Sun sun; private Thread animationThread; @Override public void init() { sun = new Sun(getWidth()/2, getHeight()/2); animationThread = new Thread(this); animationThread.start(); } @Override public void run() { long lastTime = System.currentTimeMillis(); while (true) { long currentTime = System.currentTimeMillis(); long deltaTime = currentTime - lastTime; sun.update(deltaTime); repaint(); lastTime = currentTime; try { Thread.sleep(16); // 约60帧每秒 } catch (InterruptedException e) { e.printStackTrace(); } } } @Override public void paint(Graphics g) { sun.draw(g); } }
小提示
- 如果UML里有特定的方法名、成员变量或者访问修饰符要求,直接对应调整上面的代码就行
- 轨道运动的速度、半径这些参数可以根据你的动画效果微调,确保行星和卫星的运动节奏合理
- 可以给不同行星设置不同的颜色、大小,让太阳系更逼真
内容的提问来源于stack exchange,提问作者Diego Rodriguez
相关产品推荐
相关产品推荐

