You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

封装依赖对象组合行为的优雅方式及多形状碰撞检测策略设计

这两个问题刚好是图形/游戏开发里非常经典的设计场景,我来分享下我在实际项目中用过的优雅解决方案:

一、封装依赖关联对象组合行为的优雅方式

首先,这类场景核心要解决的是别让主对象变成“大杂烩”,同时让行为可以灵活组合、动态替换。我最推荐的是基于接口抽象的委托模式,完全贴合开闭原则:

  • 第一步:把需要组合的行为抽成独立接口。比如你的物体有碰撞、移动两种核心行为,就定义CollisionBehavior(包含碰撞检测方法)和MovementBehavior(包含位置更新方法)。
  • 第二步:主对象(比如GameObject)只持有这些接口的实例,而不是具体实现类。比如在GameObject里声明private CollisionBehavior collisionBehavior;,完全不关心它是圆形碰撞还是多边形碰撞。
  • 第三步:主对象的相关方法直接“甩锅”给行为实例。比如GameObject的checkCollision(GameObject other)方法里,直接返回collisionBehavior.checkCollision(this, other);,自己只做协调工作。
  • 第四步:具体行为实现独立编写,比如ShapeCollisionBehavior、BoundingBoxCollisionBehavior,需要时直接注入到GameObject中就行。

这种设计的好处太明显了:

  • 行为和主对象彻底解耦,新增行为根本不需要改GameObject的代码,完全符合开闭原则。
  • 同一个对象可以动态切换行为(比如某个物体从圆形碰撞切换到多边形碰撞),只需要替换对应的行为实例。
  • 代码职责清晰,每个行为类只干自己的事,维护和测试都方便。

举个简化的Java代码片段(其他语言思路完全一致):

// 抽象碰撞行为接口
public interface CollisionBehavior {
    boolean checkCollision(GameObject self, GameObject other);
}

// 主游戏对象类
public class GameObject {
    private CollisionBehavior collisionBehavior;
    private Shape shape;

    public GameObject(CollisionBehavior behavior, Shape shape) {
        this.collisionBehavior = behavior;
        this.shape = shape;
    }

    public boolean checkCollision(GameObject other) {
        return collisionBehavior.checkCollision(this, other);
    }

    // 提供形状的访问方法
    public Shape getShape() { return shape; }
}

// 基于形状的碰撞行为实现
public class ShapeBasedCollisionBehavior implements CollisionBehavior {
    private final CollisionDetector detector = new CollisionDetector();

    @Override
    public boolean checkCollision(GameObject self, GameObject other) {
        return detector.check(self.getShape(), other.getShape());
    }
}
二、多形状碰撞检测的实现策略(低重复、易扩展)

这个场景的核心痛点是避免写一堆重复的if-else或者O(n²)的代码,同时新增形状时不用动现有代码。我用过最优雅的方案是双重分派(Double Dispatch)+ 抽象形状基类,本质是访问者模式的实战应用:

