从写组件说Xml——Xsd篇(二)

千变万化的表达式

    上回说到,去和组件的合作者以及组件的使用者讨论,还是从上一篇的最后的Xml开始:

image

    还记得上一回说过,要完成这样一个修改pptx文本的几个要素吗?

  • 在哪里修改(where)
  • 修改成什么(what)
  • 怎么修改(how to)
  • 何时修改(when)

    而上一次仅仅解答了where、what的问题,当然how依然不用关心,因为这回继续是讲xsd。

    思考一下,然后来说说一些可以想到的改进:

  1. 关于what的问题,因为直接用value中的值,显然并不可行,不可能用为了改一个值就修改一次xml配置文件
  2. 关于when的问题,对于每一个modification可能有条件限制,在某些条件下才执行某些修改

    关于第一个问题,显然,value这个节点的类型不能简单地用string,需要是一个可以代表变量的或者代表参数的类型。

    那么就定义一个参数类型,并且修改value的类型:

image

    再看看xml变成什么样子了:

image

    似乎不怎么样啊,没什么大区别。

    别急,替换的内容不一定就非要是变量,常量也是有可能的。(例如:同一个模板的同一个地方在不同的配置里面被替换成不同的常量

    那么好吧,现在再加一个常量类型:

image

    问题来了,现在value可以是一个参数(Parameter)也可以是一个常量(Const),那么value的类型应该是什么哪?

    还记得第一篇吗?如果出现多个可选择的类型,要是用xs:choice,要么就用继承,这里,似乎还是选择继承更好一些,那么就定义一个比常量和参数更基础的类型:

image

    (是不是想起了编程语言中的表达式?

    然后把参数和常量也修改成从表达式继承,并且把value的类型修改成表达式:

image

    现在再来看看xml:

image

    现在,value就可以在参数和常量中随意选择,并且对将来新添加的其他表达式也可以兼容。

 

    接下来,来看看when的问题。

    任何modification都可能有某些条件才能执行的限制,所以,modificaion应该包含一个when节,但是如果考虑更多的情况,例如给某个slide加条件,或者给整个配置加条件,那么when节就可以出现在configuation或slide或modification或者其它将来可能出现的节下面,因此,再次借助抽象类的力量,定义一个Conditionable的抽象类:

image

    写到这里,立即发现when的类型是boolean显然是不正确的,如果是boolean的话,那显然在配置xml写好之后,就立即可以确定这些被when修饰的节点到底需不需要执行了,这显然违反了我们的初衷。

    这里,我们需要为这个xml配置做出一个重要的选择:是想把这么xml的类型系统(这里可以把它当成某一门语言看待)做成一个强类型的还是弱类型的。

    强类型的好处,显而易见,禁止错误类型的参数继续运算,可以说,之前的xsd就是这种强类型的体现。

    那么弱类型哪?弱类型语言可以隐式的转换类型,也就是说,一个字符串可以被当成布尔值,也可以被当成数字,遇到无法转换的情况,在运行时报错。

    说了这一段废话,再来看xml,when后边的类型必须是个boolean,但是它又需要在运行时计算,也就是需要上面的Expression类型,但是Expression类型并不保证其结果是boolean。

    这里强类型的做法是再定义一个BooleanExpression,但是这样就做得太复杂了,使用者可能无法接受。

    如果用弱类型,Expression类型并不保证其结果是boolean,但是因为是弱类型的,所以,无论Expression返回了什么类型,都尝试把它转换成boolean,如果无法转换,就报错。

    这样,使用者就不需要关心烦人的类型问题了:

image

    另外,增加了minOccurs=“0”,这个是表达了when节可以不出现的意思,也就所在的节必须执行。

    有时候,条件可能比较复杂,不能用简单的一个boolean表示,这是,我们就需要新添加几个boolean运算的表达式,来执行And、Or、Not:

image

    写到这里,是不是突然发现Expression和弱类型结合在一起,真是强大到让人不可思议?当有任何需求时,似乎都可以通过继承Expression的方式来扩展。

    (强大的Expression类型就暂时说到这里,后面继续会提到它

    回过头来看Conditionable类型,目前还没有任何类型继承自它,接着,修改一下Modification,Slide已经Configuation类型:

image

    现在,再看一下xml:

image

    例子中的条件就是相当于:(p1||p2)&&(!p3),是不是够复杂了?

    现在,思考另一个问题,如果某些应用需要其他表达式做更加复杂的问题,怎么办哪?方案有下面几个:

  1. 新写一些表达式,继承自Expression类
  2. 写一个通用的表达式,用于回调.net中符合特定接口

    关于第一个,我想已经不用多说了,前面的And,Or和Not已经充分显示了这种做法的强大。但是,这个方法的缺点也显而易见,And不会真正的做And操作,这个需要后面的执行引擎去支持,也就是说,新加一个表达式不但需要修改Xsd,也需要修改执行引擎部分的代码,导致执行引擎的稳定性下降。因此,对于那些不常用的表达式而言,这样做显然是得不偿失的。

    第二个方案其实和.net的PInvoke相似,只不过这里是Xml配置PInvoke到.net代码上而已。

    这里,给这个特殊的"PInvoke"取名叫CliFunction,至于如何PInvoke到.net的代码上,将在执行引擎部分详细介绍。这里,就仅仅看一下Xsd以及Xml中的形式:

image

    是不是出奇的简单?不过要注意里面的有个属性function的类型——QName,这里有一个很重要的约定:

  • QName的Namespace部分代表这个特定的CliFunction所在的Assembly
  • QName的Name部分代表特定的CliFunction的方法名

    所以,xml的形式如下:

image

    例子中调用了"cli:StringConcat"方法,其中cli被定义成"cli-assembly::PptxExtensions",也就是说,这里让执行引擎去查找一个叫PptxExtensions的assembly,使用cli-assembly的协议(虽然目前只支持.net的assembly,不过为了以后扩展,还是加上协议比较好),并且去查找里面一个叫StringConcat的方法,然后去调用这个方法,把p1,p2,p3作为参数传递进去。

    而且,因为每一个param节的类型都是Expression类,所以在一个CliFunction的参数上调用另一个CliFunction也是合法的:

image

    现在是不是感觉这个xml已经无所不能了?但是别忘了一点,调用CliFunction的时候,所有的参数都将会被预先求解出来,也就是说,它的行为和c#中的:

StringConcat(Trim(p1), Trim(p2))

的行为一致,也就是这样的顺序运行:

  1. 从参数中取出p1
  2. 执行Trim方法(Trim(p1)
  3. 从参数中取出p2
  4. 执行Trim方法(Trim(p2)
  5. 在把前面两次调用的结果传给StringConcat,执行方法

    看起来很正常是吧,但是如果是这样的语句:

if (p1 == null)

    return “”;

else

    return Trim(p1);

    这时候,需要额外定义一个IsNull的表达式:

image

    xml可能就会这样写:

image

    这里调用了一个IIF的CliFunction,来完成一个if分支,但是有没有发现一个问题:

    如果p1的值为null,那么IsNull返回true,理论上只需要求解第二个表达式const的"",而不需要求解Trim(p1)。而实际上,在调用IIF时,3个参数都被求解出来了,看起来似乎没什么问题,但是,这一个运行顺序的改变,在某些时候可能是致命的。如果Trim方法在输入值为null时,会抛出异常,那么这里的IIF就会因为异常而被终止执行,这显然是不愿意看到的结果。

    所以,为了能够获得一个有延迟求解效果的if语句,必须在xsd中定义一个if表达式:

image

    这当然还是需要执行引擎延迟对待If表达式中的trueValue和falseValue里面的表达式,仅仅当condition为true时,才求解trueValue,反之为false时,才求解falseValue。

    写到这里,表达式已经已千变万化的形态展现给我们了,什么是表达式,一块可以有输入,但必须有输出的代码段就是表达式,当然看到现在,似乎还没看到代码段,仅仅看到了单节的代码(不计算嵌套),那么是否可以有多行的表达式哪?

    可以,但是这里我更想称它为代码块(Block),但是一旦出现代码块,就必须引入一些新的概念,使其变得比较复杂,将在下一篇中继续。


上一篇:从写组件说Xml——Xsd篇(一)

下一篇:从写组件说Xml——Xsd篇(三)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值