10-NameSpaceHandlerSupport
背景简介
出现的原因
我们上面介绍了 NameSpaceHandler
定义的接口,其中我们发现初始化接口还好,根据各自需要定制;但是后面的接口定义我们明显发现在同一个接口中实现了多个功能:
我们定义的命名空间中有多个标签,就像这在默认命名空间中我们有四个标签就
if-else
的写了四次,如果定义了十个呢?这就很恐怖了。接口定义时可能考虑的是向外提供简单、实用、统一的接口,但是对内部实现人员来时则增加了复杂度,也更容易出错。
所以,根据我们之前一贯的风格,如果接口定义的粒度过大,我们在后面实现时就进行委托操作,及:保证每个具体的函数/类只负责属于自己的功能。
考虑到具体的函数都是要根据各自命名空间的解析规则自行实现的,NameSpaceHandlerSupport
采用了"注册——读取”的方式来完成功能,并对parse/decorete
接口的中的核心逻辑顺序进行实现,拆出更细粒度、更专注于该命名空间中唯一的节点操作的接口。
职责
适配器模式或者说是模版方法模式的一个中间角色。
注意点
弄清楚角色就好,没什么坑。
源码
实例属性
/**
* key 节点对应的本地名字
* value 解析此节点所需的 Parser
*/
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
/**
* 同上
*/
private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
/**
* 同上
*/
private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap<>();
专门将解析和装饰拆成了两个实现接口,接口入参和NameSpaceHandler
的一样。
注册解析接口
void init();
自行实现 init()
接口,往实例属性的Map
里放定义的各个元素对应的解析方法。
根据注册的接口提供功能
/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Element}.
*/
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
思路很明确,不用再解释了。
装饰的逻辑和这个一样。
总结记录
在有多种分支的代码中,尤其是每个分支都可能有复杂逻辑的代码中,尽量不要直接用
if-else
-
if-else
加private
函数
这两种方法做:
- 只用
if-else
容易造成缩进过多,单个方法过长的问题,也不便扩展 -
if-else
加private
函数在逻辑复杂时能很好的将逻辑拆分到多个函数中,但是如果命名空间的逻辑太多,容易造成单个类的不可控
综上,像NameSpaceSupport
这样将每个具体的处理逻辑拆成一个确定的类,找个专门的包放一下,也是个不错的选择。
这个和我之前看的别的后端组的逻辑很像:
在某些后端组,在
manager
或者service
层会直接将各个服务接口分给对应的Handler
,每个实例类负责一个服务接口的功能。
我们做的项目逻辑不是特别复杂,所以项目结构大概如下:
在逻辑简单的网关组,直接
controller
层处理请求基本信息service
层处理业务逻辑converter
层用来做对象转换【此处有点像Util】【一般一个service
类对应一个converter
类,也就是多个服务接口用一个converter
类】在逻辑复杂的后端组,一般采用的是:
service
层处理请求基本信息manager
层负责根据service
整合n个adapter
的基本功能【此处在极度复杂的情况下可能会有过长的风险】adapter
层负责封装对接的后端接口
问题
传进来的上下文都是什么样子的?