实现“依赖注入”的技术

问题描述 投票:0回答:1

“依赖注入”是指向代码传递某种指针的面向对象概念。在 FP 中,这可以使用高阶函数来完成。

在 xslt 2+ 中似乎有这些替代方案

  • 传递函数(根据函数式编程)
  • 传递伪“模式”,即传递显式构造的元素(或嵌入某些节点结构中的值)并匹配它。

(还有其他替代方案可以使用 include/import 静态注入代码,其中包含样式表中的模板覆盖或与包含的样式表交互,但这更类似于“模板方法”,即静态组合)

XSLT 社区中有这方面的惯用/模式吗?

(如果能够以某种方式“捆绑”注入,就像接口或类型类所做的那样,这似乎可以使用伪模式技术,通过将“模式”作为元素或值嵌入到另一个元素中来实现,或者“模式”是一个“捆绑包”,但直接使用函数并不明显)


我没有给出明确的例子,如果您需要它们来明确我的意思,请告诉我。


实际上还有第三种选择,你返回 DSL,并“解释它”,不同的上下文以不同的方式解释,这可能类似于“管道”技术,虽然我非常喜欢它,但它有点矫枉过正当你只想注入一点细微差别时。


示例:

(都使用这个 xml - 这是不相关的

<root>
    <foo/>
</root>

#1“高阶函数”(我认为这可能是从 Martin Honnen 之前关于 HOF 的回答中窃取的)

函数“apply-function”允许“注入”函数然后执行该函数,显然任何具有兼容签名的函数都可以注入。这是默认的 FP 技术。

<xsl:stylesheet version="3.0" 
    xmlns:my="my"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <!-- Define a function that accepts a function parameter -->
    <xsl:function name="my:apply-function" as="xs:integer">
        <xsl:param name="input" as="xs:integer"/>
        <xsl:param name="function" as="function(xs:integer) as xs:integer"/>
        <xsl:sequence select="$function($input)"/>
    </xsl:function>
    
    <!-- Define a sample function to be passed as a parameter -->
    <xsl:function name="my:double" as="xs:integer">
        <xsl:param name="value" as="xs:integer"/>
        <xsl:sequence select="$value * 2"/>
    </xsl:function>
    
    <!-- Main template -->
    <xsl:template match="/">
        <result>
            <!-- Call my:apply-function and pass my:double as a parameter -->
            <xsl:variable name="result" select="my:apply-function(5, my:double#1)"/>
            <doubled-value>
                <xsl:value-of select="$result"/>
            </doubled-value>
        </result>
    </xsl:template>
</xsl:stylesheet>

#2“伪模式”

通过这种方式,您可以在某种结构中明确编码您的“选择”,然后对其进行匹配。

即“函数指针”被编码为

<Mode value='double'/>

我怀疑这种技术有一个更好的标签,但对我来说,它感觉像是一种模拟运行时“模式”的机制。

<xsl:stylesheet version="3.0" 
    xmlns:my="my"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template mode="inject" match="Mode[@value = 'double']">
        <xsl:param name="value" as="xs:integer"/>
        <xsl:sequence select="$value * 2"/>
    </xsl:template>

    <xsl:template mode="inject" match="Mode[@value = 'treble']">
        <xsl:param name="value" as="xs:integer"/>
        <xsl:sequence select="$value * 3"/>
    </xsl:template>
    
    <!-- Main template -->
    <xsl:template match="/">
        <result>
            <doubled-value>
                <xsl:variable name="mode">
                    <Mode value='double'/>
                </xsl:variable>
                <xsl:apply-templates mode="inject" select="$mode">
                    <xsl:with-param name="value" select="5"/>                    
                </xsl:apply-templates>
            </doubled-value>
        </result>
    </xsl:template>
</xsl:stylesheet>

#3 DSL/解释器

这里的“被调用者”代码返回一些由 my:makeDSL 编码为 xml 的特定于域的语言,然后由客户端特定的解释器进行解释。

这里的依赖关系并不是真正注入的,而是外部化的,然后以被调用者想要的方式解释,但结果是相同的。

(“nop”指令仅用于说明)

<xsl:stylesheet version="3.0" 
    xmlns:my="my"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template mode="inject" match="Mode[@value = 'double']">
        <xsl:param name="value" as="xs:integer"/>
        <xsl:sequence select="$value * 2"/>
    </xsl:template>

    <xsl:template mode="inject" match="Mode[@value = 'treble']">
        <xsl:param name="value" as="xs:integer"/>
        <xsl:sequence select="$value * 3"/>
    </xsl:template>
    
    <xsl:template mode="double" match="nop"/>
        
    <xsl:template mode="double" match="inject">
        <xsl:sequence select="2 * @value"/>
    </xsl:template>
    
    <xsl:template mode="treble" match="nop"/>
    
    <xsl:template mode="treble" match="inject">
        <xsl:sequence select="3 * @value"/>
    </xsl:template>
    
    <!-- Main template -->
    <xsl:template match="/">
        <result>
            <doubled-value>
                <xsl:apply-templates mode="double" select="my:makeDSL(5)"/>
            </doubled-value>
        </result>
    </xsl:template>
    
    <xsl:function name="my:makeDSL">
        <xsl:param name="value"/>
        <nop/>
        <inject value="{$value}"/>
        <nop/>
    </xsl:function>
</xsl:stylesheet>
xslt-2.0 xslt-3.0
1个回答
0
投票

目前,答案似乎是否定的(没有惯用的方法),至少不是有意识的。

我怀疑管道模式是惯用的,但在更一般的意义上,该语言鼓励采用类似于 OO 模板方法(即静态注入)的方法,其中包含/导入样式表并通过模式覆盖模板。

这并不是说它无法实现。

问题中的技术都是适用的,并且可以通过在Maps中嵌入函数来使用类似于“字典传递”的技术。

这是一个使用“monoid”的简单示例,即可以相加的事物。

我们有 2 个幺半群“字典”编码为映射,一个用于整数,一个用于字符串,我们将每个字典传递(注入)到一些代码中,“添加”一些值并返回答案。

输入的xml是这样的

<root>
    <foo value1="123" value2="456"/>
</root>

样式表首先将 foo 元素处理为整数,然后处理为字符串

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:kooks="kookerella.com"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    exclude-result-prefixes="#all"
    version="3.1">
    
    <xsl:function name="kooks:stringAdd" as="xs:string">
        <xsl:param name="s1" as="xs:string"/>
        <xsl:param name="s2" as="xs:string"/>
        <xsl:sequence select="concat($s1,$s2)"/>
    </xsl:function>
    
    <xsl:variable name="stringMonoid" as="map(*)">
        <xsl:map>
            <xsl:map-entry key="'zero'" select="''" as="xs:integer"/>
            <xsl:map-entry key="'add'">
                <xsl:sequence select="kooks:stringAdd#2"/>
            </xsl:map-entry>
        </xsl:map>
    </xsl:variable>
    
    <xsl:function name="kooks:integerAdd" as="xs:integer">
        <xsl:param name="i1" as="xs:integer"/>
        <xsl:param name="i2" as="xs:integer"/>
        <xsl:sequence select="$i1 + $i2"/>
    </xsl:function>
    
    <xsl:variable name="integerMonoid" as="map(*)">
        <xsl:map>
            <xsl:map-entry key="'zero'" select="0" as="xs:integer"/>
            <xsl:map-entry key="'add'">
                <xsl:sequence select="kooks:integerAdd#2"/>
            </xsl:map-entry>
        </xsl:map>
    </xsl:variable>
    
    <xsl:template match="/">
        <root>
            <integer>
                <xsl:apply-templates select="root/foo">
                    <xsl:with-param name="monoid" select="$integerMonoid"/>
                </xsl:apply-templates>
            </integer>
            <string>
                <xsl:apply-templates select="root/foo">
                    <xsl:with-param name="monoid" select="$stringMonoid"/>
                </xsl:apply-templates>
            </string>
        </root>    
    </xsl:template>
    
    <xsl:template match="foo">
        <xsl:param name="monoid"/>
    
        <result 
            value1="{@value1}" 
            value2="{@value2}" 
            zero="{map:get($monoid,'zero')}"
            sum="{map:get($monoid,'add')(@value1,@value2)}"/>
    </xsl:template>
</xsl:stylesheet>

导致这样的结果

<root>
   <integer>
      <result value1="123" value2="456" zero="0" sum="579"/>
   </integer>
   <string>
      <result value1="123" value2="456" zero="" sum="123456"/>
   </string>
</root>
© www.soinside.com 2019 - 2024. All rights reserved.