ECS (Entity-Component-System) 即实体-组件-系统,是一种软件架构模式,遵循组合优于继承 (Composition Over Inheritance) 原则,主要用于游戏开发。在 ECS 架构中,场景内的每个对象(如敌人、子弹、车辆等)都是一个实体 (Entity),每个实体都由一个或多个包含数据(状态)的组件 (Component) 组成,架构允许灵活地定义、添加和删除实体,避免了难以理解、维护和扩展的继承层次造成的模糊性问题。
面向对象程序设计 (OOP) 中的组合优于继承是指类应该倾向于通过组合,即包含实现所需功能的其他类的实例,而非从基类继承来实现多态行为和代码复用。
定义表示系统所具有行为的接口 (Interface) 是实现组合优于继承的基础,接口实现多态行为,而实现指定接口的类根据需要被添加到业务领域类 (Business Domain Classes) 中。通常情况下,业务领域类都是基类,无需任何继承,系统行为的替代实现通过另一个实现所需行为的接口来完成。包含对接口引用的类可以将对该接口实现的选择推迟到运行时。
以 C++ 为例,分别采用继承和组合的方式实现一个简单的系统。
class Object {
public:
virtual void update() {}
virtual void draw() {}
virtual void collide(Object objects[]) {}
};
class Visible: public Object {
public:
virtual void draw() override {
// draw
}
};
class Movable: public Object {
public:
virtual void update() override {
// update
}
};
以上定义了系统的两个接口,此时,可定义实现对应接口的业务领域类,如:
Player
:是 Visible
且 Movable
Building
:Visible
但不是 Movable
C++ 中多重继承十分复杂,且是一个具有很大风险的行为,可能导致钻石问题 (Diamond Problem)。为了避免使用多重继承,可以定义组合接口 VisibleAndMovable
、VisibleButNotMovable
等。但是,当接口很多时,此类组合将会导致大量重复的代码,难以维护和扩展。
class VisibilityDelegate {
public:
virtual void draw() = 0;
};
class Visible: public VisibilityDelegate {
public:
virtual void draw() override {
// draw
}
};
class NotVisible: public VisibilityDelegate {
public:
virtual void draw() override {
// do nothing
}
};
class UpdateDelegate {
public:
virtual void update() = 0;
};
class Movable: public UpdateDelegate {
public:
virtual void update() override {
// update
}
};
class NotMovable: public UpdateDelegate {
public:
virtual void update() override {
// do nothing
}
};
此时,再实现业务领域类
class Object {
public:
Object(VisibilityDelegate* v, UpdateDelegate* u) : v_(v), u_(u) {}
void draw() { v_->draw(); }
void update() { u_->update(); }
private:
VisibilityDelegate* v_;
UpdateDelegate* u_;
};
class Player {
public:
Player() : Object(new Visible(), new Movable()) {}
};
class Building {
public:
Building() : Object(new Visible(), new NotMovable()) {}
}