基本上,一个对象(结构)是通过组合不同的组件来构造的。每个具体组件都可以很容易地被另一个与接口(interface)匹配的组件交换(我猜是特征)。
我目前正在尝试使用 traits 来实现,这让我陷入了一些错误,并让我开始思考这是否在 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()); }
最佳答案
听起来您只是在询问特征、该特征的多个具体实现以及将自身限制为实现该特征的类型的包装对象。可选地,容器可以通过将特征委托(delegate)给内部对象来实现特征。
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);
}
it is not possible to have an array of such Players without using Boxed values
没错。数组或向量(或任何泛型,实际上)都需要属于相同类型。这对于数组/向量尤其重要,因为它们的内存布局是连续的,并且每个项目都需要有固定的间隔。
如果允许你有不同的类型,那么你可以让一个玩家的生命值占用 1 个字节,而另一个玩家的生命值占用 2 个字节。那么所有的偏移量都是不正确的。
您可以实现 Health
Box<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());
}
关于rust - 有没有一种惯用的方法来实现组件模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31897986/