【问题标题】:Is there an idiomatic way to implement the component pattern?有没有一种惯用的方式来实现组件模式?
【发布时间】:2015-08-08 19:58:12
【问题描述】:

基本上一个对象(结构)是通过组合不同的组件来构造的。每个具体组件都可以很容易地被另一个匹配接口的组件交换(我猜是 trait)。

我目前正在尝试使用 trait 来实现,这让我陷入了一些错误,并让我开始思考这在 Rust 中是否很常见。

// usage example
fn main() {
    let obj = MainObject::new(Component1::new(), Component2::new(), Component3());
    // Where each component is a type(struct) with some well predefined methods.
}

这背后的主要思想是实现游戏中常用的组件模式。基本上,游戏将包含许多不同的对象,行为和包含的数据略有不同。对象不是有一个大的类层次结构,而是由标准组件组成,更完整的例子应该是。

pub struct Container
{
   input: InputHandlerComponent, // Probably a trait
   physics: PhysicsComponent,  // Probably a trait
   renderer: RendererCompoent // Probably a trait
}

impl Container {
  fn new(p: PhysicsComponent, i: InputComponent, r: RenderComponent) -> Container {
    Container {input: i, physics: p, renderer: r}
 }
}

struct ConcretePhysicsComponent;
impl PhysicsComponent for ConcretePhysicsComponent
{
  // ...
}

struct ConcreteInputComponent;
impl InputComponent for ConcreteInputComponent
{
  // ...
}

struct ConcreteRendererComponent;
impl RendererComponent for ConcreteRendererComponent
{
  // ...
}

struct AnotherConcreteRendererComponent;
impl RendererComponent for AnotherConcreteRendererComponent
{
  // ...
}

// usage example
fn main() {
    let obj = Container::new(ConcreteInputComponent::new(), ConcretePhysicsComponent::new(), ConcreteRendererComponent::new());
    // Where each component is a type(struct) with some well predefined methods.

    // This is a slightly modified version of this object, with changed rendering behaviour
    let obj2 = Container::new(ConcreteInputComponent::new(), ConcretePhysicsComponent::new(), AnotherConcreteRendererComponent::new());    }

【问题讨论】:

  • 添加了关于我要做什么的更详细的描述。我觉得应该可以。

标签: rust


【解决方案1】:

听起来您只是在询问特征、该特征的多个具体实现以及将自身限制为实现该特征的类型的包装器对象。或者,容器可以通过将 trait 委托给内部对象来实现 trait。

trait Health {
    fn life(&self) -> u8;
    fn hit_for(&mut self, lost_life: u8);
}

#[derive(Debug, Copy, Clone)]
struct WimpyHealth(u8);
impl Health for WimpyHealth {
    fn life(&self) -> u8 { self.0 }
    fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life * 2; }
}

#[derive(Debug, Copy, Clone)]
struct BuffHealth(u8);
impl Health for BuffHealth {
    fn life(&self) -> u8 { self.0 }
    fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life / 2; }
}

#[derive(Debug, Copy, Clone)]
struct Player<H> {
    health: H,
}

impl<H> Health for Player<H>
    where H: Health
{
    fn life(&self) -> u8 { self.health.life() }
    fn hit_for(&mut self, lost_life: u8) { self.health.hit_for(lost_life) }
}

fn main() {
    let mut player_one = Player { health: WimpyHealth(128) };
    let mut player_two = Player { health: BuffHealth(128) };

    player_one.hit_for(12);
    player_two.hit_for(12);

    println!("{:?}", player_one);
    println!("{:?}", player_two);
}

如果不使用 Boxed 值,就不可能有这样的玩家数组

没错。数组或向量(或任何泛型类型,真的)都需要是 same 类型。这对于数组/向量尤其重要,因为它们的内存布局是连续的,并且每个项目都需要处于固定的间隔。

如果允许你有不同的类型,那么你可以让一个玩家的生命值占用 1 个字节,而另一个玩家的生命值占用 2 个字节。那么所有的偏移量都是不正确的。

您可以为Box&lt;Health&gt; 实现Health 特征,然后Player 对象可以按顺序存储,但它们每个都有一个指向Health 的适当具体实现的指针。

impl<H: ?Sized> Health for Box<H>
    where H: Health
{
    fn life(&self) -> u8 { (**self).life() }
    fn hit_for(&mut self, lost_life: u8) { (**self).hit_for(lost_life) }
}

fn main() {
    let mut players = vec![
        Player { health: Box::new(WimpyHealth(128)) as Box<Health> },
        Player { health: Box::new(BuffHealth(128)) as Box<Health> }
    ];

    for player in players.iter_mut() {
        player.hit_for(42);
    }

    println!("{:?}", players[0].life());
    println!("{:?}", players[1].life());
}

【讨论】:

  • 实际上我偶然发现了这种方法的一个问题。据我了解,如果不使用 Boxed 值,就不可能拥有这样的 Player 数组。例如let arr = vec![Player { health: WimpyHealth(128) }, Player { health: BuffHealth(128) }]; 会失败,因为第一个播放器实例和第二个播放器实例具有不同的类型。
  • 我正在寻找一种将每个组件连续存储在数组中的方法, Vec> 是否会将玩家彼此相邻地存储在堆上?我会自己寻找答案,但我将把这个问题留在这里,作为上下文。
  • @6D65 不一定,要做这种事情(向量中的连续组件存储),您将不得不为您的组件使用大枚举或求助于 mem::转化和其他不安全的功能。
  • @Jsor 是的。我只是想采用 C 方式为每种类型的组件创建一个数组。该实体将是这些数组的索引。将不得不考虑如何使它不那么丑陋。这可能有助于拥有一个连续的数组。
猜你喜欢
  • 2018-12-04
  • 1970-01-01
  • 1970-01-01
  • 2019-11-23
  • 2019-03-24
  • 1970-01-01
  • 2012-10-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多