前言
組合設(shè)計模式摧玫,又被稱為部分整體模式。組合模式就是把一組比較相似的對象當做一樣的對象處理绑青。并根據(jù)樹狀結(jié)構(gòu)來組合對象席赂,然后提供可以一個統(tǒng)一方法去訪問這些對象吮铭,這樣就可以忽略對象和集合之間的差別。
我們可以看下這兩張樹狀圖颅停。公司架構(gòu)圖里邊谓晌,樹狀圖的各個節(jié)點node,還有各個葉子leaf實際上他們是由差別的。而組合模式就是把node當做一樣的對象處理癞揉。leaf也當做一樣的對象處理纸肉。而當我們要對node和leaf操作的時候,不需要考慮他是節(jié)點還是葉子喊熟,組合模式提供一致的方式來操作柏肪。這就是組合模式了。
組合模式定義
將部分整體的層次結(jié)構(gòu)轉(zhuǎn)換為樹狀結(jié)構(gòu)芥牌,是的客戶訪問對象和組合對象具有一致性烦味。
組合模式舉例
組合模式在寫法上分為透明組合模式和安全組合模式。我們先來透明組合模式
透明組合模式
1壁拉、先抽象出方法
public abstract class Component {
String name;
public Component(String name){
this.name = name;
}
public abstract void print();
public abstract void addChild(Component component);
public abstract void removeChild(Component component);
public abstract Component getChild(int index);
}
2谬俄、node節(jié)點
由于root根節(jié)點根node幾乎一樣,這里就直接定義node未單獨定義一個root節(jié)點
public class Node extends Component {
private static final String TAG = Node.class.getSimpleName();
private List<Component> list = new ArrayList<>();
public Node(String name) {
super(name);
}
@Override
public void print() {
Log.d(TAG,name);
for (Component component:list) {
component.print();
}
}
@Override
public void addChild(Component component) {
list.add(component);
}
@Override
public void removeChild(Component component) {
list.remove(component);
}
@Override
public Component getChild(int index) {
return list.get(index);
}
}
3弃理、葉子
枝干很簡單就是實現(xiàn)我們的增刪查和遍歷溃论。然后看葉子
public class Leaf extends Component {
private static final String TAG = Leaf.class.getSimpleName();
public Leaf(String name) {
super(name);
}
@Override
public void print() {
Log.d(TAG,name);
}
@Override
public void addChild(Component component) {
Log.d(TAG,"葉子節(jié)點,沒有子節(jié)點");
}
@Override
public void removeChild(Component component) {
Log.d(TAG,"葉子節(jié)點痘昌,沒有子節(jié)點");
}
@Override
public Component getChild(int index) {
Log.d(TAG,"葉子節(jié)點钥勋,沒有子節(jié)點");
return null;
}
}
4、調(diào)用
由于葉子節(jié)點沒有子節(jié)點了辆苔,所以增刪查詢就沒有作用了算灸。接下來調(diào)用
Component root = new Node("XX公司");
Component software = new Node("軟件部");
Component hardware = new Node("硬件部");
Component androidSoftware = new Leaf("android");
Component iosSoftware = new Leaf("ios");
Component layout = new Leaf("layout");
root.addChild(software);
root.addChild(hardware);
software.addChild(androidSoftware);
software.addChild(iosSoftware);
hardware.addChild(layout);
root.print();
5、打印
上也說了由于node和root極其相似驻啤,所以就復用了菲驴。看輸出
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Node: XX公司
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Node: 軟件部
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Leaf: android
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Leaf: ios
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Node: 硬件部
01-24 11:17:52.649 30713-30713/com.yink.designpattern.designpattern D/Leaf: layout
到此街佑,我想你已經(jīng)理解什么是組合模式了跃赚。這時候有的讀者可能發(fā)現(xiàn)了径密,葉子節(jié)點很多方法沒必要存在,如果方法多的時候,代碼就十分繁瑣多于嚎京。這個時候另外一種組合模式的寫法就出來了顺少。
安全組合模式
1酌摇、抽象方法
安全組合模式和上面的透明組合模式最大差別呢就是把抽象方法簡化了鉴吹,只留了我們都需要的。
public abstract class SafeComponent {
protected String name;
public SafeComponent(String name) {
this.name = name;
}
public abstract void print();
}
2、node
增刪查不再是統(tǒng)一接口
public class SafeNode extends SafeComponent {
private static final String TAG = SafeNode.class.getSimpleName();
private List<SafeComponent> list = new ArrayList<>();
public SafeNode(String name) {
super(name);
}
@Override
public void print() {
Log.d(TAG,name);
for (SafeComponent safeComponent:list) {
safeComponent.print();
}
}
public void addChild(SafeComponent safeComponent) {
list.add(safeComponent);
}
public void removeChild(SafeComponent safeComponent) {
list.remove(safeComponent);
}
public SafeComponent getChild(int index) {
return list.get(index);
}
}
3闷供、leaf
public class SafeLeaf extends SafeComponent {
private static final String TAG = SafeLeaf.class.getSimpleName();
public SafeLeaf(String name) {
super(name);
}
@Override
public void print() {
Log.d(TAG,name);
}
}
4烟央、調(diào)用
SafeComponent root = new SafeNode("XX公司");
SafeComponent software = new SafeNode("軟件部");
SafeComponent hardware = new SafeNode("硬件部");
SafeComponent androidSoftware = new SafeLeaf("android");
SafeComponent iosSoftware = new SafeLeaf("ios");
SafeComponent layout = new SafeLeaf("layout");
((SafeNode) root).addChild(software);
((SafeNode) root).addChild(hardware);
((SafeNode) software).addChild(androidSoftware);
((SafeNode) software).addChild(iosSoftware);
((SafeNode) hardware).addChild(layout);
root.print();
調(diào)用過后輸出和上面是一樣的,這里就不重復了歪脏。安全組合模式分工就很明確了疑俭。它還有一個好處就是當我們add/remove/getchild的時候,我們能知道具體的類是什么了婿失,而透明組合模式就得在運行時去判斷钞艇,比較麻煩。
View和ViewGroup
1豪硅、View和ViewGroup就是安全組合容器
組合模式Android中最經(jīng)典的用法無非就是View和ViewGroup的運用了哩照。我們都知道View還有Textview、Button他們都是繼承于View懒浮。他們就像葉子飘弧。而ViewGroup是容器,ViewGroup可以添加View砚著,而View不能添加ViewGroup次伶。這不就是安全組合模式里邊Leaf和Node的關(guān)系嗎。所以它就是安全組合模式的一種運用赖草。
2学少、ViewGroup容器實現(xiàn)方式
2.1剪个、接口添加操作子視圖方法
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
首先ViewGroup繼承自View秧骑,所有ViewGroup擁有view的公開方法。具有view的特性扣囊。然后ViewGroup繼承了兩個接口乎折。我們先看ViewManager這個接口
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
一眼就明白了,跟我們之前的node一樣侵歇,添加了add/remove等對子視圖的操作方法骂澄。
在看ViewParent
public interface ViewParent {
public void requestLayout();
public void invalidateChild(View child, Rect r);
public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset);
public void requestChildFocus(View child, View focused);
...
}
這里省略了ViewParent其它接口,我們會看到我們很多熟悉的方法惕虑,請求重新布局坟冲、獲取子視圖焦點等等。這里也是一些對子視圖的操作溃蔫。
2.2健提、繼承View復寫onalyout操作子元素
ViewGroup是一個抽象類,通篇ViewGroup的代碼伟叛,ViewGroup就一個為了把View的onlayout方法重置為抽象方法私痹。為啥View要這么做呢?我們知道View的onlatyou的方法是,View在父視圖中l(wèi)ayout布局過后調(diào)用onlayout方法紊遵,onlayout只是一個空實現(xiàn)账千,因為View已經(jīng)布局完成。onlayout沒有其他的實現(xiàn)意義暗膜。而ViewGroup作為一個視圖容器匀奏,ViewGroup調(diào)用layout布局完自己后。還需要布局子視圖学搜。所以它把onlayout重置為抽象方法攒射, 讓子類必須實現(xiàn)。
2.3恒水、其他方法
除了onlayout還有一些其他方法会放。我們再舉一個例子。
View中的dispatchDraw是一個空實現(xiàn)钉凌,而ViewGroup中就是為了遍歷然后drawChild咧最。等等還有其他方法都是為了操作子視圖 。這不就是組合模式么御雕。
protected void dispatchDraw(Canvas canvas) {
...
for (int i = 0; i < childrenCount; i++) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
....
}
總結(jié)
現(xiàn)在我想大家對什么是組合模式已經(jīng)有桿秤了矢沿。最后我想說下組合模式的優(yōu)缺點
優(yōu)點:
1、組合模式可以以層次結(jié)構(gòu)清楚的定義復雜對象酸纲。讓高層次忽略層次差異捣鲸。
2、高層次可以使用統(tǒng)一的方法而不用擔心它是枝干還是葉子闽坡。比如ViewGroup的dispatchView
3栽惶、組合模式可以形成復雜的樹形結(jié)構(gòu),但對樹形機構(gòu)的控制卻非常簡單疾嗅。
缺點:
1外厂、葉子類型不能控制。比如我想控制ViewGroup添加的View必須為TextView的時候代承,約束起來就很麻煩汁蝶。特別是類型多的時候。