多态效果 提高复用

OO中的多态是子类各有各的逻辑,调用时用父类类型但传入子类, 保障代码简约的同时大大提高扩展性,是OO的三大基本特征之一.

在下面的代码,使用两个特型就模拟出了类似的效果. 特型Spawn实体生成器有两个实现: EntityCommands普通实体生成器和RelatedSpawnerCommands关系实体生成器.

这里核心是实体生成器Spawn可以生成挂件Widgets. 不管是挂件还是实体生成器都是特型,后期就算新增了其他的实体生成器,这块的代码都不用修改,灵活.

/// An extension trait for spawning UI widgets.
pub trait Widgets {
      /// Spawn a simple button with text.
    fn button(&mut self, text: impl Into<String>) -> EntityCommands;

      /// Spawn a simple header label. Bigger than [`Widgets::label`].
    fn header(&mut self, text: impl Into<String>) -> EntityCommands;

      /// Spawn a simple text label.
    fn label(&mut self, text: impl Into<String>) -> EntityCommands;
}

impl<T: Spawn> Widgets for T {
    fn button(&mut self, text: impl Into<String>) -> EntityCommands {
        // 这里调用的是T::spawn,也就是子类的逻辑.
        self.spawn((
            Name::new("Button"),
            Button,
            Node {
                width: Px(200.0),
                height: Px(65.0),
                justify_content: JustifyContent::Center,
                align_items: AlignItems::Center,
                ..default()
            },
            BackgroundColor(NODE_BACKGROUND),
            InteractionPalette {
                none: NODE_BACKGROUND,
                hovered: BUTTON_HOVERED_BACKGROUND,
                pressed: BUTTON_PRESSED_BACKGROUND,
            },
            children![(
                Name::new("Button Text"),
                Text(text.into()),
                TextFont::from_font_size(40.0),
                TextColor(BUTTON_TEXT),
            )],
        ))
    }

    fn header(&mut self, text: impl Into<String>) -> EntityCommands { }
    fn label(&mut self, text: impl Into<String>) -> EntityCommands { }
}

/// An internal trait for types that can spawn entities.
/// This is here so that [`Widgets`] can be implemented on all types that
/// are able to spawn entities.
/// Ideally, this trait should be [part of Bevy itself](https://github.com/bevyengine/bevy/issues/14231).
trait Spawn {
      fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityCommands;
}

impl Spawn for Commands<'_, '_> {
      fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityCommands {
        self.spawn(bundle)
      }
}

impl<R: Relationship> Spawn for RelatedSpawnerCommands<'_, R> {
      fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityCommands {
        self.spawn(bundle)
      }
}