控件的内容本身是一个模板片断,也可以进一步细分为多个小模板片断
每一个模板片断是可被重写的
在重写的过程中,每一个模板片断是可被重用的。但当前正在被重写的模板片段不可被重用
控件的属性分为3类:
- 常量属性,随控件定义赋值,永不变更,如
type
属性 - 配置属性,随控件构造赋值,生命周期内不变更,如
TextBox#type
属性 - 状态属性,任何时候均可赋值,随时可以变化
控件在设计上将这3类属性用不同的方式实现,类似React中的state
和props
。具体实现方式待讨论,主要还需考虑以下点:
- 配置属性和状态属性由使用者决定还是控件定义决定,还是一起决定
- 如果使用者可以决定部分状态属性作为配置属性,是通过传递为配置属性,还是作为单向绑定实现
- 如果用单向绑定实现配置属性,则是否要求被单向绑定的属性不得修改
- 如果被单向绑定的属性不得修改,则一个属性是否能在
foo
中被用于单向,在bar
中被用于双向
对除常量属性外的其它属性,均可以定义默认属性
默认属性定义的方式要考虑开发控件的便捷性和多级继承时重写的便携性
一个控件不是简单地接受一个普通对象作为属性,而是一个带有get
、set
、change
的Model对象,作为其Context(命名待定)
在父子控件的数据传递上,控件可以选择:
- 将整个Context传递给其子控件
- 选取部分属性传递给其子控件,但保持
change
的关联,即子控件修改某个属性时,父控件同样收到change
事件 - 断开
change
的关联
由于传递过来的Context的属性与控件的属性并不一定完全一致,因此控件可以通过配置来实现这之间的绑定关系,即控件内部的数据池和Context之间的映射
如果采用双向绑定,则控件内部的数据池和Context的变化是互通的,而单向绑定则不再有相互的关联
不再把构造逻辑、类型->模块映射逻辑等硬编码,所有控件由工厂生产,不实际调用new方法
复杂系统可以以IoC为核心自己实现工厂
基于此,控件本身的各通用逻辑会需要比较细致的拆分设计
数据为根源指一个控件就是一个数据对象的具体实现,控件本身不会有任何的“方法”和“事件”,所有的实现均封装在数据之后,由数据的set
、get
和change
来完成所有需求
在这种方案中,所有操作映射为数据的变化。但事实上像“按钮点击”这种映射为数据的变化是很别扭的,所以是否应该再细化“操作”和“状态变化”
数据归属唯一是指明确一个数据由哪一个控件掌握,该控件的子控件不得对这一数据做写操作
如果数据归属是唯一的,则由父控件控制的数据,子控件是不能修改的,那么做绑定就要通过子控件事件->父控件处理函数->更新数据
如果数据归属不是唯一的,则父子控件共享一个数据池,都可以修改,那么父控件会缺失对数据校验、预处理的机会
对于change
这一类简单的通知型的事件,可以转换为数据的变化来触发,对外界保持统一的接口方便快速的开发
对于带有拦截功能的事件,如那些可以preventDefault()
的事件,则通过统一的接口很难处理。虽然数据的change
事件也有preventDefault()
方法,但由于控件的数据池和外面关联的Model并不是一个对象,会导致复杂性的上升
进一步的,需要通过Event对象传递信息的事件会更难处理,通过Model抽象为数据的控件不应该针对不属属性的变化有不同的处理逻辑,应该一切都是统一的
部分此类事件可以通过preventDefault()
断开默认处理逻辑,转为修改Model中的其它属性来实现,如Table#sort
事件要禁止基于当前数据源的排序,然后再远程加载数据修改datasource
。但很可能也存在另一些事件不能以这个方案解决