当前位置: 首页 > news >正文

前端设计模式 - 树对象结构与订阅发布者模式 Unity与Qt【极简版】

树本身很好的体现了对象的层级与局部性,在此基础上很容易应用各种设计模式

树结构中,每个节点(TreeNode)可同时作为发布者订阅者

  • 订阅者:节点可以订阅其他节点(通常是父节点或祖先节点)的特定事件(如状态变更、数据更新)。
  • 发布者:节点状态变化时,向所有订阅者(通常是子节点或后代节点)发布事件,触发响应逻辑。

通过这种双向通信机制,树节点无需硬编码依赖彼此的引用,只需通过事件接口交互,降低耦合度。

Qt 如何体现我们的设计原则

通过订阅-发布者模式,树结构的节点通信从“硬依赖”转变为“松耦合”,尤其适合复杂UI组件(如树形控件、组织架构图)或数据结构(如AST语法树)的设计。、

[!question] 为什么父不订阅子对象信号而直接调用或委托

  1. 父对象对子对象本就有一种隐式的依赖,直接调用或者 委托(强引用)可以减少 消息&事件 带来的性能损失,同时简单调用链可直接追踪。
    对于需要编译时语言通常如此,对于运行时语言就不一定了,一般也是用的信号传递,因为父对象可能也不预先知道他有几个子对象,无法强引用
  2. 子对象不确定他属于哪个父对象或者爷爷对象,所以需要使用 消息&事件 保持解耦。同时对于非局部要向外扩展的消息可以利用事件系统广播

Qt 中的两种通信方式

1. 直接调用方法(父对子)

// 父对象直接调用子对象的方法
QWidget* parent = new QWidget;
QPushButton* button = new QPushButton("Click me", parent); // 父子关系// 父对象直接调用子的方法 - 不需要信号槽
button->setText("New Text");
button->setEnabled(false);
button->move(100, 100);

2. 信号槽机制(子对父/跨对象)

