Java设计模式---组合模式
一、组合模式定义
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
如上图所示(截取自《Head First Design Patterns》一书),主要包括三个部分:
1. Component抽象组件。定义参加组合对象的共有方法和属性,可以定义一些默认的函数或属性。
2. Leaf叶子节点。构成组合树的最小构建单元。
3. Composite树枝节点组件。它的作用是组合树枝节点和叶子节点形成一个树形结构。
Component : 组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理 Component 的子部件。
abstract class Component {
protected String name; public Component(String name) {
this.name = name;
} public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract void Display(int depth);
}
Leaf : 表示叶节点对象。叶子节点没有子节点。
class Leaf extends Component { public Leaf(String name) {
super(name);
} @Override
public void Add(Component c) {
System.out.println("Can not add to a leaf");
} @Override
public void Remove(Component c) {
System.out.println("Can not remove from a leaf");
} @Override
public void Display(int depth) {
String temp = "";
for (int i = 0; i < depth; i++)
temp += '-';
System.out.println(temp + name);
} }
Composite : 定义枝节点行为,用来存储子部件,在 Component 接口中实现与子部件相关的操作。例如 Add 和 Remove。
class Composite extends Component { private List<Component> children = new ArrayList<Component>(); public Composite(String name) {
super(name);
} @Override
public void Add(Component c) {
children.add(c);
} @Override
public void Remove(Component c) {
children.remove(c);
} @Override
public void Display(int depth) {
String temp = "";
for (int i = 0; i < depth; i++)
temp += '-';
System.out.println(temp + name); for (Component c : children) {
c.Display(depth + 2);
}
} }
Client : 通过 Component 接口操作结构中的对象。
public class CompositePattern { public static void main(String[] args) {
Composite root = new Composite("root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B")); Composite compX = new Composite("Composite X");
compX.Add(new Leaf("Leaf XA"));
compX.Add(new Leaf("Leaf XB"));
root.Add(compX); Composite compXY = new Composite("Composite XY");
compXY.Add(new Leaf("Leaf XYA"));
compXY.Add(new Leaf("Leaf XYB"));
compX.Add(compXY); root.Display(1);
} }
二、组合模式优势
节点自由扩展增加。使用组合模式,如果想增加一个树枝节点或者叶子节点都是很简单的,只要找到它的父节点就可以了,非常容易扩展,符合“开闭原则”。应用最广的模式之一。应用在维护和展示部分-整体关系的场景,如树形菜单、文件夹管理等等。一棵树形结构的所有节点都是Component,局部和整体对调用者来说都是一样的,没有区别,所以高层模块不比关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
1、可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。
2、客户端调用简单,客户端可以一致的使用组合结构或其中单个对象。
3、定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。
4、更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码。
组合模式的缺点: 使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。
使用场景:
1、需要表示一个对象整体或部分层次,在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,可以一致地对待它们。
2、让客户能够忽略不同对象层次的变化,客户端可以针对抽象构件编程,无须关心对象层次结构的细节。
三、组合模式在Android源码中的应用
在Android源码中,都能找到使用组合模式的例子,其中在《Android源码学习之观察者模式应用》介绍到的ViewGroup和View的结构就是一个组合模式,结构图如下所示:
现在来看看它们是如何利用组合模式组织在一起的,首先在View类定义了有关具体操作,然后在ViewGroup类中继承View类,并添加相关的增加、删除和查找孩子View节点,代码如下:
/*
* @attr ref android.R.styleable#ViewGroup_clipChildren
* @attr ref android.R.styleable#ViewGroup_clipToPadding
* @attr ref android.R.styleable#ViewGroup_layoutAnimation
* @attr ref android.R.styleable#ViewGroup_animationCache
* @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
* @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
* @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
* @attr ref android.R.styleable#ViewGroup_descendantFocusability
* @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
*/
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
接着看增加孩子节点函数:
/**
* Adds a child view. If no layout parameters are already set on the child, the
* default parameters for this ViewGroup are set on the child.
*
* @param child the child view to add
*
* @see #generateDefaultLayoutParams()
*/
public void addView(View child) {
addView(child, -1);
} /**
* Adds a child view. If no layout parameters are already set on the child, the
* default parameters for this ViewGroup are set on the child.
*
* @param child the child view to add
* @param index the position at which to add the child
*
* @see #generateDefaultLayoutParams()
*/
public void addView(View child, int index) {
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
} /**
* Adds a child view with this ViewGroup's default layout parameters and the
* specified width and height.
*
* @param child the child view to add
*/
public void addView(View child, int width, int height) {
final LayoutParams params = generateDefaultLayoutParams();
params.width = width;
params.height = height;
addView(child, -1, params);
} /**
* Adds a child view with the specified layout parameters.
*
* @param child the child view to add
* @param params the layout parameters to set on the child
*/
public void addView(View child, LayoutParams params) {
addView(child, -1, params);
} /**
* Adds a child view with the specified layout parameters.
*
* @param child the child view to add
* @param index the position at which to add the child
* @param params the layout parameters to set on the child
*/
public void addView(View child, int index, LayoutParams params) {
if (DBG) {
System.out.println(this + " addView");
} // addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
在ViewGroup中我们找到了添加addView()方法,有了增加孩子节点,肯定有相对应删除孩子节点的方法,接着看:
@Override
public void removeView(View view) {
if (removeViewInternal(view)) {
requestLayout();
invalidate(true);
}
}
private boolean removeViewInternal(View view) {
final int index = indexOfChild(view);
if (index >= 0) {
removeViewInternal(index, view);
return true;
}
return false;
} private void removeViewInternal(int index, View view) {
if (mTransition != null) {
mTransition.removeChild(this, view);
} boolean clearChildFocus = false;
if (view == mFocused) {
view.unFocus(null);
clearChildFocus = true;
}
if (view == mFocusedInCluster) {
clearFocusedInCluster(view);
} view.clearAccessibilityFocus(); cancelTouchTarget(view);
cancelHoverTarget(view); if (view.getAnimation() != null ||
(mTransitioningViews != null && mTransitioningViews.contains(view))) {
addDisappearingView(view);
} else if (view.mAttachInfo != null) {
view.dispatchDetachedFromWindow();
} if (view.hasTransientState()) {
childHasTransientStateChanged(view, false);
} needGlobalAttributesUpdate(false); removeFromArray(index); if (view == mDefaultFocus) {
clearDefaultFocus(view);
}
if (clearChildFocus) {
clearChildFocus(view);
if (!rootViewRequestFocus()) {
notifyGlobalFocusCleared(this);
}
} dispatchViewRemoved(view); if (view.getVisibility() != View.GONE) {
notifySubtreeAccessibilityStateChangedIfNeeded();
} int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
for (int i = 0; i < transientCount; ++i) {
final int oldIndex = mTransientIndices.get(i);
if (index < oldIndex) {
mTransientIndices.set(i, oldIndex - 1);
}
} if (mCurrentDragStartEvent != null) {
mChildrenInterestedInDrag.remove(view);
}
}
同样的,也有查找获得孩子节点的函数:
/**
* Returns the view at the specified position in the group.
*
* @param index the position at which to get the view from
* @return the view at the specified position or null if the position
* does not exist within the group
*/
public View getChildAt(int index) {
if (index < 0 || index >= mChildrenCount) {
return null;
}
return mChildren[index];
}
注:其中具体叶子节点,如Button,它是继承TextView的,TextView是继承View的,代码如下:
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
......
}
注:其中使用(继承)到ViewGroup类的有我们常用的容器类(包装和容纳各种View),如LinearLayout、FrameLayout等,代码如下:
public class LinearLayout extends ViewGroup {
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
......
}
public class FrameLayout extends ViewGroup {
...
} public class RelativeLayout extends ViewGroup {
private static final String LOG_TAG = "RelativeLayout"; private static final boolean DEBUG_GRAPH = false;
...
} public class AbsoluteLayout extends ViewGroup { public AbsoluteLayout(Context context) {
super(context);
} }
四、基本控件继承关系图
最后送上“基本控件继承关系图”:
Java设计模式---组合模式的更多相关文章
- Java设计模式——组合模式
JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...
- 【设计模式】Java设计模式 - 组合模式
Java设计模式 - 组合模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自己 ...
- JAVA 设计模式 组合模式
用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模式. 结构
- Java 设计模式 —— 组合模式
在现实生活中,存在很多"部分-整体"的关系,例如,大学中的部门与学院.总公司中的部门与分公司.学习用品中的书与书包.生活用品中的衣服与衣柜.以及厨房中的锅碗瓢盆等.在软件开发中也是 ...
- Java设计模式—组合模式
组合模式是一种常见的设计模式(但我感觉有点复杂)也叫合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系. 个人理解:组合模式就是将部分组装成整体. 定义如下: 将对象组合成树形结构以表 ...
- Java设计模式-组合模式(Composite)
组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便,看看关系图: 直接来看代码: public class TreeNode { private String name; private ...
- Java设计模式----组合模式(Composit )
1. 组合模式定义: 组合模式,又叫合成模式,有时又叫部分-整体模式,主要用来描述部分与整体的关系. 定义:将对象组合成树形结构以示" 部分--整体 "的层次结构,使得用户对单个 ...
- 3.java设计模式-建造者模式
Java设计模式-建造者模式 在<JAVA与模式>一书中开头是这样描述建造(Builder)模式的: 建造模式是对象的创建模式.建造模式可以将一个产品的内部表象(internal repr ...
- 16. 星际争霸之php设计模式--组合模式
题记==============================================================================本php设计模式专辑来源于博客(jymo ...
随机推荐
- idea sass scss配置
1.安装Ruby win 直接http://rj.baidu.com/soft/detail/22711.html?ald mac linux https://ruby.taobao.org/ 可下 ...
- 2.常用快捷键.md
[toc] 1.mian函数补全 在IntelJ中和Eclipse中稍有不同,在Eclipse中,输入main再按Alt+/即可自动补全main函数,但是在IntellJ中则是输入psvm,选中即可 ...
- Spring mvc 中有关 Shiro 1.2.3 配置问题
Spring 版本:3.2.x, 4.0.x [问题说明] 首先介绍下配置出错情况: (1)项目中,Spring3 and Spring4 的 applicationContext.xml aop ...
- html5 input属性
今天才接确html5 +css3 实在是太赞了. 下面我就来介绍一下今天我用到的 input 属性. html5 代码如下: <input type="text" plac ...
- git log友好显示
查看commit 提交日志 $ git log $git log --pretty=oneline $git reflog 显示所有提交记录,包括已经回退的提交,如图:提交了abc 和 bb 然后回退 ...
- JS冒泡事件与处理
JavaSciprt事件中有两个很重要的特性:事件冒泡以及目标元素. 事件冒泡: 当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发.这 一过程被 ...
- 扩展PHP内置的异常处理类
在try代码块中,需要使用throw语句抛出一个异常对象,才能跳到转到catch代码块中执行,并在catch代码块中捕获并使用这个异常类的对象.虽然在PHP中提供的内置异常处理类Exception,已 ...
- CentOS 7 ARM 版发布:支持树莓派2/香蕉派/CubieTruck
CentOS 7 ARM 版发布:支持树莓派2/香蕉派/CubieTruck 来自CentOS团队的Karanbir Singh很高兴地宣布,面向ARM硬件架构的CentOS 7 Linux已经正式发 ...
- POJ1068 Parencodings(模拟)
题目链接. 分析: 水题. #include <iostream> #include <cstdio> #include <cstring> using names ...
- Delphi TcxTreeList 怎们显示下拉列表
TcxTreeList 怎们显示下拉列表, 操作如下: 1.定义一个TStringlist中存储你的信息. 2.然后选定在TcxTreeList的哪个列要变成TCombobox, 如下图: 3. ...