是否有一种惯用的方式来实现组件模式?

时间:2015-08-08 19:58:12

标签: rust

基本上,通过组合不同的组件来构造对象(struct)。每个具体组件都可以被与界面匹配的另一个组件轻松交换(我想特性)。

我目前正在尝试使用让我遇到一些错误的特性并让我开始思考这是否是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());    }

1 个答案:

答案 0 :(得分:5)

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

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

如果没有使用盒装值,则无法拥有此类玩家阵列

那是对的。数组或向量(或任何通用类型,实际上)需要都是相同的类型。这对于数组/向量尤其重要,因为它们的内存布局是连续的,每个项目都需要固定的间隔。

如果你被允许有不同的类型,那么你可以拥有一个拥有1个字节的健康状态的玩家和另一个拥有2个字节的健康状态的玩家。然后所有偏移都是不正确的。

您可以为Health实现Box<Health>特征,然后可以按顺序存储Player个对象,但每个对象都有一个指向{{的具体实现的指针1}}通过盒子。

Health