开发设计模式学习之桥接模式
这周学习一种学习容易使用难的设计模式——桥接模式也称桥梁模式,这是结构性设计模式之一,可以将抽象部分与实现部分分离,使他们都可以独立变化。
关于桥接模式的应用场景,主要有以下三种:
(1)如果一个系统需要在构件的抽象画角色和具体化角色之间增加更多灵活性,避免在两个层次之间建立静态的继承联系,而是通过桥接模式使他们在抽象层建立一个连接;
(2)对于那些不希望使用继承关系,或因为多层继承导致系统类数量急剧增加的系统,也可以考虑使用桥接模式;
(3)一个类存在两个独立变化的维度,且这两个维度都需要扩展;
桥接模式的结构设计主要分四部分,
(1)抽象部分:该类保持一个对实现部分对象的引用,在抽象部分调用的方法实现,是通过实现部分对象调用其方法来实现,该类一般为抽象类;
(2)优化的抽象部分(抽象部分的子类):抽象部分的具体实现,该类一般是对抽象部分的方法进行完善和扩展;
(3)实现部分:可以是接口或抽象类,其方法不一定要与抽象部分中的一致,一般情况下是由实现部分定义具体被使用到的操作方法,而抽象部分定义的则是实现部分中基本操作方法的执行流程所在的业务方法;
(4)实现部分的具体实现(实现部分的子类):完善实现部分中方法定义的具体逻辑;
在Android源码中,有很多涉及到桥接模式的案例,比如对于View的视图层和绘制层的功能划分,就是范围很广的应用体现,在ListView和ListAdapter的使用中,ListView作为AdapterView的子类,属于桥接模式中优化的抽象部分,那么AdapterView就作为抽象部分了,在AdapterView中有对Adapter对象的引用,因此Adapter便是实现部分,对于ListAdapter或者自定义Adapter由于继承自Adapter,则是实现部分的具体实现。
这部分的逻辑很简单,在实现部分,也就是Adapter接口中定义了操作数据的具体方法,包括getCount()等熟知的方法。下面是源码部分
1 public interface Adapter { 2 /** 3 * Register an observer that is called when changes happen to the data used by this adapter. 4 * 5 * @param observer the object that gets notified when the data set changes. 6 */ 7 void registerDataSetObserver(DataSetObserver observer); 8 9 /** 10 * Unregister an observer that has previously been registered with this 11 * adapter via {@link #registerDataSetObserver}. 12 * 13 * @param observer the object to unregister. 14 */ 15 void unregisterDataSetObserver(DataSetObserver observer); 16 17 /** 18 * How many items are in the data set represented by this Adapter. 19 * 20 * @return Count of items. 21 */ 22 int getCount(); 23 24 /** 25 * Get the data item associated with the specified position in the data set. 26 * 27 * @param position Position of the item whose data we want within the adapter's 28 * data set. 29 * @return The data at the specified position. 30 */ 31 Object getItem(int position); 32 33 /** 34 * Get the row id associated with the specified position in the list. 35 * 36 * @param position The position of the item within the adapter's data set whose row id we want. 37 * @return The id of the item at the specified position. 38 */ 39 long getItemId(int position); 40 41 /** 42 * Indicates whether the item ids are stable across changes to the 43 * underlying data. 44 * 45 * @return True if the same id always refers to the same object. 46 */ 47 boolean hasStableIds(); 48 49 /** 50 * Get a View that displays the data at the specified position in the data set. You can either 51 * create a View manually or inflate it from an XML layout file. When the View is inflated, the 52 * parent View (GridView, ListView...) will apply default layout parameters unless you use 53 * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)} 54 * to specify a root view and to prevent attachment to the root. 55 * 56 * @param position The position of the item within the adapter's data set of the item whose view 57 * we want. 58 * @param convertView The old view to reuse, if possible. Note: You should check that this view 59 * is non-null and of an appropriate type before using. If it is not possible to convert 60 * this view to display the correct data, this method can create a new view. 61 * Heterogeneous lists can specify their number of view types, so that this View is 62 * always of the right type (see {@link #getViewTypeCount()} and 63 * {@link #getItemViewType(int)}). 64 * @param parent The parent that this view will eventually be attached to 65 * @return A View corresponding to the data at the specified position. 66 */ 67 View getView(int position, View convertView, ViewGroup parent); 68 69 /** 70 * An item view type that causes the {@link AdapterView} to ignore the item 71 * view. For example, this can be used if the client does not want a 72 * particular view to be given for conversion in 73 * {@link #getView(int, View, ViewGroup)}. 74 * 75 * @see #getItemViewType(int) 76 * @see #getViewTypeCount() 77 */ 78 static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE; 79 80 /** 81 * Get the type of View that will be created by {@link #getView} for the specified item. 82 * 83 * @param position The position of the item within the adapter's data set whose view type we 84 * want. 85 * @return An integer representing the type of View. Two views should share the same type if one 86 * can be converted to the other in {@link #getView}. Note: Integers must be in the 87 * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can 88 * also be returned. 89 * @see #IGNORE_ITEM_VIEW_TYPE 90 */ 91 int getItemViewType(int position); 92 93 /** 94 * <p> 95 * Returns the number of types of Views that will be created by 96 * {@link #getView}. Each type represents a set of views that can be 97 * converted in {@link #getView}. If the adapter always returns the same 98 * type of View for all items, this method should return 1. 99 * </p> 100 * <p> 101 * This method will only be called when the adapter is set on the {@link AdapterView}. 102 * </p> 103 * 104 * @return The number of types of Views that will be created by this adapter 105 */ 106 int getViewTypeCount(); 107 108 static final int NO_SELECTION = Integer.MIN_VALUE; 109 110 /** 111 * @return true if this adapter doesn't contain any data. This is used to determine 112 * whether the empty view should be displayed. A typical implementation will return 113 * getCount() == 0 but since getCount() includes the headers and footers, specialized 114 * adapters might want a different behavior. 115 */ 116 boolean isEmpty(); 117 }
对于实现部分的具体实现,最上层就是我们可以自定义的Adapter了,里边具体实现了getCount()等方法,这里不再过多说明;
再看另一边的抽象部分,也就是AdapterView抽象类,在抽象部分定义了子类获取的Adapter对象,同时通过getAdapter()获取Adapter对象后调用其方法,从而实现AdapterView的内部业务。部分相关代码如下
1 public abstract class AdapterView<T extends Adapter> extends ViewGroup { 2 /** 3 * 此次省略部分代码 4 */ 5 6 /** 7 * Returns the adapter currently associated with this widget. 8 * 9 * @return The adapter used to provide this view's content. 10 */ 11 public abstract T getAdapter(); 12 13 /** 14 * Sets the adapter that provides the data and the views to represent the data 15 * in this widget. 16 * 17 * @param adapter The adapter to use to create this view's content. 18 */ 19 public abstract void setAdapter(T adapter); 20 21 22 /** 23 * Sets the view to show if the adapter is empty 24 */ 25 @android.view.RemotableViewMethod 26 public void setEmptyView(View emptyView) { 27 mEmptyView = emptyView; 28 29 // If not explicitly specified this view is important for accessibility. 30 if (emptyView != null 31 && emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 32 emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); 33 } 34 35 final T adapter = getAdapter(); 36 final boolean empty = ((adapter == null) || adapter.isEmpty()); 37 updateEmptyStatus(empty); 38 } 39 40 41 @Override 42 public void setFocusable(boolean focusable) { 43 final T adapter = getAdapter(); 44 final boolean empty = adapter == null || adapter.getCount() == 0; 45 46 mDesiredFocusableState = focusable; 47 if (!focusable) { 48 mDesiredFocusableInTouchModeState = false; 49 } 50 51 super.setFocusable(focusable && (!empty || isInFilterMode())); 52 } 53 54 55 @Override 56 public void setFocusableInTouchMode(boolean focusable) { 57 final T adapter = getAdapter(); 58 final boolean empty = adapter == null || adapter.getCount() == 0; 59 60 mDesiredFocusableInTouchModeState = focusable; 61 if (focusable) { 62 mDesiredFocusableState = true; 63 } 64 65 super.setFocusableInTouchMode(focusable && (!empty || isInFilterMode())); 66 } 67 68 void checkFocus() { 69 final T adapter = getAdapter(); 70 final boolean empty = adapter == null || adapter.getCount() == 0; 71 final boolean focusable = !empty || isInFilterMode(); 72 // The order in which we set focusable in touch mode/focusable may matter 73 // for the client, see View.setFocusableInTouchMode() comments for more 74 // details 75 super.setFocusableInTouchMode(focusable && mDesiredFocusableInTouchModeState); 76 super.setFocusable(focusable && mDesiredFocusableState); 77 if (mEmptyView != null) { 78 updateEmptyStatus((adapter == null) || adapter.isEmpty()); 79 } 80 } 81 82 83 /** 84 * Gets the data associated with the specified position in the list. 85 * 86 * @param position Which data to get 87 * @return The data associated with the specified position in the list 88 */ 89 public Object getItemAtPosition(int position) { 90 T adapter = getAdapter(); 91 return (adapter == null || position < 0) ? null : adapter.getItem(position); 92 } 93 94 public long getItemIdAtPosition(int position) { 95 T adapter = getAdapter(); 96 return (adapter == null || position < 0) ? INVALID_ROW_ID : adapter.getItemId(position); 97 } 98 }
最后看一下优化的抽象部分,也就是ListView的相关内容,主要是实现了getAdapter()和setAdatper()方法,并对外提供setAdapter()设置适配器。这部分的相关源码如下,需要注意ListView的直接父类是AbsListView,在AbsListView中定义了ListAdapter对象mAdapter作为默认访问,而AbsListView才继承了AdapterView:
1 @RemoteView 2 public class ListView extends AbsListView { 3 /** 4 * 部分代码已省略 5 */ 6 7 /** 8 * Returns the adapter currently in use in this ListView. The returned adapter 9 * might not be the same adapter passed to {@link #setAdapter(ListAdapter)} but 10 * might be a {@link WrapperListAdapter}. 11 * 12 * @return The adapter currently used to display data in this ListView. 13 * 14 * @see #setAdapter(ListAdapter) 15 */ 16 @Override 17 public ListAdapter getAdapter() { 18 return mAdapter; 19 } 20 21 /** 22 * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService 23 * through the specified intent. 24 * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to. 25 */ 26 @android.view.RemotableViewMethod 27 public void setRemoteViewsAdapter(Intent intent) { 28 super.setRemoteViewsAdapter(intent); 29 } 30 31 /** 32 * Sets the data behind this ListView. 33 * 34 * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter}, 35 * depending on the ListView features currently in use. For instance, adding 36 * headers and/or footers will cause the adapter to be wrapped. 37 * 38 * @param adapter The ListAdapter which is responsible for maintaining the 39 * data backing this list and for producing a view to represent an 40 * item in that data set. 41 * 42 * @see #getAdapter() 43 */ 44 @Override 45 public void setAdapter(ListAdapter adapter) { 46 if (mAdapter != null && mDataSetObserver != null) { 47 mAdapter.unregisterDataSetObserver(mDataSetObserver); 48 } 49 50 resetList(); 51 mRecycler.clear(); 52 53 if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { 54 mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); 55 } else { 56 mAdapter = adapter; 57 } 58 59 mOldSelectedPosition = INVALID_POSITION; 60 mOldSelectedRowId = INVALID_ROW_ID; 61 62 // AbsListView#setAdapter will update choice mode states. 63 super.setAdapter(adapter); 64 65 if (mAdapter != null) { 66 mAreAllItemsSelectable = mAdapter.areAllItemsEnabled(); 67 mOldItemCount = mItemCount; 68 mItemCount = mAdapter.getCount(); 69 checkFocus(); 70 71 mDataSetObserver = new AdapterDataSetObserver(); 72 mAdapter.registerDataSetObserver(mDataSetObserver); 73 74 mRecycler.setViewTypeCount(mAdapter.getViewTypeCount()); 75 76 int position; 77 if (mStackFromBottom) { 78 position = lookForSelectablePosition(mItemCount - 1, false); 79 } else { 80 position = lookForSelectablePosition(0, true); 81 } 82 setSelectedPositionInt(position); 83 setNextSelectedPositionInt(position); 84 85 if (mItemCount == 0) { 86 // Nothing selected 87 checkSelectionChanged(); 88 } 89 } else { 90 mAreAllItemsSelectable = true; 91 checkFocus(); 92 // Nothing selected 93 checkSelectionChanged(); 94 } 95 96 requestLayout(); 97 } 98 }
简言归纳一下桥接模式,实现部分定义操作方法,抽象部分定义操作的流程方法,其中,抽象部分持有实现部分的对象引用。各自的实现类实现他们的方法逻辑。