SAP Fiori Elements 应用里的 visitor 访问者设计模式

发布时间 2023-07-29 18:23:41作者: JerryWang_汪子熙

SAP Fiori Elements 应用 SmartTable title 控件的 text 属性,在运行时如何生成的?

在 TitleRenderer.js 里设置断点:

调用 getText 拿到 text property 值:

问题是这个 Text 属性是从哪里来的?

SmartTable.js 内部能够观察到这个 Products 字符串:

最终的入口还是 XMLTemplateProcessor.js:

应该是这些 XML fragment 加载触发的:

把 smarttable.fragment.xml 下载到本地打开,观察其 header 绑定信息:

{= !${parameter>/settings/quickVariantSelection/showCounts} ? ${path: 'header>TypeNamePlural', formatter: 'sap.ui.model.odata.AnnotationHelper.format'} : '' }

根据关键字 TypeNamePlural 进行搜索:

设置了断点,一个也没有触发。
PropertyBinding:

一个 visitor 模式:

源代码:

/**
			 * Visits the given attribute of the given element. If the attribute value represents a
			 * binding expression that can be resolved, it is replaced with the resulting value.
			 *
			 * @param {Element} oElement the XML DOM element
			 * @param {Attr} oAttribute one of the element's attribute nodes
			 * @param {sap.ui.core.template._with} oWithControl the "with" control
			 * @returns {sap.ui.base.SyncPromise}
			 *   A sync promise which resolves with <code>undefined</code> as soon as the
			 *   attribute's value has been replaced, or is rejected with a corresponding error if
			 *   getting the binding's value fails.
			 */
			function visitAttribute(oElement, oAttribute, oWithControl) {
				if (fnSupportInfo) {
					fnSupportInfo({context:undefined /*context from node clone*/, env:{caller:"visitAttribute", before: {name: oAttribute.name, value: oAttribute.value}}});
				}
				return resolveAttributeBinding(oElement, oAttribute, oWithControl)
					.then(function () {
						if (fnSupportInfo) {
							fnSupportInfo({context:undefined /*context from node clone*/, env:{caller:"visitAttribute", after: {name: oAttribute.name, value: oAttribute.value}}});
						}
					});
			}

			/**
			 * Visits all attributes of the given element. If an attribute value represents a
			 * binding expression that can be resolved, it is replaced with the resulting value.
			 *
			 * @param {Element} oElement the XML DOM element
			 * @param {sap.ui.core.template._with} oWithControl the "with" control
			 * @returns {sap.ui.base.SyncPromise}
			 *   A sync promise which resolves with <code>undefined</code> as soon as all
			 *   attributes' values have been replaced, or is rejected with a corresponding error if
			 *   getting some binding's value fails.
			 */
			function visitAttributes(oElement, oWithControl) {
				/*
				 * Comparator for DOM attributes by name.
				 *
				 * @param {Attr} oAttributeA
				 * @param {Attr} oAttributeB
				 * @returns {number} <0, 0, >0
				 */
				function comparator(oAttributeA, oAttributeB) {
					return oAttributeA.name.localeCompare(oAttributeB.name);
				}

				return stopAndGo(
					// Note: iterate over a shallow copy to account for removal of attributes!
					// Note: sort attributes by name to achieve a stable log order across browsers
					Array.prototype.slice.apply(oElement.attributes).sort(comparator),
					function (oAttribute) {
						return visitAttribute(oElement, oAttribute, oWithControl);
					});
			}

这段代码中所使用的设计模式为访问者模式(Visitor pattern)。

访问者模式是一种将算法与对象结构分离的设计模式。这种模式在对象结构中的元素上定义一个访问操作,该操作以一个访问者作为参数,访问者在该操作中实现了一个针对该元素的操作。

在这段代码中,visitAttributevisitAttributes 函数就充当了访问者的角色。它们对 XML DOM 元素及其属性进行访问,解析属性中可能存在的绑定表达式,并将解析后的结果替换原始属性值。

例如,visitAttribute 函数访问单个属性,通过 resolveAttributeBinding 解析可能的绑定表达式,并通过 Promise 结构异步替换属性值。而 visitAttributes 函数则访问元素的所有属性,通过调用 visitAttribute 对每个属性进行处理。

这种通过访问者访问和处理元素的方式,将元素与处理逻辑分离,增加了代码的可扩展性和复用性。当需要对元素进行新的操作时,只需要定义新的访问者,而无需修改元素类。同时,访问者模式也使得添加新的操作变得更简单,只需添加一个新的访问者即可。

这段代码还体现了访问者模式的另一特性,即访问者可以累积状态。在这段代码中,访问者通过 Promise 结构返回处理结果,这可以看作是访问者在访问过程中累积的状态。通过 Promise 结构,可以方便地处理异步操作和错误,这也是访问者模式的一种常见应用。