// 子对象通过信号通知父对象(不确定的监听者)
class MyWidget : public QWidget {Q_OBJECT
public:MyWidget() {QPushButton* button = new QPushButton("Click me", this);// 父对象订阅子的信号connect(button, &QPushButton::clicked, this, &MyWidget::onButtonClicked);}private slots:void onButtonClicked() {// 响应子对象的信号qDebug() << "Button was clicked!";}
};

Qt 如何体现我们的设计原则

原则1:父直接调用子(性能+精确控制)

// 父窗口直接操作子控件
QMainWindow* window = new QMainWindow;
QStatusBar* statusBar = new QStatusBar(window);// 直接调用 - 高效、精确
statusBar->showMessage("Loading...");
statusBar->setSizeGripEnabled(true);// 布局管理也是直接调用
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(new QPushButton("OK"));
layout->addWidget(new QPushButton("Cancel"));

原则2:子通过信号通知父(解耦)

// 按钮不知道谁会响应它的点击
// 可能是直接父级、祖父级、甚至是完全无关的对象
QPushButton* button = new QPushButton;// 多个可能的监听者
connect(button, &QPushButton::clicked, dialog, &QDialog::accept);
connect(button, &QPushButton::clicked, logger, &Logger::logButtonClick);
connect(button, &QPushButton::clicked, analytics, &Analytics::trackEvent);

Unity 中的通信方式与设计原则

Unity 的设计哲学与 Qt 类似,但具体实现机制不同。让我展示 Unity 中如何体现同样的设计原则:

Unity 中的两种通信方式

1. 直接调用方法(父对子)

// 父对象直接调用子对象的方法
public class ParentController : MonoBehaviour
{private ChildComponent child;void Start(){// 获取子对象的引用child = GetComponentInChildren<ChildComponent>();// 或者通过Transform查找// child = transform.Find("ChildObject").GetComponent<ChildComponent>();}void Update(){// 父对象直接调用子的方法child.UpdatePosition(new Vector3(10, 0, 0));child.SetEnabled(true);child.ChangeColor(Color.red);}
}public class ChildComponent : MonoBehaviour
{public void UpdatePosition(Vector3 newPos){transform.position = newPos;}public void SetEnabled(bool enabled){gameObject.SetActive(enabled);}public void ChangeColor(Color color){GetComponent<Renderer>().material.color = color;}
}

2. 事件系统(子对父/跨对象)

// 子对象通过事件通知父对象
public class ButtonController : MonoBehaviour
{// 定义事件public event System.Action OnButtonClicked;public event System.Action<int> OnValueChanged;void Update(){if (Input.GetMouseButtonDown(0)){// 触发事件,不知道谁会监听OnButtonClicked?.Invoke();}}public void SetValue(int value){// 触发值变化事件OnValueChanged?.Invoke(value);}
}public class UIManager : MonoBehaviour
{void Start(){// 父对象订阅子对象的事件ButtonController button = FindObjectOfType<ButtonController>();button.OnButtonClicked += HandleButtonClick;button.OnValueChanged += HandleValueChange;}void HandleButtonClick(){Debug.Log("Button was clicked!");}void HandleValueChange(int newValue){UpdateUI(newValue);}
}

Unity 如何体现设计原则

原则1:父直接调用子(性能+精确控制)

// 父对象直接操作子对象
public class GameManager : MonoBehaviour
{public PlayerController player;public EnemySpawner spawner;public UIScoreDisplay scoreDisplay;void StartGame(){// 直接调用 - 高效、精确控制player.ResetPosition();player.SetHealth(100);spawner.StartSpawning();scoreDisplay.ResetScore();}void EndGame(){// 直接停止所有子系统spawner.StopAllCoroutines();player.DisableControls();scoreDisplay.ShowGameOver();}
}

原则2:子通过事件通知父(解耦)

// 子对象不知道谁会监听它的事件
public class HealthComponent : MonoBehaviour
{public event System.Action<float> OnHealthChanged;public event System.Action OnDeath;private float currentHealth = 100;public void TakeDamage(float damage){currentHealth -= damage;OnHealthChanged?.Invoke(currentHealth);if (currentHealth <= 0){OnDeath?.Invoke();}}
}// 多个可能的监听者
public class PlayerUI : MonoBehaviour
{void Start(){HealthComponent health = GetComponent<HealthComponent>();health.OnHealthChanged += UpdateHealthBar;}void UpdateHealthBar(float health) { /* 更新血条UI */ }
}public class AchievementSystem : MonoBehaviour
{void Start(){HealthComponent health = FindObjectOfType<Player>().GetComponent<HealthComponent>();health.OnDeath += UnlockDeathAchievement;}void UnlockDeathAchievement() { /* 解锁成就 */ }
}public class SoundManager : MonoBehaviour
{void Start(){HealthComponent health = FindObjectOfType<Player>().GetComponent<HealthComponent>();health.OnHealthChanged += PlayHurtSound;}void PlayHurtSound(float health) { /* 播放受伤音效 */ }
}
http://www.zskr.cn/news/42717.html

相关文章:

  • 2025年11月园区车辆管理系统推荐榜:五强对比评测与选型指南
  • 《投资-99》价值投资者的认知升级与交易规则重构 - 什么是周期性股票?有哪些周期性股票?不同周期性股票的周期多少?周期性股票的买入和卖出的特点? - 详解
  • 2025年新店微信朋友圈推广权威推荐榜单:腾讯朋友圈推广/腾讯朋友圈广告开户/微信朋友圈本地推广源头服务商精选
  • 初学:运用工具进行SQL注入
  • 2025年靠谱的薄壁不锈钢管厂家最新推荐权威榜
  • 爱思益联系方式: 如何验证信息真伪
  • Newstar web week4
  • 2025 年长沙打印机出租平台口碑推荐榜单重磅揭晓,最新推荐高性价比租赁之选含全包 / 低价 / 长短期 / 包维修服务公司推荐
  • 2025 年自动感应门 / 旋转门 / 平移门厂家选购指南,多玛自动门(上海)有限公司专业门控解决方案解析
  • 2025年上轨隐藏式全景门生产厂家权威推荐榜单:无下轨全景门/折叠全景门/联动移门全景门源头厂家精选
  • 2025年石棉橡胶板厂家联系电话推荐:工业密封材料采购指南
  • 2025年热门的双锥干燥机TOP实力厂家推荐榜
  • 2025年广东叛逆机构权威推荐榜单:素质教育/早恋教育/厌学源头机构精选
  • 2025年热门的防火门TOP实力厂家推荐榜
  • 2025年专业的工业制氮机设备品牌厂家排行榜
  • Linux内核开发_将Linux内核打包成img文件
  • 2025年热门的艺术楼梯制作高评价厂家推荐榜
  • vue vant适配 - 东方不败-
  • 2025年评价高的数据中心展观众登记
  • 心情日记 | 而一切终归于平静
  • 2025年上海智能运维智算中心展会议论坛
  • 搭建AI资讯早报:AiOnly全球大模型服务+N8N自动化工作流实战
  • 2025年靠谱的新中式香氛五金厂家最新权威实力榜
  • 2025年评价高的通用水性色浆厂家最新推荐权威榜
  • 2025年机械设备制造行业SAP实施商哪家好?哪家比较专业?
  • 2025年质量好的防静电珍珠棉厂家最新热销排行
  • 2025年口碑好的折叠伸缩门杭州老房装修
  • 2025 年 11 月学习平板权威排名:新课标适配与 AI 精准学双维度测评
  • 完整教程:论文阅读:arxiv 2025 Scaling Laws for Differentially Private Language Models
  • 2025年11月化妆培训学校评测榜:南昌妆典领衔优选排行