组合模式
定义:组合模式(Composite Pattern)将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户可以使用一致的方法操作单个对象和组合对象。
UML图:
(1)Component:抽象根节点,为组合中的对象声明接口。在适当的情况下,实现所有类共有接口的缺省行为。声明一个接口用于访问和管理Component的子节点。可在递归结构中定义一个接口,用于访问一个父节点,并在合适的情况下实现它。
(2)Composite:定义有子节点的那些枝干节点行为,存储子节点,在Component接口中实现与子节点有关的操作。
(3)Leaf:在组合中表示叶子节点对象,叶子节点没有子节点,在组合中定义节点对象的行为。
(4)Client:通过Component接口操纵组合节点的对象。
适用场景:
● 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
● 从一个整体中能够独立出部分模块或功能的场景。
安全组合模式
即是上面的UML图
安全组合模式中,在抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法。这种做法是安全的,因为根本不向叶子对象提供这些管理成员对象的方法,对于叶子对象,客户端不可能调用到这些方法,所以是安全。
举例:一个公司的架构层次
Member即是上面的Component
public abstract class Member {
public String mName;
public int mLevel;
public Member(String name) {
mName = name;
}
public abstract void descript();
public abstract String getInfo();
public void setLevel(int level) {
mLevel = level;
}
public void setName(String name) {
mName = name;
}
}
部门,也是公司的一个”Member”,是Composite
public class Branch extends Member {
public List<Member> mMembers = new ArrayList<>();
public Branch(String name) {
super(name);
}
@Override
public void descript() {
String str = "";
for(int i = 0; i < mLevel; i++){
str += "....";
}
System.out.println(str + getInfo());
for(Member member : mMembers){
member.descript();//遍历每一个分支
}
}
@Override
public String getInfo() {
if(mLevel == 0){
return "公司名称:" + mName;
}
return "部门名称:" + mName + " 部门人数:" + mMembers.size();
}
public void addMember(Member member){
member.setLevel(mLevel + 1);
mMembers.add(member);
}
public void removeMember(Member member){
mMembers.remove(member);
}
public String getName() {
return mName;
}
}
普通员工,也就是Leaf
public class Worker extends Member {
public Worker(String name) {
super(name);
}
@Override
public void descript() {
String str = "";
for(int i = 0; i < mLevel; i++){
str += "....";
}
System.out.println(str + getInfo());
}
@Override
public String getInfo() {
return "职级:" + mLevel + " 姓名:" + mName;
}
}
测试代码:
public class TextCompany {
public static void main(String args[]){
Branch company = new Branch("美丽人生公司");
company.setLevel(0);
Member member = new Worker("执行总裁");
Branch technology = new Branch("技术部门");
Branch product = new Branch("产品部门");
Branch operation = new Branch("运营部门");
//违反依赖倒置原则
company.addMember(member);
company.addMember(technology);
company.addMember(product);
company.addMember(operation);
technology.addMember(new Worker("程序猿1"));
technology.addMember(new Worker("程序猿2"));
technology.addMember(new Worker("程序猿3"));
technology.addMember(new Worker("程序猿4"));
product.addMember(new Worker("产品经理1"));
product.addMember(new Worker("产品经理2"));
operation.addMember(new Worker("运营专员1"));
operation.addMember(new Worker("运营专员2"));
operation.addMember(new Worker("运营专员3"));
company.descript();
}
}
输出公司架构:
公司名称:美丽人生公司
....职级:1 姓名:执行总裁
....部门名称:技术部门 部门人数:4
........职级:2 姓名:程序猿1
........职级:2 姓名:程序猿2
........职级:2 姓名:程序猿3
........职级:2 姓名:程序猿4
....部门名称:产品部门 部门人数:2
........职级:2 姓名:产品经理1
........职级:2 姓名:产品经理2
....部门名称:运营部门 部门人数:3
........职级:2 姓名:运营专员1
........职级:2 姓名:运营专员2
........职级:2 姓名:运营专员3
安全组合模式违反了 6 个设计模式原则中依赖倒置原则,客户端不应该直接依赖于具体实现,而应该依赖于抽象,既然是面向接口编程,就应该把更多的焦点放在接口的设计上,于是这样就产生了透明的组合模式。
比如上面的addmember操作,依赖于Branch具体类,而不是依赖于接口。一旦Branch有改变,客户端也需要做相对应改变,比如把add方法名改了,就客户端所有的调用add的地方都要修改。
透明组合模式
和安全的组合模式差异就是在将 Composite 的操作放到了 Component 中,这就造成 Leaf 角色也要实现 Component 中的所有方法。实现的代码做出相应改变:
UML图如下:
举例子:一个公司的人员组织架构
公共的Component:包含所有客户需要的操作方法
public abstract class Member {
public String mName;
public int mLevel;
public Member(String name) {
mName = name;
}
public abstract void descript();
public abstract String getInfo();
public void setLevel(int level) {
mLevel = level;
}
public void setName(String name) {
mName = name;
}
public abstract void addMember(Member member);
public abstract void removeMember(Member member);
}
公司管理层,即树干
public class Leader extends Member {
private List<Member> mMembers = new ArrayList<>();
public Leader(String name) {
super(name);
}
@Override
public void descript() {
String str = "";
for(int i = 0; i < mLevel; i++){
str += "....";
}
System.out.println(str + getInfo());
for(Member member : mMembers){
member.descript();//遍历部下
}
}
@Override
public String getInfo() {
return "职级:" + mLevel + " 名称:" + mName + " 直接下属:" + mMembers.size() + "个";
}
@Override
public void addMember(Member member) {
member.setLevel(mLevel + 1);
mMembers.add(member);
}
@Override
public void removeMember(Member member) {
}
}
普通员工:也要实现Component所有方法
public class Worker extends Member {
public Worker(String name) {
super(name);
}
@Override
public void descript() {
String str = "";
for(int i = 0; i < mLevel; i++){
str += "....";
}
System.out.println(str + getInfo());
}
@Override
public String getInfo() {
return "职级:" + mLevel + " 姓名:" + mName;
}
@Override
public void addMember(Member member) {
}
@Override
public void removeMember(Member member) {
}
}
测试:
public class TestTransparent {
public static void main(String args[]){
Member ceo = new Leader("总裁");
ceo.setLevel(1);
Member cto = new Leader("技术总监");
Member cpo = new Leader("产品总监");
Member coo = new Leader("运营总监");
ceo.addMember(cto);
ceo.addMember(cpo);
ceo.addMember(coo);
cto.addMember(new Worker("程序猿1"));
cto.addMember(new Worker("程序猿2"));
cto.addMember(new Worker("程序猿3"));
cto.addMember(new Worker("程序猿4"));
cpo.addMember(new Worker("产品经理1"));
cpo.addMember(new Worker("产品经理2"));
coo.addMember(new Worker("运营专员1"));
coo.addMember(new Worker("运营专员2"));
coo.addMember(new Worker("运营专员3"));
ceo.descript();
}
}
输出结果:
....职级:1 名称:总裁 直接下属:3个
........职级:2 名称:技术总监 直接下属:4个
............职级:3 姓名:程序猿1
............职级:3 姓名:程序猿2
............职级:3 姓名:程序猿3
............职级:3 姓名:程序猿4
........职级:2 名称:产品总监 直接下属:2个
............职级:3 姓名:产品经理1
............职级:3 姓名:产品经理2
........职级:2 名称:运营总监 直接下属:3个
............职级:3 姓名:运营专员1
............职级:3 姓名:运营专员2
............职级:3 姓名:运营专员3
由于是在 Component 类中定义了所有的行为,所以客户端就不用直接依赖于具体 Composite 和 Leaf 类的实现,遵循了依赖倒置原则——依赖抽象,而不依赖具体实现。但是也违反了单一职责原则与接口隔离原则让 Leaf 类继承了它本不应该有的方法,这样做的目的就是为了客户端可以透明的去调用对应组件的方法,将枝干节点和子节点一视同仁。
另外,将 Component 写成一个虚基类,并且实现所有的 Composite 方法(空方法),只让 Composite 去覆盖重写父类的方法,而 Leaf 类就不需要去实现 Composite 的相关方法,这么去实现当然也是可以的。
总结
View和ViewGroup就是组合模式的实现,不过View的视图层级使用的是安全的组合模式。ViewGroup相对于View增加了addView、removeView、getChildAt等方法.
优点
(1)组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,他让高层模块忽略了层次的差异,方便对整个层次结构进行控制。
(2)简化了高层模块的代码。
(3)在组合模式中增加新的枝干构件和叶子构件都很方便,无须对现有类库进行修改,符合“开闭原则”。
(4)对树形结构的控制变得简单。
缺点
组合模式不容易限制组合中的构件。因为大多数情况下,它们都来自于相同的抽象层,此时,必须进行类型检查来实现,这个实现过程较为复杂,比如我worker类后来有添加了一个TemWorker,无法静态(即编译时)判断添加到Branch的是一个worker还是TemWorker,只能在添加的时候动态(运行时候,比如用instanceof)的判断。