具体实现步骤:

  1. 定义抽象形状基类:
    所有形状都继承这个基类,只需要一个接受“碰撞访问者”的方法:

    public abstract class Shape {
        public abstract <T> T accept(ShapeVisitor<T> visitor);
    }
    
  2. 定义形状访问者接口:
    这个接口为每个具体形状定义一个访问方法,返回值可以是碰撞检测的结果(boolean):

    public interface ShapeVisitor<T> {
        T visitCircle(Circle circle);
        T visitPolygon(Polygon polygon);
        T visitLine(Line line);
        // 以后新增形状,就在这里加对应的visit方法就行
    }
    
  3. 实现具体形状类:
    每个形状实现accept方法,调用访问者对应的visit方法,把自己传进去:

    public class Circle extends Shape {
        public float x, y, radius;
    
        @Override
        public <T> T accept(ShapeVisitor<T> visitor) {
            return visitor.visitCircle(this);
        }
    }
    
    public class Polygon extends Shape {
        public List<Point> vertices;
    
        @Override
        public <T> T accept(ShapeVisitor<T> visitor) {
            return visitor.visitPolygon(this);
        }
    }
    
  4. 实现核心碰撞检测器:
    这是整个逻辑的核心,通过双重分派来处理任意形状对的碰撞:

    public class CollisionDetector {
        public boolean check(Shape a, Shape b) {
            // 第一次分派:让a自己决定怎么处理和b的碰撞
            return a.accept(new ShapeVisitor<Boolean>() {
                @Override
                public Boolean visitCircle(Circle circleA) {
                    // 第二次分派:让b自己决定怎么和circleA碰撞
                    return b.accept(new ShapeVisitor<Boolean>() {
                        @Override
                        public Boolean visitCircle(Circle circleB) {
                            return checkCircleCircle(circleA, circleB);
                        }
    
                        @Override
                        public Boolean visitPolygon(Polygon polygon) {
                            return checkCirclePolygon(circleA, polygon);
                        }
    
                        @Override
                        public Boolean visitLine(Line line) {
                            return checkCircleLine(circleA, line);
                        }
                    });
                }
    
                @Override
                public Boolean visitPolygon(Polygon polygonA) {
                    return b.accept(new ShapeVisitor<Boolean>() {
                        @Override
                        public Boolean visitCircle(Circle circle) {
                            // 复用圆和多边形的碰撞逻辑,只需要调换参数
                            return checkCirclePolygon(circle, polygonA);
                        }
    
                        @Override
                        public Boolean visitPolygon(Polygon polygonB) {
                            return checkPolygonPolygon(polygonA, polygonB);
                        }
    
                        @Override
                        public Boolean visitLine(Line line) {
                            return checkPolygonLine(polygonA, line);
                        }
                    });
                }
    
                @Override
                public Boolean visitLine(Line lineA) {
                    return b.accept(new ShapeVisitor<Boolean>() {
                        @Override
                        public Boolean visitCircle(Circle circle) {
                            return checkCircleLine(circle, lineA);
                        }
    
                        @Override
                        public Boolean visitPolygon(Polygon polygon) {
                            return checkPolygonLine(polygon, lineA);
                        }
    
                        @Override
                        public Boolean visitLine(Line lineB) {
                            return checkLineLine(lineA, lineB);
                        }
                    });
                }
            });
        }
    
        // 具体的碰撞算法,每个形状对只写一次
        private boolean checkCircleCircle(Circle a, Circle b) {
            float dx = a.x - b.x;
            float dy = a.y - b.y;
            float distanceSquared = dx*dx + dy*dy;
            float radiusSum = a.radius + b.radius;
            return distanceSquared <= radiusSum * radiusSum;
        }
    
        private boolean checkCirclePolygon(Circle circle, Polygon polygon) {
            // 这里实现圆和多边形的碰撞逻辑,比如用分离轴定理
            // ...
        }
    
        // 其他形状对的碰撞方法同理...
    }
    

这个方案的优势:

  • 极低的代码重复:每个形状对的碰撞逻辑只需要实现一次,比如圆和多边形的逻辑,在圆访问多边形和多边形访问圆时可以直接复用,完全不用重复写。
  • 超级容易扩展:新增一个形状(比如Rectangle)时,只需要做3件事:
    1. 新增Rectangle类继承Shape,实现accept方法。
    2. 在ShapeVisitor接口里加visitRectangle(Rectangle rectangle)方法。
    3. 在CollisionDetector的各个内部访问者里加对应的处理逻辑,再实现checkRectangleX的具体算法。
      完全不需要修改现有形状的代码,完美符合开闭原则。
  • 逻辑清晰易维护:所有碰撞逻辑都集中在CollisionDetector里,形状类只负责存储自身数据,不需要关心碰撞逻辑,后期维护和测试都非常方便。

最后补个小技巧:可以把通用的碰撞算法(比如分离轴定理)抽成独立的工具类,让CollisionDetector的代码更简洁,也方便在其他地方复用。


内容的提问来源于stack exchange,提问作者MonkeyKhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:38:43