一、概述
定义:Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly。
角 {MOD}组成:
抽象构件 Component
定义:各个对象可以提取出来的公共属性和行为。
叶子构件 Leaf
定义:最小对象单位,其下再无子分支。
容器构件 Composite
定义:组合对象,包含多个叶子构件。
二、应用场景
具有树形结构的业务模型,例如文件系统、具有嵌套字段的报文、复杂的布尔表达式等。
重要的是将业务模型树形化,通过用抽象构件或者容器构件来抽象各个节点,然后组织各个节点成组合对象的形式。从而用户对单个对象和组合对象的使用具有一致性。
三、实现方式
Composite Pattern实现方式分为两种:
透明式的
将管理子构件方法定义在抽象构件Component中。在接口层次上叶子构件Leaf和容器构件Composite没有区别,即用户通过操作抽象控件,透明的完成叶子构件和容器构件的业务处理。但Leaf会有一些管理子构件的方法,这些方法对于Leaf构件意义不大,需要在运行期进行空处理或者抛出异常,这样会带来一些安全问题。
安全式的
将管理子构件的方法定义在容器控件Composite中。在接口层次上叶子构件Leaf和容器构件Composite有不同的接口方法,即用户通过操作不同的控件,完成不同的功能。这样编译时处理Leaf功能只能用Leaf控件,处理Composite功能只能用Composite控件,又失去了透明性。
四、示例
(一)文件系统
在文件系统中,可能存在很多种格式的文件,如果图片,文本文件、视频文件等等,这些不同的格式文件的浏览方式都不同,同时对文件夹的浏览就是对文件夹中文件的浏览。
我们一般会分别定义了文件节点对象与目录节点对象,这是因为文件与目录之间的操作不同,文件没有下级节点,而目录可以有下级节点。这样操作文件节点和目录节点完成不同的操作。
重要的是将业务模型树形化,通过用抽象构件或者容器构件来抽象各个节点,然后组织各个节点成组合对象的形式。从而用户对单个对象和组合对象的使用具有一致性。
但是如果将文件系统树形结构化,文件与目录都是可以作为一个节点的下级节点而存在,我们可以将这两类抽象成一类对象,例如抽象构件(透明模式)和容器构件(安全模式)。
透明模式的示例:
抽象构件 Node
/**
* 将文件与目录统一看作是一类节点,做一个抽象类来定义这种节点,然后以其实现类来区分文件与目录,在实现类中分别定义各自的具体实现内容
*/
public abstract class Node {
protected String name;
public Node (String name){
this .name = name;
}
public void addNode (Node node) throws Exception{
throw new Exception("Invalid exception" );
}
abstract void display();
}
叶子构件
/**
* 实现文件节点
*/
public class Filer extends Node {
public Filer (String name) {
super (name);
}
@Override
public void display () {
System.out.println(name);
}
}
容器构件
import java.util.*;
/**
* 实现目录节点
*/
public class Noder extends Node {
List nodeList = new ArrayList();
public Noder (String name) {
super (name);
}
@Override
public void addNode (Node node) throws Exception{
nodeList.add(node);
}
@Override
void display() {
System.out.println(name);
for (Node node:nodeList){
node.display();
}
}
}
测试代码
import java.io.File;
public class Clienter {
public static void createTree (Node node) throws Exception{
File file = new File(node.name);
File[] f = file.listFiles();
for (File fi : f){
if (fi.isFile()){
Filer filer = new Filer(fi.getAbsolutePath());
node.addNode(filer);
}
if (fi.isDirectory()){
Noder noder = new Noder(fi.getAbsolutePath());
node.addNode(noder);
createTree(noder);
}
}
}
public static void main (String[] args) {
Node noder = new Noder("E://ceshi" );
try {
createTree(noder);
} catch (Exception e) {
e.printStackTrace();
}
noder.display();
}
}
安全模式的实现:
抽象构件
public abstract class Node {
protected String name;
public Node (String name){
this .name = name;
}
abstract void display();
}
容器构件
import java.util.*;
/**
* 实现目录节点
*/
public class Noder extends Node {
List nodeList = new ArrayList();
public Noder (String name) {
super (name);
}
public void addNode (Node node) throws Exception{
nodeList.add(node);
}
@Override
void display() {
System.out.println(name);
for (Node node:nodeList){
node.display();
}
}
}
(二)复杂的布尔表达式
业务模型 solr命令
price >= 10.25 AND ( category : [21 TO 37 ] OR name : 张三
抽象层表示 json串
{
"condition ": "AND" ,
"rules ": [{
"field ": "price" ,
"operator ": "greater_or_equal" ,
"value ": 10.25
},
{
"condition ": "OR" ,
"rules ": [{
"field ": "category" ,
"operator ": "between" ,
"value ": [21 ,37 ]
},
{
"field ": "name" ,
"operator ": "equal" ,
"value ": "张三"
}
]
}
]
}
对象化
将布尔表达式结合的两边对象化,没有包含布尔表达式的为最小单位,叶子构件Rule
{
"field ": "name" ,
"operator ": "equal" ,
"value ": "张三"
}
包含布尔表达式的是容器构件Group
{
"condition ": "OR" ,
"rules ": [{
"field ": "category" ,
"operator ": "between" ,
"value ": [21 ,37 ]
},
{
"field ": "name" ,
"operator ": "equal" ,
"value ": "张三"
}
]
}
当然整个json串也是个容器构件。
4. 组合模式使用
使用透明模式的组合模式,将所有对象共有的属性和行为定义为抽象构件。这里只将公共行为进行抽象,且分成两个接口。
叶子构件接口IRule
public interface IRule {
String getField();
void setField(String field);
String getOperator();
void setOperator(String operator );
Object getValue();
void setValue(Object value );
}
容器构件接口IGroup
public interface IGroup {
String getCondition();
void setCondition(String condition);
List getRules();
void setRules(List rules);
}
再就是泛化这两个接口的容器构件实现JsonRule
public class JsonRule implements IGroup , IRule {
private String id;
private String field;
private String type;
private String input;
private String operator;
private Object value;
private Object data;
private String condition;
private Boolean not;
private List rules;
/**
* 判断是否为group
* @return
*/
public boolean isGroup () {
return condition != null ;
}
/**
* group
* @return
*/
public IGroup toGroup () {
return this ;
}
/**
* rule
* @return
*/
public IRule toRule () {
return this ;
}
/**
* to String
* @return
*/
@Override
public String toString () {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this );
} catch (Exception e) {
return super .toString();
}
}
@Override
public String getField () {
return field;
}
@Override
public void setField (String field) {
this .field = field;
}
@Override
public String getOperator () {
return operator;
}
@Override
public void setOperator (String operator) {
this .operator = operator;
}
@Override
public Object getValue () {
return value;
}
@Override
public void setValue (Object value) {
this .value = value;
}
@Override
public String getCondition () {
return condition;
}
@Override
public void setCondition (String condition) {
this .condition = condition;
}
@Override
public List getRules () {
return rules;
}
@Override
public void setRules (List rules) {
this .rules = rules;
}
}
最后具体解析的时候,就可以统一按照JsonRule来进行处理
public Object parse (JsonRule jsonRule) {
if (jsonRule.isGroup()) {
return groupParser.parse(jsonRule, this );
} else {
for (IRuleParser ruleParser : ruleParsers) {
if (ruleParser.canParse(jsonRule)) {
return ruleParser.parse(jsonRule, this );
}
}
throw new ParserNotFoundException("Can't found rule parser for:" + jsonRule);
}
}
其中对于有Condition的情况,用groupParser处理,获取里面的每个Rules元素,再分别用RuleParser.parse处理
(三)嵌套字段报文
银行的请求报文,经常出现一个字段包含几重的多个子字段。这时候报文格式梳理到excel中,就是类似
Key
Name
IsGroup
Count
Value
field1
文件
0
d:
group1
1
3
field2
账号
133
field3
金额
200
field4
时间
0
today
对于这种情况,所有字段都可以抽象成抽象构件,容器构件Group自身引用多个抽象构件(透明模式)
即:
抽象构件IField
public abstract class AField{
--------------------------------Field
private String key;
private String name;
private Object value ;
----------------------------Group
private boolean isGroup;
private int counts;
----------------------------
gettingsetting...
}
没有嵌套的字段field1field4,Field
public class Field extends AField {
public Field (String key, String name,String value){
}
}
嵌套字段field2field3
public class Group extends AField {
public Group (String key, String name,boolean isGroup,int count){
}
}
后续解析统一用AField处理处理。
五、总结
理解组合模式的使用场景,学会从复杂的业务模型中对象化各个构件,能够统一方式处理各个独立对象和组合对象。