ECS (Entity-Component-System) 即实体-组件-系统,是一种软件架构模式,遵循组合优于继承 (Composition Over Inheritance) 原则,主要用于游戏开发。在 ECS 架构中,场景内的每个对象(如敌人、子弹、车辆等)都是一个实体 (Entity),每个实体都由一个或多个包含数据(状态)的组件 (Component) 组成,架构允许灵活地定义、添加和删除实体,避免了难以理解、维护和扩展的继承层次造成的模糊性问题。

1. 组合优于继承 (Composition Over Inheritance)

面向对象程序设计 (OOP) 中的组合优于继承是指类应该倾向于通过组合,即包含实现所需功能的其他类的实例,而非从基类继承来实现多态行为和代码复用。

定义表示系统所具有行为的接口 (Interface) 是实现组合优于继承的基础,接口实现多态行为,而实现指定接口的类根据需要被添加到业务领域类 (Business Domain Classes) 中。通常情况下,业务领域类都是基类,无需任何继承,系统行为的替代实现通过另一个实现所需行为的接口来完成。包含对接口引用的类可以将对该接口实现的选择推迟到运行时。

以 C++ 为例,分别采用继承和组合的方式实现一个简单的系统。

1.1. 继承

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

以上定义了系统的两个接口,此时,可定义实现对应接口的业务领域类,如:

C++ 中多重继承十分复杂,且是一个具有很大风险的行为,可能导致钻石问题 (Diamond Problem)。为了避免使用多重继承,可以定义组合接口 VisibleAndMovableVisibleButNotMovable 等。但是,当接口很多时,此类组合将会导致大量重复的代码,难以维护和扩展。

1.2. 组合

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()) {}
}

1.3. 优点