前言
内部类在学习基础Java知识的时候大部分人都了解过,但也是大部分人都只是了解而已,在长年累月的开发中你才会发现内部类的真正好处
定义
可以将一个类定义在另一个类的内部,这就是内部类。内部类是一种非常有用的的特征,因为它允许你把一些逻辑相关的数据组织在一起,并控制内部类的可视性。
- 静态内部类:静态内部类除了访问权限修饰符比外围类多以外, 和外围类没有区别, 只是代码上将静态内部类组织在了外部类里面
- 非静态内部类:创建该类的前提是外部类实例已经被创建好了,而内部类可以访问外部类的实例变量,也就可以做到了一个外部类实例私有成员被其他多个内部类实例访问。
场景
- 利用内部类实现多重继承
- 利用多实例内部类共享外部类实例成员特性:迭代器模式
实践
背景:系统是一个基于流程作出决策的状态机系统,有一个流程对象,也有节点类,边类,而流程的表示是用邻接表的数据结构,而每一个流程节点都是一个状态模式中workflowContext的状态;因为流程是通过配置出来的,所以流程节点具有配置好的实例和状态。现在要求不同的用户请求可以执行相同的流程。
public class Workflow{ public Node root }
问题:同一个流程节点,不同的用户执行,就会对每一个用户产生不同的流程节点状态(这个状态是节点运行状态,到这里状态分了两种,流程状态和节点状态,他们当然是包含关系了)也就是单例的流程节点本身对用户而言的无状态的,但确确实实在用户使用的时候会产生状态值,那么该如何放置这些状态呢?
public class Node{ ...... Link link; //图的邻接表表示法 ...... Map config = new HashMap(); }
方案:我们现在参考一件事:jvm中,方法本身也是对每一次执行无状态的,放置在方法区中;但是每一次执行方法都会创建一个方法帧,压进线程栈中。是不是和流程的执行很相似?所以我为每一次流程节点的执行,都创建一个流程的执行帧对象,这多个用户执行同一个节点的时候,只需创建该执行帧就行了,因为执行帧能拿到节点的私有引用,包括节点的配置。所以这个执行帧是放置用户执行状态最好的地方。
public class Node{ private Map config = new HashMap(); public ExecuteFrame{ Map exeConfig = new HashMap(); public void doExecute(){ Service.doAction(config.putAll(exeConfig)) } } }
完整的代码如下:
public class Node{
private Map config = new HashMap();
//利用上下文,创建一个节点的执行空间 public ExecuteFrame newFrame(Context context){ ..... return new ExecuteFrame(context); } public class ExecuteFrame{ Context context public ExecuteFrame(Context context){ this.context = context; } Map exeConfig = new HashMap(); public void doExecute(Context context){ Service.doAction(config.putAll(exeConfig)) ...... } } }
如果节点还可以设计为状态机或者简单的状态模式的话,也是很好拓展的,另外最后我还创建了一个执行器对象,用在管理不同流程的执行帧,这样又可以跟着用户的执行路径,有些同学可能会用类似职责链模式去执行流程,但这样是行不通的,所以才有执行器对象存在。
执行器
这点是后话了,本来流程链打算做成职责链模式类型的,但发现后来实在走不通,问题出现在:同步问题,你想想一个节点执行完逻辑,再调用下一个节点,如果还需要后面节点执行完它才返回,那么这本身就和问题域背道而驰。所以与其让一个个节点链式同步,不如让执行器来统筹,每一个节点都是被同步的,一个节点执行完就返回,后面的同步全部交给执行器。
总结:同步是一个比较有意思的问题,在写代码的时候,一定要把场景和方法同步、返回考虑好,不然抽象就不是一个好的抽象。