├── .DS_Store ├── README.md ├── abstract-factory └── README.md ├── adapter ├── .DS_Store ├── README.md └── mr.simple │ └── readme.md ├── bridge ├── README.md └── shen0834 │ └── readme.md ├── builder ├── .DS_Store ├── README.md └── mr.simple │ ├── .DS_Store │ ├── images │ ├── .DS_Store │ ├── builder-uml.png │ └── result.png │ └── readme.md ├── chain-of-responsibility ├── AigeStudio │ ├── images │ │ └── chain-of-responsibility.jpg │ └── readme.md └── README.md ├── command ├── .DS_Store ├── README.md └── lijunhuayc │ ├── .DS_Store │ ├── images │ ├── .DS_Store │ ├── lijunhuayc_result.png │ └── lijunhuayc_uml.png │ ├── readme.md │ └── resource │ └── command │ ├── ClientRole.java │ ├── Command.java │ ├── ConcreteCommandImpl1.java │ ├── ConcreteCommandImpl2.java │ ├── ConcreteCommandImpl3.java │ ├── InvokerRole.java │ ├── MainTest.java │ ├── PeopleBean.java │ └── ReceiverRole.java ├── composite ├── README.md └── tiny-times │ └── readme.md ├── decorator ├── README.md └── tiny-times │ └── readme.md ├── facade ├── .DS_Store ├── README.md └── elsdnwn │ ├── .DS_Store │ ├── images │ ├── .DS_Store │ ├── contextimpl.png │ ├── facade.png │ └── no-facade.png │ └── readme.md ├── factory-method ├── AigeStudio │ ├── images │ │ └── factory-method.jpg │ └── readme.md └── README.md ├── flyweight ├── README.md └── lvtea0105 │ └── readme.md ├── interpreter └── README.md ├── iterator ├── .DS_Store ├── README.md └── haoxiqiang │ ├── images │ └── Iterator_UML_class_diagram.svg.png │ ├── readme.md │ └── resource │ └── AndroidMileage.java ├── mediator └── README.md ├── memento └── README.md ├── observer ├── .DS_Store └── README.md ├── oop-principles └── oop-principles.md ├── prototype ├── .DS_Store ├── README.md └── mr.simple │ ├── .DS_Store │ ├── images │ ├── prototype-uml.png │ ├── result-2.png │ ├── result-3.png │ ├── result.png │ └── sms.png │ └── readme.md ├── proxy ├── .DS_Store ├── README.md └── singwhatiwanna │ ├── README.md │ └── images │ └── proxy-uml.png ├── singleton ├── .DS_Store ├── README.md └── mr.simple │ ├── .DS_Store │ ├── images │ ├── .DS_Store │ ├── singleton-mr.simple-result.png │ └── singleton-mr.simple-uml.png │ └── readme.md ├── state ├── README.md └── Thinan │ └── readme.md ├── strategy ├── .DS_Store ├── README.md └── gkerison │ ├── README.md │ └── images │ ├── strategy-kerison-uml-android-animation-matrix.png │ ├── strategy-kerison-uml-android-animation.png │ ├── strategy-kerison-uml-android-interpolator.png │ ├── strategy-kerison-uml-android.png │ ├── strategy-kerison-uml-calc-result.png │ ├── strategy-kerison-uml-calc.png │ └── strategy-kerison-uml.png ├── template-method ├── .DS_Store ├── README.md └── mr.simple │ ├── .DS_Store │ ├── images │ ├── .DS_Store │ ├── async-flow.png │ ├── flow.png │ └── uml.png │ └── readme.md ├── template.md └── visitor ├── .DS_Store └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android源码设计模式分析开源项目 2 | 3 | ## 简述 4 | 该项目通过分析Android系统中的设计模式来提升大家对设计模式的理解,从源码的角度来剖析既增加了对Android系统本身的了解,也从优秀的设计中领悟模式的实际运用以及它适用的场景,避免在实际开发中的生搬硬套。如果你对面向对象的六大开发原则还不太熟悉,那么在学习模式之前先学习一下[面向对象的六大原则](oop-principles/oop-principles.md)是非常有必要的。 5 | 6 | 7 | ## 任务表 ( 一期截止 2015.3.20 ) 8 | | 模式名 | 分析者 | 状态 | 9 | | ------------- | ------------- |--------------| 10 | | [单例模式](singleton/mr.simple) | [Mr.Simple](https://github.com/bboyfeiyu)| 完成 | 11 | | [Builder模式](builder/mr.simple) | [Mr.Simple](https://github.com/bboyfeiyu)| 完成 | 12 | | [外观模式](facade/elsdnwn) | [elsdnwn](https://github.com/elsdnwn)、[Mr.Simple](https://github.com/bboyfeiyu)| 完成 | 13 | | [模板方法](template-method/mr.simple) | [Mr.Simple](https://github.com/bboyfeiyu) | 完成 | 14 | | [策略模式](strategy/gkerison) | [GKerison](https://github.com/GKerison) | 完成 | 15 | | [代理模式](proxy/singwhatiwanna) | [singwhatiwanna](https://github.com/singwhatiwanna) | 完成 | 16 | | [迭代器模式](iterator/haoxiqiang) | [Haoxiqiang](https://github.com/Haoxiqiang)| 完成 | 17 | | [责任链模式](chain-of-responsibility/AigeStudio) | [AigeStudio](https://github.com/AigeStudio)| 完成 | 18 | | [命令模式](command/lijunhuayc) | [lijunhuayc](https://github.com/lijunhuayc)| 完成 | 19 | | [桥接模式](bridge/shen0834) | [shen0834](https://github.com/shen0834)| 完成 | 20 | | [原型模式](prototype/mr.simple) | [Mr.Simple](https://github.com/bboyfeiyu)| 完成 | 21 | 22 | ## 参考资料 23 | * [GOF的设计模式:可复用面向对象软件的基础](http://item.jd.com/10057319.html) 24 | * [设计模式之禅](http://item.jd.com/11414555.html) 25 | * [Java与模式](http://item.jd.com/10094286.html) 26 | * [java-design-patterns](https://github.com/iluwatar/java-design-patterns) 27 | * [Java之美[从菜鸟到高手演变]之设计模式](http://blog.csdn.net/zhangerqing/article/details/8194653) 28 | -------------------------------------------------------------------------------- /abstract-factory/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [用户名](git地址) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /adapter/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/adapter/.DS_Store -------------------------------------------------------------------------------- /adapter/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [Mr.Simple](https://github.com/bboyfeiyu) | 2015.3.2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /adapter/mr.simple/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之适配器(Adapter)模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 适配器模式 分析 4 | > Android系统版本: 2.3 5 | > 分析者:[Mr.Simple](https://github.com/bboyfeiyu),分析状态:完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:完成 6 | 7 | 8 | 9 | ## 1. 模式介绍 10 | ### 模式的定义 11 | 适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。 12 | 13 | ### 使用场景 14 |   用电源接口做例子,笔记本电脑的电源一般都是接受5V的电压,但是我们生活中的电线电压一般都是220V的输出。这个时候就出现了不匹配的状况,在软件开发中我们称之为接口不兼容,此时就需要适配器来进行一个接口转换。在软件开发中有一句话正好体现了这点:任何问题都可以加一个中间层来解决。这个层我们可以理解为这里的Adapter层,通过这层来进行一个接口转换就达到了兼容的目的。 15 | 16 | ## 2. UML类图 17 |    18 |   适配器模式也分两种,即类适配器模式、对象适配器模式,结构图如图1、图2。 19 |    20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
图1图2
29 | 30 |    如图所示,类适配器是通过实现Target接口以及继承Adaptee类来实现接口转换,而对象适配器模式则是通过实现Target接口和代理Adaptee的某个方法来实现。结构上略有不同。 31 | 32 | ### 角色介绍   33 | * 目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。 34 | * 源(Adapee)角色:现在需要适配的接口。 35 | * 适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。 36 | 37 | 38 | ## 3. 模式的简单实现 39 | 40 | 在上述电源接口这个示例中,5V电压就是Target接口,220v电压就是Adaptee类,而将电压从220V转换到5V就是Adapter。 41 | 42 | ### 类适配器模式 43 | 44 | ```java 45 | /** 46 | * Target角色 47 | */ 48 | public interface FiveVolt { 49 | public int getVolt5(); 50 | } 51 | 52 | /** 53 | * Adaptee角色,需要被转换的对象 54 | */ 55 | public class Volt220 { 56 | public int getVolt220() { 57 | return 220; 58 | } 59 | } 60 | 61 | // adapter角色 62 | public class ClassAdapter extends Volt220 implements FiveVolt { 63 | 64 | @Override 65 | public int getVolt5() { 66 | return 5; 67 | } 68 | 69 | } 70 | ``` 71 |   Target角色给出了需要的目标接口,而Adaptee类则是需要被转换的对象。Adapter则是将Volt220转换成Target的接口。对应的是Target的目标是要获取5V的输出电压,而Adaptee即正常输出电压是220V,此时我们就需要电源适配器类将220V的电压转换为5V电压,解决接口不兼容的问题。 72 | 73 | ```java 74 | public class Test { 75 | public static void main(String[] args) { 76 | ClassAdapter adapter = new ClassAdapter(); 77 | System.out.println("输出电压 : " + adapter.getVolt5()); 78 | } 79 | } 80 | ``` 81 |   82 | 83 | ### 对象适配器模式 84 |   与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用代理关系连接到Adaptee类。 85 |    86 |   从图2可以看出,Adaptee类 ( Volt220 ) 并没有getVolt5()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,需要提供一个包装类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是委派关系,这决定了适配器模式是对象的。 87 | 88 | 示例代码如下 : 89 | 90 | ```java 91 | /** 92 | * Target角色 93 | */ 94 | public interface FiveVolt { 95 | public int getVolt5(); 96 | } 97 | 98 | /** 99 | * Adaptee角色,需要被转换的对象 100 | */ 101 | public class Volt220 { 102 | public int getVolt220() { 103 | return 220; 104 | } 105 | } 106 | 107 | // 对象适配器模式 108 | public class ObjectAdapter implements FiveVolt { 109 | 110 | Volt220 mVolt220; 111 | 112 | public ObjectAdapter(Volt220 adaptee) { 113 | mVolt220 = adaptee; 114 | } 115 | 116 | public int getVolt220() { 117 | return mVolt220.getVolt220(); 118 | } 119 | 120 | @Override 121 | public int getVolt5() { 122 | return 5; 123 | } 124 | 125 | } 126 | ``` 127 | 注意,这里为了节省代码,我们并没有遵循一些面向对象的基本原则。 128 | 129 | 使用示例 : 130 | 131 | ```java 132 | public class Test { 133 | public static void main(String[] args) { 134 | ObjectAdapter adapter = new ObjectAdapter(new Volt220()); 135 | System.out.println("输出电压 : " + adapter.getVolt5()); 136 | } 137 | } 138 | ``` 139 | 140 | ### 类适配器和对象适配器的权衡 141 | 142 |   *  类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。 143 | 144 |   *  对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理Adaptee的子类了。对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。 145 | 146 |   *  对于类适配器,适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。对于对象适配器,要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。 147 | 148 |   *  对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。对于对象适配器,需要额外的引用来间接得到Adaptee。 149 | 150 |   建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。 151 |    152 | 153 | ## Android ListView中的Adapter模式 154 | 在开发过程中,ListView的Adapter是我们最为常见的类型之一。一般的用法大致如下: 155 | 156 | ```java 157 | // 代码省略 158 | ListView myListView = (ListView)findViewById(listview_id); 159 | // 设置适配器 160 | myListView.setAdapter(new MyAdapter(context, myDatas)); 161 | 162 | // 适配器 163 | public class MyAdapter extends BaseAdapter{ 164 | 165 | private LayoutInflater mInflater; 166 | List mDatas ; 167 | 168 | public MyAdapter(Context context, List datas){ 169 | this.mInflater = LayoutInflater.from(context); 170 | mDatas = datas ; 171 | } 172 | @Override 173 | public int getCount() { 174 | return mDatas.size(); 175 | } 176 | 177 | @Override 178 | public String getItem(int pos) { 179 | return mDatas.get(pos); 180 | } 181 | 182 | @Override 183 | public long getItemId(int pos) { 184 | return pos; 185 | } 186 | 187 | // 解析、设置、缓存convertView以及相关内容 188 | @Override 189 | public View getView(int position, View convertView, ViewGroup parent) { 190 | ViewHolder holder = null; 191 | // Item View的复用 192 | if (convertView == null) { 193 | holder = new ViewHolder(); 194 | convertView = mInflater.inflate(R.layout.my_listview_item, null); 195 | // 获取title 196 | holder.title = (TextView)convertView.findViewById(R.id.title); 197 | convertView.setTag(holder); 198 | } else { 199 | holder = (ViewHolder)convertView.getTag(); 200 | } 201 | holder.title.setText(mDatas.get(position)); 202 | return convertView; 203 | } 204 | 205 | } 206 | ``` 207 | 这看起来似乎还挺麻烦的,看到这里我们不禁要问,ListView为什么要使用Adapter模式呢? 208 | 我们知道,作为最重要的View,ListView需要能够显示各式各样的视图,每个人需要的显示效果各不相同,显示的数据类型、数量等也千变万化。那么如何隔离这种变化尤为重要。 209 | 210 | Android的做法是增加一个Adapter层来应对变化,将ListView需要的接口抽象到Adapter对象中,这样只要用户实现了Adapter的接口,ListView就可以按照用户设定的显示效果、数量、数据来显示特定的Item View。 211 | 通过代理数据集来告知ListView数据的个数( getCount函数 )以及每个数据的类型( getItem函数 ),最重要的是要解决Item View的输出。Item View千变万化,但终究它都是View类型,Adapter统一将Item View输出为View ( getView函数 ),这样就很好的应对了Item View的可变性。 212 | 213 | 那么ListView是如何通过Adapter模式 ( 不止Adapter模式 )来运作的呢 ?我们一起来看一看。 214 | ListView继承自AbsListView,Adapter定义在AbsListView中,我们看一看这个类。 215 | 216 | ```java 217 | public abstract class AbsListView extends AdapterView implements TextWatcher, 218 | ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener, 219 | ViewTreeObserver.OnTouchModeChangeListener, 220 | RemoteViewsAdapter.RemoteAdapterConnectionCallback { 221 | 222 | ListAdapter mAdapter ; 223 | 224 | // 关联到Window时调用的函数 225 | @Override 226 | protected void onAttachedToWindow() { 227 | super.onAttachedToWindow(); 228 | // 代码省略 229 | // 给适配器注册一个观察者,该模式下一篇介绍。 230 | if (mAdapter != null && mDataSetObserver == null) { 231 | mDataSetObserver = new AdapterDataSetObserver(); 232 | mAdapter.registerDataSetObserver(mDataSetObserver); 233 | 234 | // Data may have changed while we were detached. Refresh. 235 | mDataChanged = true; 236 | mOldItemCount = mItemCount 237 | // 获取Item的数量,调用的是mAdapter的getCount方法 238 | mItemCount = mAdapter.getCount(); 239 | } 240 | mIsAttached = true; 241 | } 242 | 243 | /** 244 | * 子类需要覆写layoutChildren()函数来布局child view,也就是Item View 245 | */ 246 | @Override 247 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 248 | super.onLayout(changed, l, t, r, b); 249 | mInLayout = true; 250 | if (changed) { 251 | int childCount = getChildCount(); 252 | for (int i = 0; i < childCount; i++) { 253 | getChildAt(i).forceLayout(); 254 | } 255 | mRecycler.markChildrenDirty(); 256 | } 257 | 258 | if (mFastScroller != null && mItemCount != mOldItemCount) { 259 | mFastScroller.onItemCountChanged(mOldItemCount, mItemCount); 260 | } 261 | // 布局Child View 262 | layoutChildren(); 263 | mInLayout = false; 264 | 265 | mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR; 266 | } 267 | 268 | // 获取一个Item View 269 | View obtainView(int position, boolean[] isScrap) { 270 | isScrap[0] = false; 271 | View scrapView; 272 | // 从缓存的Item View中获取,ListView的复用机制就在这里 273 | scrapView = mRecycler.getScrapView(position); 274 | 275 | View child; 276 | if (scrapView != null) { 277 | // 代码省略 278 | child = mAdapter.getView(position, scrapView, this); 279 | // 代码省略 280 | } else { 281 | child = mAdapter.getView(position, null, this); 282 | // 代码省略 283 | } 284 | 285 | return child; 286 | } 287 | } 288 | ``` 289 | AbsListView定义了集合视图的框架,比如Adapter模式的应用、复用Item View的逻辑、布局Item View的逻辑等。子类只需要覆写特定的方法即可实现集合视图的功能,例如ListView。 290 | 291 | ListView中的相关方法。 292 | 293 | ```java 294 | @Override 295 | protected void layoutChildren() { 296 | // 代码省略 297 | 298 | try { 299 | super.layoutChildren(); 300 | invalidate(); 301 | // 代码省略 302 | // 根据布局模式来布局Item View 303 | switch (mLayoutMode) { 304 | case LAYOUT_SET_SELECTION: 305 | if (newSel != null) { 306 | sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom); 307 | } else { 308 | sel = fillFromMiddle(childrenTop, childrenBottom); 309 | } 310 | break; 311 | case LAYOUT_SYNC: 312 | sel = fillSpecific(mSyncPosition, mSpecificTop); 313 | break; 314 | case LAYOUT_FORCE_BOTTOM: 315 | sel = fillUp(mItemCount - 1, childrenBottom); 316 | adjustViewsUpOrDown(); 317 | break; 318 | case LAYOUT_FORCE_TOP: 319 | mFirstPosition = 0; 320 | sel = fillFromTop(childrenTop); 321 | adjustViewsUpOrDown(); 322 | break; 323 | case LAYOUT_SPECIFIC: 324 | sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop); 325 | break; 326 | case LAYOUT_MOVE_SELECTION: 327 | sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom); 328 | break; 329 | default: 330 | // 代码省略 331 | break; 332 | } 333 | 334 | } 335 | 336 | // 从上到下填充Item View [ 只是其中一种填充方式 ] 337 | private View fillDown(int pos, int nextTop) { 338 | View selectedView = null; 339 | 340 | int end = (mBottom - mTop); 341 | if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) { 342 | end -= mListPadding.bottom; 343 | } 344 | 345 | while (nextTop < end && pos < mItemCount) { 346 | // is this the selected item? 347 | boolean selected = pos == mSelectedPosition; 348 | View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected); 349 | 350 | nextTop = child.getBottom() + mDividerHeight; 351 | if (selected) { 352 | selectedView = child; 353 | } 354 | pos++; 355 | } 356 | 357 | return selectedView; 358 | } 359 | 360 | // 添加Item View 361 | private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, 362 | boolean selected) { 363 | View child; 364 | 365 | // 代码省略 366 | // Make a new view for this position, or convert an unused view if possible 367 | child = obtainView(position, mIsScrap); 368 | 369 | // This needs to be positioned and measured 370 | setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); 371 | 372 | return child; 373 | } 374 | 375 | ``` 376 | ListView覆写了AbsListView中的layoutChilden函数,在该函数中根据布局模式来布局Item View。Item View的个数、样式都通过Adapter对应的方法来获取,获取个数、Item View之后,将这些Item View布局到ListView对应的坐标上,再加上Item View的复用机制,整个ListView就基本运转起来了。 377 | 378 | 当然这里的Adapter并不是经典的适配器模式,但是却是对象适配器模式的优秀示例,也很好的体现了面向对象的一些基本原则。这里的Target角色和Adapter角色融合在一起,Adapter中的方法就是目标方法;而Adaptee角色就是ListView的数据集与Item View,Adapter代理数据集,从而获取到数据集的个数、元素。 379 | 380 | 通过增加Adapter一层来将Item View的操作抽象起来,ListView等集合视图通过Adapter对象获得Item的个数、数据元素、Item View等,从而达到适配各种数据、各种Item视图的效果。因为Item View和数据类型千变万化,Android的架构师们将这些变化的部分交给用户来处理,通过getCount、getItem、getView等几个方法抽象出来,也就是将Item View的构造过程交给用户来处理,灵活地运用了适配器模式,达到了无限适配、拥抱变化的目的。 381 | 382 | ## 杂谈 383 | ### 优点 384 | * 更好的复用性 385 |   系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。 386 | 387 | * 更好的扩展性 388 |   在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。 389 | 390 | ### 缺点 391 | * 过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 392 | -------------------------------------------------------------------------------- /bridge/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [用户名](git地址) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /bridge/shen0834/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之桥接模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 桥接模式 分析 4 | > Android系统版本: 4.2 5 | > 分析者:[shen0834](https://github.com/shen0834),分析状态:未完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:完成 6 | 7 | ## 模式介绍 8 | 9 | ### 模式的定义 10 | 11 | 将抽象部分与实现部分分离,使它们都可以独立的变化。 12 | 13 | ### 模式的使用场景 14 | * 如果一个系统需要在构件的抽象化角色和具体化角色之间添加更多的灵活性,避免在两个层次之间建立静态的联系。 15 | * 设计要求实现化角色的任何改变不应当影响客户端,或者实现化角色的改变对客户端是完全透明的。 16 | * 需要跨越多个平台的图形和窗口系统上。 17 | * 一个类存在两个独立变化的维度,且两个维度都需要进行扩展。 18 | 19 | ### UML类图 20 | 21 | ![uml](http://img.blog.csdn.net/20150322120730408) 22 | 23 | ### 角色介绍 24 | 25 | * 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。 26 | 修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。 27 | * 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接 口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。 28 | * 具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。 29 | 30 | ## 模式的简单实现 31 | 32 | ### 介绍 33 | 34 | 其实Java的虚拟机就是一个很好的例子,在不同平台平台上,用不同的虚拟机进行实现,这样只需把Java程序编译成符合虚拟机规范的文件,且只用编译一次,便在不同平台上都能工作。 但是这样说比较抽象,用一个简单的例子来实现bridge模式。 35 | 36 | 编写一个程序,使用两个绘图的程序的其中一个来绘制矩形或者原型,同时,在实例化矩形的时候,它要知道使用绘图程序1(DP1)还是绘图程序2(DP2)。 37 | 38 | (ps:假设dp1和dp2的绘制方式不一样,它们是用不同方式进行绘制,示例代码,不讨论过多细节) 39 | 40 | ### 实现源码 41 | 42 | ```java 43 | 首先是两个绘图程序dp1,dp2 44 | //具体的绘图程序类dp1 45 | public class DP1 { 46 | 47 | public void draw_1_Rantanle(){ 48 | System.out.println("使用DP1的程序画矩形"); 49 | } 50 | 51 | public void draw_1_Circle(){ 52 | System.out.println("使用DP1的程序画圆形"); 53 | } 54 | } 55 | //具体的绘图程序类dp2 56 | public class DP2 { 57 | 58 | public void drawRantanle(){ 59 | System.out.println("使用DP2的程序画矩形"); 60 | } 61 | 62 | public void drawCircle(){ 63 | System.out.println("使用DP2的程序画圆形"); 64 | } 65 | 66 | } 67 | 接着​抽象的形状Shape和两个派生类:矩形Rantanle和圆形Circle 68 | //抽象化角色Abstraction 69 | abstract class Shape { 70 | //持有实现的角色Implementor(作图类) 71 | protected Drawing myDrawing; 72 | 73 | public Shape(Drawing drawing) { 74 | this.myDrawing = drawing; 75 | } 76 | 77 | abstract public void draw(); 78 | 79 | //保护方法drawRectangle 80 | protected void drawRectangle(){ 81 | //this.impl.implmentation() 82 | myDrawing.drawRantangle(); 83 | } 84 | 85 | //保护方法drawCircle 86 | protected void drawCircle(){ 87 | //this.impl.implmentation() 88 | myDrawing.drawCircle(); 89 | } 90 | } 91 | //修正抽象化角色Refined Abstraction(矩形) 92 | public class Rantangle extends Shape{ 93 | public Rantangle(Drawing drawing) { 94 | super(drawing); 95 | } 96 | 97 | @Override 98 | public void draw() { 99 | drawRectangle(); 100 | } 101 | } 102 | //修正抽象化角色Refined Abstraction(圆形) 103 | public class Circle extends Shape { 104 | public Circle(Drawing drawing) { 105 | super(drawing); 106 | } 107 | @Override 108 | public void draw() { 109 | drawCircle(); 110 | } 111 | } 112 | 最后,我们的实现绘图的Drawing和分别实现dp1的V1Drawing和dp2的V2Drawing 113 | //实现化角色Implementor 114 | //implmentation两个方法,画圆和画矩形 115 | public interface Drawing { 116 | public void drawRantangle(); 117 | public void drawCircle(); 118 | } 119 | //具体实现化逻辑ConcreteImplementor 120 | //实现了接口方法,使用DP1进行绘图 121 | public class V1Drawing implements Drawing{ 122 | 123 | DP1 dp1; 124 | 125 | public V1Drawing() { 126 | dp1 = new DP1(); 127 | } 128 | @Override 129 | public void drawRantangle() { 130 | dp1.draw_1_Rantanle(); 131 | } 132 | @Override 133 | public void drawCircle() { 134 | dp1.draw_1_Circle(); 135 | } 136 | } 137 | //具体实现化逻辑ConcreteImplementor 138 | //实现了接口方法,使用DP2进行绘图 139 | public class V2Drawing implements Drawing{ 140 | DP2 dp2; 141 | 142 | public V2Drawing() { 143 | dp2 = new DP2(); 144 | } 145 | 146 | @Override 147 | public void drawRantangle() { 148 | dp2.drawRantanle(); 149 | } 150 | @Override 151 | public void drawCircle() { 152 | dp2.drawCircle(); 153 | } 154 | } 155 | ``` 156 | 157 | ​在这个示例中,图形Shape类有两种类型,圆形和矩形,为了使用不同的绘图程序绘制图形,把实现的部分进行了分离,构成了Drawing类层次结构,包括V1Drawing和V2Drawing。在具体实现类中,V1Drawing控制着DP1程序进行绘图,V2Drawing控制着DP2程序进行绘图,以及保护的方法drawRantangle,drawCircle(Shape类中) 。 158 | 159 | ## Android源码中的模式实现 160 | 161 | 在Android中也运用到了Bridge模式,我们使用很多的ListView和BaseAdpater其实就是Bridge模式的运行,很多人会问这个不是Adapter模式,接下来根据源码来分析。 162 | 163 | 首先ListAdapter.java: 164 | 165 | ```java 166 | public interface ListAdapter extends Adapter{ 167 | //继承自Adapter,扩展了自己的两个实现方法 168 | public boolean areAllItemsEnabled(); 169 | boolean isEnabled(int position); 170 | } 171 | ``` 172 | 173 | 这里先来看一下父类AdapterView。 174 | 175 | ```java 176 | public abstract class AdapterView extends ViewGroup { 177 | //这里需要一个泛型的Adapter 178 | public abstract T getAdapter(); 179 | public abstract void setAdapter(T adapter); 180 | } 181 | ``` 182 | 183 | 接着来看ListView的父类AbsListView,继承自AdapterView 184 | 185 | ```java 186 | public abstract class AbsListView extends AdapterView 187 | //继承自AdapterView,并且指明了T为ListAdapter 188 | /** 189 | * The adapter containing the data to be displayed by this view 190 | */ 191 | ListAdapter mAdapter; 192 | //代码省略 193 | //这里实现了setAdapter的方法,实例了对实现化对象的引用 194 | public void setAdapter(ListAdapter adapter) { 195 | //这的adapter是从子类传入上来,也就是listview,拿到了具体实现化的对象 196 | if (adapter != null) { 197 | mAdapterHasStableIds = mAdapter.hasStableIds(); 198 | if (mChoiceMode != CHOICE_MODE_NONE && mAdapterHasStableIds && 199 | mCheckedIdStates == null) { 200 | mCheckedIdStates = new LongSparseArray(); 201 | } 202 | } 203 | if (mCheckStates != null) { 204 | mCheckStates.clear(); 205 | } 206 | if (mCheckedIdStates != null) { 207 | mCheckedIdStates.clear(); 208 | } 209 | } 210 | ``` 211 | 212 | 大家都知道,构建一个listview,adapter中最重要的两个方法,getCount()告知数量,getview()告知具体的view类型,接下来看看AbsListView作为一个视图的集合是如何来根据实现化对象adapter来实现的具体的view呢? 213 | 214 | ```java 215 | protected void onAttachedToWindow() { 216 | super.onAttachedToWindow(); 217 | 218 | //省略代码, 219 | //这里在加入window的时候,getCount()确定了集合的个数 220 | mDataChanged = true; 221 | mOldItemCount = mItemCount; 222 | mItemCount = mAdapter.getCount(); 223 | } 224 | } 225 | ``` 226 | 227 | 接着来看 228 | 229 | ```java 230 | View obtainView(int position, boolean[] isScrap) { 231 | //代码省略 232 | ​//这里根据位置显示具体的view,return的child是从持有的实现对象mAdapter里面的具体实现的 233 | ​//方法getview来得到的。 234 | final View child = mAdapter.getView(position, scrapView, this); 235 | //代码省略 236 | return child; 237 | } 238 | ``` 239 | 240 | 接下来在ListView中,onMeasure调用了obtainView来确定宽高,在扩展自己的方法来排列这些view。知道了 241 | 242 | 这些以后,我们来画一个简易的UML图来看下: 243 | 244 | ![uml](http://img.blog.csdn.net/20150322120809221) 245 | 246 | 对比下GOF的上图,是不是发现很像呢?实际上最开始研究Adapter模式的时候,越看越不对啊,于是整理结构,画了UML发现这更像是一个bridge模式,那时候对设计模式也是模模糊糊的,于是静下来研究。抽象化的角色一个视图的集合AdapterView,它扩展了AbsListView,AbsSpinner,接下来他们分别扩展了ListView,GridView,Spinner,Gallery,用不同方式来展现这些ItemViews,我们继续扩展类似ListView的PulltoRefreshView等等。而实现化角色Adapter扩展了ListAdpater,SpinnerAdapter,接着具体的实现化角色BaseAdapter实现了他们,我们通过继承BaseAdapter又实现了我们各式各样的ItemView。 247 | 248 | 249 | ## 杂谈 250 | 251 | 这里就是Android工程师的牛X之处了,用一个bridge和adapter来解决了一个大的难题。试想一下,视图的排列方式是无穷尽,是人们每个人开发的视图也是无穷尽的。如果你正常开发,你需要多少类来完成呢?而Android把最常用用的展现方式全部都封装了出来,而在实现角色通过Adapter模式来应变无穷无尽的视图需要。抽象化了一个容器使用适配器来给容器里面添加视图,容器的形状(或理解为展现的方式)以及怎么样来绘制容器内的视图,你都可以独自的变化,双双不会干扰,真正的脱耦,就要最开始说的那样:“将抽象部分与实现部分分离,使它们都可以独立的变化。” 252 | 253 | 从上面的两个案例,我们可以看出,我们在两个解决方案中都用到bridge和adapter模式,那是因为我们必须使用给定的绘图程序(adapter适配器),绘图程序(adapter适配器)有已经存在的接口必须要遵循,因此需要使用Adapter进行适配,然后才能用同样的方式处理他们,他们经常一起使用,并且相似,但是Adapter并不是Bridge的一部分。 254 | 255 | ### 优点与缺点 256 | 实现与使用实现的对象解耦,提供了可扩展性,客户对象无需担心操作的实现问题。 如果你采用了bridge模式,在处理新的实现将会非常容易。你只需定义一个新的具体实现类,并且实现它就好了,不需要修改任何其他的东西。但是如果你出现了一个新的具体情况,需要对实现进行修改时,就得先修改抽象的接口,再对其派生类进行修改,但是这种修改只会存在于局部,并且这种修改将变化的英雄控制在局部,并且降低了出现副作用的风险,而且类之间的关系十分清晰,如何实现一目了然。 257 | -------------------------------------------------------------------------------- /builder/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/builder/.DS_Store -------------------------------------------------------------------------------- /builder/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [Mr.Simple](https://github.com/bboyfeiyu) | 2015.3.2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /builder/mr.simple/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/builder/mr.simple/.DS_Store -------------------------------------------------------------------------------- /builder/mr.simple/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/builder/mr.simple/images/.DS_Store -------------------------------------------------------------------------------- /builder/mr.simple/images/builder-uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/builder/mr.simple/images/builder-uml.png -------------------------------------------------------------------------------- /builder/mr.simple/images/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/builder/mr.simple/images/result.png -------------------------------------------------------------------------------- /builder/mr.simple/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之Builder模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 Builder模式 分析 4 | > Android系统版本: 2.3 5 | > 分析者:[Mr.Simple](https://github.com/bboyfeiyu),分析状态:完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:完成 6 | 7 | 8 | ## 1. 模式介绍 9 | 10 | ### 模式的定义 11 | 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 12 | 13 | ### 模式的使用场景 14 | 1. 相同的方法,不同的执行顺序,产生不同的事件结果时; 15 | 2. 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时; 16 | 3. 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适; 17 | 18 | 19 | ## 2. UML类图 20 | ![url](images/builder-uml.png) 21 | 22 | ### 角色介绍 23 | * Product 产品类 : 产品的抽象类。 24 | * Builder : 抽象类, 规范产品的组建,一般是由子类实现具体的组件过程。 25 | * ConcreteBuilder : 具体的构建器. 26 | * Director : 统一组装过程(可省略)。 27 | 28 | 29 | 30 | ## 3. 模式的简单实现 31 | ### 简单实现的介绍 32 | 电脑的组装过程较为复杂,步骤繁多,但是顺序却是不固定的。下面我们以组装电脑为例来演示一下简单且经典的builder模式。 33 | 34 | ### 实现源码 35 | 36 | ```java 37 | package com.dp.example.builder; 38 | 39 | /** 40 | * Computer产品抽象类, 为了例子简单, 只列出这几个属性 41 | * 42 | * @author mrsimple 43 | * 44 | */ 45 | public abstract class Computer { 46 | 47 | protected int mCpuCore = 1; 48 | protected int mRamSize = 0; 49 | protected String mOs = "Dos"; 50 | 51 | protected Computer() { 52 | 53 | } 54 | 55 | // 设置CPU核心数 56 | public abstract void setCPU(int core); 57 | 58 | // 设置内存 59 | public abstract void setRAM(int gb); 60 | 61 | // 设置操作系统 62 | public abstract void setOs(String os); 63 | 64 | @Override 65 | public String toString() { 66 | return "Computer [mCpuCore=" + mCpuCore + ", mRamSize=" + mRamSize 67 | + ", mOs=" + mOs + "]"; 68 | } 69 | 70 | } 71 | 72 | package com.dp.example.builder; 73 | 74 | /** 75 | * Apple电脑 76 | */ 77 | public class AppleComputer extends Computer { 78 | 79 | protected AppleComputer() { 80 | 81 | } 82 | 83 | @Override 84 | public void setCPU(int core) { 85 | mCpuCore = core; 86 | } 87 | 88 | @Override 89 | public void setRAM(int gb) { 90 | mRamSize = gb; 91 | } 92 | 93 | @Override 94 | public void setOs(String os) { 95 | mOs = os; 96 | } 97 | 98 | } 99 | 100 | package com.dp.example.builder; 101 | 102 | 103 | package com.dp.example.builder; 104 | 105 | /** 106 | * builder抽象类 107 | * 108 | */ 109 | public abstract class Builder { 110 | // 设置CPU核心数 111 | public abstract void buildCPU(int core); 112 | 113 | // 设置内存 114 | public abstract void buildRAM(int gb); 115 | 116 | // 设置操作系统 117 | public abstract void buildOs(String os); 118 | 119 | // 创建Computer 120 | public abstract Computer create(); 121 | 122 | } 123 | 124 | package com.dp.example.builder; 125 | 126 | public class ApplePCBuilder extends Builder { 127 | private Computer mApplePc = new AppleComputer(); 128 | 129 | @Override 130 | public void buildCPU(int core) { 131 | mApplePc.setCPU(core); 132 | } 133 | 134 | @Override 135 | public void buildRAM(int gb) { 136 | mApplePc.setRAM(gb); 137 | } 138 | 139 | @Override 140 | public void buildOs(String os) { 141 | mApplePc.setOs(os); 142 | } 143 | 144 | @Override 145 | public Computer create() { 146 | return mApplePc; 147 | } 148 | 149 | } 150 | 151 | package com.dp.example.builder; 152 | 153 | public class Director { 154 | Builder mBuilder = null; 155 | 156 | /** 157 | * 158 | * @param builder 159 | */ 160 | public Director(Builder builder) { 161 | mBuilder = builder; 162 | } 163 | 164 | /** 165 | * 构建对象 166 | * 167 | * @param cpu 168 | * @param ram 169 | * @param os 170 | */ 171 | public void construct(int cpu, int ram, String os) { 172 | mBuilder.buildCPU(cpu); 173 | mBuilder.buildRAM(ram); 174 | mBuilder.buildOs(os); 175 | } 176 | } 177 | 178 | /** 179 | * 经典实现较为繁琐 180 | * 181 | * @author mrsimple 182 | * 183 | */ 184 | public class Test { 185 | public static void main(String[] args) { 186 | // 构建器 187 | Builder builder = new ApplePCBuilder(); 188 | // Director 189 | Director pcDirector = new Director(builder); 190 | // 封装构建过程, 4核, 内存2GB, Mac系统 191 | pcDirector.construct(4, 2, "Mac OS X 10.9.1"); 192 | // 构建电脑, 输出相关信息 193 | System.out.println("Computer Info : " + builder.create().toString()); 194 | } 195 | } 196 | ``` 197 | 198 | 通过Builder来构建产品对象, 而Director封装了构建复杂产品对象对象的过程,不对外隐藏构建细节。 199 | 200 | 201 | ## Android源码中的模式实现 202 | 在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。简单示例如下 : 203 | 204 | ```java 205 | //显示基本的AlertDialog 206 | private void showDialog(Context context) { 207 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 208 | builder.setIcon(R.drawable.icon); 209 | builder.setTitle("Title"); 210 | builder.setMessage("Message"); 211 | builder.setPositiveButton("Button1", 212 | new DialogInterface.OnClickListener() { 213 | public void onClick(DialogInterface dialog, int whichButton) { 214 | setTitle("点击了对话框上的Button1"); 215 | } 216 | }); 217 | builder.setNeutralButton("Button2", 218 | new DialogInterface.OnClickListener() { 219 | public void onClick(DialogInterface dialog, int whichButton) { 220 | setTitle("点击了对话框上的Button2"); 221 | } 222 | }); 223 | builder.setNegativeButton("Button3", 224 | new DialogInterface.OnClickListener() { 225 | public void onClick(DialogInterface dialog, int whichButton) { 226 | setTitle("点击了对话框上的Button3"); 227 | } 228 | }); 229 | builder.create().show(); // 构建AlertDialog, 并且显示 230 | } 231 | ``` 232 | 233 | 结果 : 234 | ![result](images/result.png) 235 | 236 | 下面我们看看AlertDialog的相关源码 : 237 | 238 | ```java 239 | // AlertDialog 240 | public class AlertDialog extends Dialog implements DialogInterface { 241 | // Controller, 接受Builder成员变量P中的各个参数 242 | private AlertController mAlert; 243 | 244 | // 构造函数 245 | protected AlertDialog(Context context, int theme) { 246 | this(context, theme, true); 247 | } 248 | 249 | // 4 : 构造AlertDialog 250 | AlertDialog(Context context, int theme, boolean createContextWrapper) { 251 | super(context, resolveDialogTheme(context, theme), createContextWrapper); 252 | mWindow.alwaysReadCloseOnTouchAttr(); 253 | mAlert = new AlertController(getContext(), this, getWindow()); 254 | } 255 | 256 | // 实际上调用的是mAlert的setTitle方法 257 | @Override 258 | public void setTitle(CharSequence title) { 259 | super.setTitle(title); 260 | mAlert.setTitle(title); 261 | } 262 | 263 | // 实际上调用的是mAlert的setCustomTitle方法 264 | public void setCustomTitle(View customTitleView) { 265 | mAlert.setCustomTitle(customTitleView); 266 | } 267 | 268 | public void setMessage(CharSequence message) { 269 | mAlert.setMessage(message); 270 | } 271 | 272 | // AlertDialog其他的代码省略 273 | 274 | // ************ Builder为AlertDialog的内部类 ******************* 275 | public static class Builder { 276 | // 1 : 存储AlertDialog的各个参数, 例如title, message, icon等. 277 | private final AlertController.AlertParams P; 278 | // 属性省略 279 | 280 | /** 281 | * Constructor using a context for this builder and the {@link AlertDialog} it creates. 282 | */ 283 | public Builder(Context context) { 284 | this(context, resolveDialogTheme(context, 0)); 285 | } 286 | 287 | 288 | public Builder(Context context, int theme) { 289 | P = new AlertController.AlertParams(new ContextThemeWrapper( 290 | context, resolveDialogTheme(context, theme))); 291 | mTheme = theme; 292 | } 293 | 294 | // Builder的其他代码省略 ...... 295 | 296 | // 2 : 设置各种参数 297 | public Builder setTitle(CharSequence title) { 298 | P.mTitle = title; 299 | return this; 300 | } 301 | 302 | 303 | public Builder setMessage(CharSequence message) { 304 | P.mMessage = message; 305 | return this; 306 | } 307 | 308 | public Builder setIcon(int iconId) { 309 | P.mIconId = iconId; 310 | return this; 311 | } 312 | 313 | public Builder setPositiveButton(CharSequence text, final OnClickListener listener) { 314 | P.mPositiveButtonText = text; 315 | P.mPositiveButtonListener = listener; 316 | return this; 317 | } 318 | 319 | 320 | public Builder setView(View view) { 321 | P.mView = view; 322 | P.mViewSpacingSpecified = false; 323 | return this; 324 | } 325 | 326 | // 3 : 构建AlertDialog, 传递参数 327 | public AlertDialog create() { 328 | // 调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog 329 | final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false); 330 | // 5 : 将P中的参数应用的dialog中的mAlert对象中 331 | P.apply(dialog.mAlert); 332 | dialog.setCancelable(P.mCancelable); 333 | if (P.mCancelable) { 334 | dialog.setCanceledOnTouchOutside(true); 335 | } 336 | dialog.setOnCancelListener(P.mOnCancelListener); 337 | if (P.mOnKeyListener != null) { 338 | dialog.setOnKeyListener(P.mOnKeyListener); 339 | } 340 | return dialog; 341 | } 342 | } 343 | 344 | } 345 | ``` 346 | 可以看到,通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。我们看看apply函数的实现 : 347 | 348 | ```java 349 | public void apply(AlertController dialog) { 350 | if (mCustomTitleView != null) { 351 | dialog.setCustomTitle(mCustomTitleView); 352 | } else { 353 | if (mTitle != null) { 354 | dialog.setTitle(mTitle); 355 | } 356 | if (mIcon != null) { 357 | dialog.setIcon(mIcon); 358 | } 359 | if (mIconId >= 0) { 360 | dialog.setIcon(mIconId); 361 | } 362 | if (mIconAttrId > 0) { 363 | dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); 364 | } 365 | } 366 | if (mMessage != null) { 367 | dialog.setMessage(mMessage); 368 | } 369 | if (mPositiveButtonText != null) { 370 | dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText, 371 | mPositiveButtonListener, null); 372 | } 373 | if (mNegativeButtonText != null) { 374 | dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText, 375 | mNegativeButtonListener, null); 376 | } 377 | if (mNeutralButtonText != null) { 378 | dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText, 379 | mNeutralButtonListener, null); 380 | } 381 | if (mForceInverseBackground) { 382 | dialog.setInverseBackgroundForced(true); 383 | } 384 | // For a list, the client can either supply an array of items or an 385 | // adapter or a cursor 386 | if ((mItems != null) || (mCursor != null) || (mAdapter != null)) { 387 | createListView(dialog); 388 | } 389 | if (mView != null) { 390 | if (mViewSpacingSpecified) { 391 | dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, 392 | mViewSpacingBottom); 393 | } else { 394 | dialog.setView(mView); 395 | } 396 | } 397 | } 398 | ``` 399 | 实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。在这里,Builder同时扮演了上文中提到的builder、ConcreteBuilder、Director的角色,简化了Builder模式的设计。 400 | 401 | 402 | 403 | ## 4. 杂谈 404 | ### 优点与缺点 405 | #### 优点 406 | * 良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节; 407 | * 建造者独立,容易扩展; 408 | * 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。 409 | 410 | #### 缺点 411 | * 会产生多余的Builder对象以及Director对象,消耗内存; 412 | * 对象的构建过程暴露。 413 | -------------------------------------------------------------------------------- /chain-of-responsibility/AigeStudio/images/chain-of-responsibility.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/chain-of-responsibility/AigeStudio/images/chain-of-responsibility.jpg -------------------------------------------------------------------------------- /chain-of-responsibility/AigeStudio/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之责任链模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中责任链模式分析 4 | > Android系统版本: 4.4.4 5 | > 分析者:[Aige](https://github.com/AigeStudio),分析状态:完成,校对者:[SM哥](https://github.com/bboyfeiyu),校对状态:撒丫校对中 6 | 7 | ## 1. 模式介绍 8 | 9 | ### 模式的定义 10 | 一个请求沿着一条“链”传递,直到该“链”上的某个处理者处理它为止。 11 | 12 | 13 | ### 模式的使用场景 14 | 一个请求可以被多个处理者处理或处理者未明确指定时。 15 | 16 | 17 | ## 2. UML类图 18 | ![UML](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis/blob/master/chain-of-responsibility/AigeStudio/images/chain-of-responsibility.jpg?raw=true) 19 | 20 | ### 角色介绍 21 | Client:客户端 22 | 23 | Handler:抽象处理者 24 | 25 | ConcreteHandler:具体处理者 26 | 27 | 28 | 29 | 30 | ## 3. 模式的简单实现 31 | ### 简单实现的介绍 32 | 责任链模式非常简单异常好理解,相信我它比单例模式还简单易懂,其应用也几乎无所不在,甚至可以这么说……从你敲代码的第一天起你就不知不觉用过了它最原始的裸体结构:分支语句: 33 | ```java 34 | public class SimpleResponsibility { 35 | public static void main(String[] args) { 36 | int request = (int) (Math.random() * 3); 37 | switch (request) { 38 | case 0: 39 | System.out.println("SMBother handle it: " + request); 40 | break; 41 | case 1: 42 | System.out.println("Aige handle it: " + request); 43 | break; 44 | case 2: 45 | System.out.println("7Bother handle it: " + request); 46 | break; 47 | default: 48 | break; 49 | } 50 | } 51 | } 52 | ``` 53 | 谁敢说没用过上面这种结构体的站出来我保证不打屎他,没用过swith至少if-else用过吧,if-else都没用过你怎么知道github的……上面的这段代码其实就是一种最最简单的责任链模式,其根据request的值进行不同的处理。当然这只是个不恰当的例子来让大家尽快对责任链模式有个简单的理解,因为可能很多童鞋第一次听说这个模式,而人对未知事物总是恐惧的,为了消除大家的这种恐惧,我将大家最常见的code搬出来相信熟悉的代码对大家来说有一种亲切的感觉,当然我们实际应用中的责任链模式绝逼不是这么Mr.Simple,但是也不会复杂不到哪去。责任链模式,顾名思义,必定与责任Responsibility相关,其实质呢就像上面定义中说的那样一个请求(比如上面代码中的request值)沿着一条“链”(比如上面代码中我们的switch分支语句)传递,当某个处于“链”上的处理者(case定义的条件)处理它时完成处理。其实现实生活中关于责任者模式的例子数不胜数,最常见的就是工作中上下级之间的责任请求关系了。比如: 54 | >程序猿狗屎运被派出去异国出差一周,这时候就要去申请一定的差旅费了,你心里小算一笔加上各种车马费估计大概要个两三万,于是先向小组长汇报申请,可是大于一千块小组长没权利批复,于是只好去找项目主管,项目主管一看妈蛋这么狠要这么多我只能批小于五千块的,于是你只能再跑去找部门经理,部门经理看了下一阵淫笑后说没法批我只能批小于一万的,于是你只能狗血地去跪求老总,老总一看哟!小伙子心忒黑啊!老总话虽如此但还是把钱批给你了毕竟是给公司办事,到此申请处理完毕,你也可以屁颠屁颠地滚了。 55 | 56 | 如果把上面的场景应用到责任链模式,那么我们的request请求就是申请经费,组长主管经理老总们就是一个个具体的责任人他们可以对请求做出处理但是他们只能在自己的责任范围内处理该处理的请求,而程序猿只是个底层狗请求者向责任人们发起请求…………苦逼的猿。 57 | 58 | ### 实现源码 59 | 上面的场景我们可以使用使用如下的代码来模拟实现: 60 | 61 | 首先定义一个程序员类: 62 | ```Java 63 | /** 64 | * 程序猿类 65 | * 66 | * @author Aige{@link https://github.com/AigeStudio} 67 | * 68 | */ 69 | public class ProgramApe { 70 | private int expenses;// 声明整型成员变量表示出差费用 71 | private String apply = "爹要点钱出差";// 声明字符串型成员变量表示差旅申请 72 | 73 | /* 74 | * 含参构造方法 75 | */ 76 | public ProgramApe(int expenses) { 77 | this.expenses = expenses; 78 | } 79 | 80 | /* 81 | * 获取程序员具体的差旅费用 82 | */ 83 | public int getExpenses() { 84 | return expenses; 85 | } 86 | 87 | /* 88 | * 获取差旅费申请 89 | */ 90 | public String getApply() { 91 | return apply; 92 | } 93 | } 94 | ``` 95 | 96 | 然后依次是各个大爷类: 97 | 98 | ```Java 99 | /** 100 | * 小组长类 101 | * 102 | * @author Aige{@link https://github.com/AigeStudio} 103 | * 104 | */ 105 | public class GroupLeader { 106 | 107 | /** 108 | * 处理请求 109 | * 110 | * @param ape 111 | * 具体的猿 112 | */ 113 | public void handleRequest(ProgramApe ape) { 114 | System.out.println(ape.getApply()); 115 | System.out.println("GroupLeader: Of course Yes!"); 116 | } 117 | } 118 | ``` 119 | 120 | ```Java 121 | /** 122 | * 项目主管类 123 | * 124 | * @author Aige{@link https://github.com/AigeStudio} 125 | * 126 | */ 127 | public class Director { 128 | /** 129 | * 处理请求 130 | * 131 | * @param ape 132 | * 具体的猿 133 | */ 134 | public void handleRequest(ProgramApe ape) { 135 | System.out.println(ape.getApply()); 136 | System.out.println("Director: Of course Yes!"); 137 | } 138 | } 139 | ``` 140 | 141 | ```Java 142 | /** 143 | * 部门经理类 144 | * 145 | * @author Aige{@link https://github.com/AigeStudio} 146 | * 147 | */ 148 | public class Manager { 149 | /** 150 | * 处理请求 151 | * 152 | * @param ape 153 | * 具体的猿 154 | */ 155 | public void handleRequest(ProgramApe ape) { 156 | System.out.println(ape.getApply()); 157 | System.out.println("Manager: Of course Yes!"); 158 | } 159 | } 160 | ``` 161 | 162 | ```Java 163 | /** 164 | * 老总类 165 | * 166 | * @author Aige{@link https://github.com/AigeStudio} 167 | * 168 | */ 169 | public class Boss { 170 | /** 171 | * 处理请求 172 | * 173 | * @param ape 174 | * 具体的猿 175 | */ 176 | public void handleRequest(ProgramApe ape) { 177 | System.out.println(ape.getApply()); 178 | System.out.println("Boss: Of course Yes!"); 179 | } 180 | } 181 | ``` 182 | 183 | 好了,万事俱备只欠场景,现在我们模拟一下整个场景过程: 184 | 185 | ```Java 186 | /** 187 | * 场景模拟类 188 | * 189 | * @author Aige{@link https://github.com/AigeStudio} 190 | * 191 | */ 192 | public class Client { 193 | public static void main(String[] args) { 194 | /* 195 | * 先来一个程序猿 这里给他一个三万以内的随机值表示需要申请的差旅费 196 | */ 197 | ProgramApe ape = new ProgramApe((int) (Math.random() * 30000)); 198 | 199 | /* 200 | * 再来四个老大 201 | */ 202 | GroupLeader leader = new GroupLeader(); 203 | Director director = new Director(); 204 | Manager manager = new Manager(); 205 | Boss boss = new Boss(); 206 | 207 | /* 208 | * 处理申请 209 | */ 210 | if (ape.getExpenses() <= 1000) { 211 | leader.handleRequest(ape); 212 | } else if (ape.getExpenses() <= 5000) { 213 | director.handleRequest(ape); 214 | } else if (ape.getExpenses() <= 10000) { 215 | manager.handleRequest(ape); 216 | } else { 217 | boss.handleRequest(ape); 218 | } 219 | } 220 | } 221 | ``` 222 | 223 | 运行一下,我的结果输出如下(注:由于随机值的原因你的结果也许与我不一样): 224 | 225 | >爹要点钱出差 226 | > 227 | >Manager: Of course Yes! 228 | 229 | 是不是感觉有点懂了?当然上面的代码虽然在一定程度上体现了责任链模式的思想,但是确是非常terrible的。作为一个code新手可以原谅,但是对有一定经验的code+来说就不可饶恕了,很明显所有的老大都有共同的handleRequest方法而程序猿也有不同类型的,比如一个公司的php、c/c++、Android、IOS等等,所有的这些共性我们都可以将其抽象为一个抽象类或接口,比如我们的程序猿抽象父类: 230 | 231 | ```java 232 | /** 233 | * 程序猿抽象接口 234 | * 235 | * @author Aige{@link https://github.com/AigeStudio} 236 | * 237 | */ 238 | public abstract class ProgramApes { 239 | /** 240 | * 获取程序员具体的差旅费用 241 | * 242 | * @return 要多少钱 243 | */ 244 | public abstract int getExpenses(); 245 | 246 | /** 247 | * 获取差旅费申请 248 | * 249 | * @return Just a request 250 | */ 251 | public abstract String getApply(); 252 | } 253 | ``` 254 | 255 | 这时我们就可以实现该接口使用呆毛具现化一个具体的程序猿,比如Android猿: 256 | 257 | ```java 258 | /** 259 | * Android程序猿类 260 | * 261 | * @author Aige{@link https://github.com/AigeStudio} 262 | * 263 | */ 264 | public class AndroidApe extends ProgramApes { 265 | private int expenses;// 声明整型成员变量表示出差费用 266 | private String apply = "爹要点钱出差";// 声明字符串型成员变量表示差旅申请 267 | 268 | /* 269 | * 含参构造方法 270 | */ 271 | public AndroidApe(int expenses) { 272 | this.expenses = expenses; 273 | } 274 | 275 | @Override 276 | public int getExpenses() { 277 | return expenses; 278 | } 279 | 280 | @Override 281 | public String getApply() { 282 | return apply; 283 | } 284 | } 285 | ``` 286 | 同样的,所有的老大都有一个批复经费申请的权利,我们把这个权利抽象为一个IPower接口: 287 | 288 | ```java 289 | /** 290 | * 老大们的权利接口 291 | * 292 | * @author Aige{@link https://github.com/AigeStudio} 293 | * 294 | */ 295 | public interface IPower { 296 | /** 297 | * 处理请求 298 | * 299 | * @param ape 300 | * 具体的猿 301 | */ 302 | public void handleRequest(ProgramApe ape); 303 | } 304 | ``` 305 | 306 | 然后让所有的老大们实现该接口即可其它不变,而场景类Client中也只是修改各个老大的引用类型为IPower而已,具体代码就不贴了,运行效果也类似。 307 | 308 | 然而上面的代码依然问题重重,为什么呢?大家想想,当程序猿发出一个申请时却是在场景类中做出判断决定的……然而这个职责事实上应该由老大们来承担并作出决定,上面的代码搞反了……既然知道了错误,那么我们就来再次重构一下代码: 309 | 310 | 把所有老大抽象为一个leader抽象类,在该抽象类中实现处理逻辑: 311 | 312 | ```java 313 | /** 314 | * 领导人抽象类 315 | * 316 | * @author Aige{@link https://github.com/AigeStudio} 317 | * 318 | */ 319 | public abstract class Leader { 320 | private int expenses;// 当前领导能批复的金额 321 | private Leader mSuperiorLeader;// 上级领导 322 | 323 | /** 324 | * 含参构造方法 325 | * 326 | * @param expenses 327 | * 当前领导能批复的金额 328 | */ 329 | public Leader(int expenses) { 330 | this.expenses = expenses; 331 | } 332 | 333 | /** 334 | * 回应程序猿 335 | * 336 | * @param ape 337 | * 具体的程序猿 338 | */ 339 | protected abstract void reply(ProgramApe ape); 340 | 341 | /** 342 | * 处理请求 343 | * 344 | * @param ape 345 | * 具体的程序猿 346 | */ 347 | public void handleRequest(ProgramApe ape) { 348 | /* 349 | * 如果说程序猿申请的money在当前领导的批复范围内 350 | */ 351 | if (ape.getExpenses() <= expenses) { 352 | // 那么就由当前领导批复即可 353 | reply(ape); 354 | } else { 355 | /* 356 | * 否则看看当前领导有木有上级 357 | */ 358 | if (null != mSuperiorLeader) { 359 | // 有的话简单撒直接扔给上级处理即可 360 | mSuperiorLeader.handleRequest(ape); 361 | } else { 362 | // 没有上级的话就批复不了老……不过在这个场景中总会有领导批复的淡定 363 | System.out.println("Goodbye my money......"); 364 | } 365 | } 366 | } 367 | 368 | /** 369 | * 为当前领导设置一个上级领导 370 | * 371 | * @param superiorLeader 372 | * 上级领导 373 | */ 374 | public void setLeader(Leader superiorLeader) { 375 | this.mSuperiorLeader = superiorLeader; 376 | } 377 | } 378 | ``` 379 | 380 | 这么一来,我们的领导老大们就有了实实在在的权利职责去处理底层苦逼程序猿的请求。OK,接下来要做的事就是让所有的领导继承该类: 381 | 382 | ```Java 383 | /** 384 | * 小组长类 385 | * 386 | * @author Aige{@link https://github.com/AigeStudio} 387 | * 388 | */ 389 | public class GroupLeader extends Leader { 390 | 391 | public GroupLeader() { 392 | super(1000); 393 | } 394 | 395 | @Override 396 | protected void reply(ProgramApe ape) { 397 | System.out.println(ape.getApply()); 398 | System.out.println("GroupLeader: Of course Yes!"); 399 | } 400 | } 401 | ``` 402 | 403 | ```java 404 | /** 405 | * 项目主管类 406 | * 407 | * @author Aige{@link https://github.com/AigeStudio} 408 | * 409 | */ 410 | public class Director extends Leader{ 411 | public Director() { 412 | super(5000); 413 | } 414 | 415 | @Override 416 | protected void reply(ProgramApe ape) { 417 | System.out.println(ape.getApply()); 418 | System.out.println("Director: Of course Yes!"); 419 | } 420 | } 421 | ``` 422 | 423 | ```java 424 | /** 425 | * 部门经理类 426 | * 427 | * @author Aige{@link https://github.com/AigeStudio} 428 | * 429 | */ 430 | public class Manager extends Leader { 431 | public Manager() { 432 | super(10000); 433 | } 434 | 435 | @Override 436 | protected void reply(ProgramApe ape) { 437 | System.out.println(ape.getApply()); 438 | System.out.println("Manager: Of course Yes!"); 439 | } 440 | } 441 | ``` 442 | 443 | ```java 444 | /** 445 | * 老总类 446 | * 447 | * @author Aige{@link https://github.com/AigeStudio} 448 | * 449 | */ 450 | public class Boss extends Leader { 451 | public Boss() { 452 | super(40000); 453 | } 454 | 455 | @Override 456 | protected void reply(ProgramApe ape) { 457 | System.out.println(ape.getApply()); 458 | System.out.println("Boss: Of course Yes!"); 459 | } 460 | } 461 | ``` 462 | 463 | 最后,更新我们的场景类,将其从责任人的角色中解放出来: 464 | 465 | ```java 466 | /** 467 | * 场景模拟类 468 | * 469 | * @author Aige{@link https://github.com/AigeStudio} 470 | * 471 | */ 472 | public class Client { 473 | public static void main(String[] args) { 474 | /* 475 | * 先来一个程序猿 这里给他一个三万以内的随机值表示需要申请的差旅费 476 | */ 477 | ProgramApe ape = new ProgramApe((int) (Math.random() * 30000)); 478 | 479 | /* 480 | * 再来四个老大 481 | */ 482 | Leader leader = new GroupLeader(); 483 | Leader director = new Director(); 484 | Leader manager = new Manager(); 485 | Leader boss = new Boss(); 486 | 487 | /* 488 | * 设置老大的上一个老大 489 | */ 490 | leader.setLeader(director); 491 | director.setLeader(manager); 492 | manager.setLeader(boss); 493 | 494 | // 处理申请 495 | leader.handleRequest(ape); 496 | } 497 | } 498 | ``` 499 | 500 | 运行三次,下面是三次运行的结果(注:由于随机值的原因你的结果也许与我不一样): 501 | 502 | >爹要点钱出差 503 | > 504 | >Boss: Of course Yes! 505 | *** 506 | >爹要点钱出差 507 | > 508 | >Director: Of course Yes! 509 | *** 510 | >爹要点钱出差 511 | > 512 | >Boss: Of course Yes! 513 | 514 | ### 总结 515 | 516 | OK,这样我们就将请求和处理分离开来,对于程序猿来说,不需要知道是谁给他批复的钱,而对于领导们来说,也不需要确切地知道是批给哪个程序猿,只要根据自己的责任做出处理即可,由此将两者优雅地解耦。 517 | 518 | ## Android源码中的模式实现 519 | Android中关于责任链模式比较明显的体现就是在事件分发过程中对事件的投递,其实严格来说,事件投递的模式并不是严格的责任链模式,但是其是责任链模式的一种变种体现,在ViewGroup中对事件处理者的查找方式如下: 520 | 521 | ```java 522 | @Override 523 | public boolean dispatchTouchEvent(MotionEvent ev) { 524 | // 省略两行代码………… 525 | 526 | boolean handled = false; 527 | if (onFilterTouchEventForSecurity(ev)) { 528 | 529 | // 省略N行代码………… 530 | 531 | /* 532 | * 如果事件未被取消并未被拦截 533 | */ 534 | if (!canceled && !intercepted) { 535 | /* 536 | * 如果事件为起始事件 537 | */ 538 | if (actionMasked == MotionEvent.ACTION_DOWN 539 | || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) 540 | || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { 541 | 542 | // 省掉部分逻辑………… 543 | 544 | final int childrenCount = mChildrenCount; 545 | 546 | /* 547 | * 如果TouchTarget为空并且子元素不为0 548 | */ 549 | if (newTouchTarget == null && childrenCount != 0) { 550 | final float x = ev.getX(actionIndex); 551 | final float y = ev.getY(actionIndex); 552 | 553 | final View[] children = mChildren; 554 | 555 | final boolean customOrder = isChildrenDrawingOrderEnabled(); 556 | 557 | /* 558 | * 遍历子元素 559 | */ 560 | for (int i = childrenCount - 1; i >= 0; i--) { 561 | final int childIndex = customOrder ? 562 | getChildDrawingOrder(childrenCount, i) : i; 563 | final View child = children[childIndex]; 564 | 565 | /* 566 | * 如果这个子元素无法接收Pointer Event或这个事件点压根就没有落在子元素的边界范围内 567 | */ 568 | if (!canViewReceivePointerEvents(child) 569 | || !isTransformedTouchPointInView(x, y, child, null)) { 570 | // 那么就跳出该次循环继续遍历 571 | continue; 572 | } 573 | 574 | // 找到Event该由哪个子元素持有 575 | newTouchTarget = getTouchTarget(child); 576 | 577 | 578 | if (newTouchTarget != null) { 579 | newTouchTarget.pointerIdBits |= idBitsToAssign; 580 | break; 581 | } 582 | 583 | resetCancelNextUpFlag(child); 584 | 585 | /* 586 | * 投递事件执行触摸操作 587 | * 如果子元素还是一个ViewGroup则递归调用重复此过程 588 | * 如果子元素是一个View那么则会调用View的dispatchTouchEvent并最终由onTouchEvent处理 589 | */ 590 | if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { 591 | mLastTouchDownTime = ev.getDownTime(); 592 | mLastTouchDownIndex = childIndex; 593 | mLastTouchDownX = ev.getX(); 594 | mLastTouchDownY = ev.getY(); 595 | newTouchTarget = addTouchTarget(child, idBitsToAssign); 596 | alreadyDispatchedToNewTouchTarget = true; 597 | break; 598 | } 599 | } 600 | } 601 | 602 | /* 603 | * 如果发现没有子元素可以持有该次事件 604 | */ 605 | if (newTouchTarget == null && mFirstTouchTarget != null) { 606 | newTouchTarget = mFirstTouchTarget; 607 | while (newTouchTarget.next != null) { 608 | newTouchTarget = newTouchTarget.next; 609 | } 610 | newTouchTarget.pointerIdBits |= idBitsToAssign; 611 | } 612 | } 613 | } 614 | 615 | // 省去不必要代码…… 616 | } 617 | 618 | // 省去一行代码…… 619 | 620 | return handled; 621 | } 622 | ``` 623 | 624 | 再来看看dispatchTransformedTouchEvent方法是如何调度子元素dispatchTouchEvent方法的: 625 | 626 | ```java 627 | private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, 628 | View child, int desiredPointerIdBits) { 629 | final boolean handled; 630 | 631 | final int oldAction = event.getAction(); 632 | 633 | /* 634 | * 如果事件被取消 635 | */ 636 | if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { 637 | event.setAction(MotionEvent.ACTION_CANCEL); 638 | 639 | /* 640 | * 如果没有子元素 641 | */ 642 | if (child == null) { 643 | // 那么就直接调用父类的dispatchTouchEvent注意这里的父类终会为View类 644 | handled = super.dispatchTouchEvent(event); 645 | } else { 646 | // 如果有子元素则传递cancle事件 647 | handled = child.dispatchTouchEvent(event); 648 | } 649 | event.setAction(oldAction); 650 | return handled; 651 | } 652 | 653 | /* 654 | * 计算即将被传递的点的数量 655 | */ 656 | final int oldPointerIdBits = event.getPointerIdBits(); 657 | final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; 658 | 659 | /* 660 | * 如果事件木有相应的点那么就丢弃该次事件 661 | */ 662 | if (newPointerIdBits == 0) { 663 | return false; 664 | } 665 | 666 | // 声明临时变量保存坐标转换后的MotionEvent 667 | final MotionEvent transformedEvent; 668 | 669 | /* 670 | * 如果事件点的数量一致 671 | */ 672 | if (newPointerIdBits == oldPointerIdBits) { 673 | /* 674 | * 子元素为空或子元素有一个单位矩阵 675 | */ 676 | if (child == null || child.hasIdentityMatrix()) { 677 | /* 678 | * 再次区分子元素为空的情况 679 | */ 680 | if (child == null) { 681 | // 为空则调用父类dispatchTouchEvent 682 | handled = super.dispatchTouchEvent(event); 683 | } else { 684 | // 否则尝试获取xy方向上的偏移量(如果通过scrollTo或scrollBy对子视图进行滚动的话) 685 | final float offsetX = mScrollX - child.mLeft; 686 | final float offsetY = mScrollY - child.mTop; 687 | 688 | // 将MotionEvent进行坐标变换 689 | event.offsetLocation(offsetX, offsetY); 690 | 691 | // 再将变换后的MotionEvent传递给子元素 692 | handled = child.dispatchTouchEvent(event); 693 | 694 | // 复位MotionEvent以便之后再次使用 695 | event.offsetLocation(-offsetX, -offsetY); 696 | } 697 | 698 | // 如果通过以上的逻辑判断当前事件被持有则可以直接返回 699 | return handled; 700 | } 701 | transformedEvent = MotionEvent.obtain(event); 702 | } else { 703 | transformedEvent = event.split(newPointerIdBits); 704 | } 705 | 706 | /* 707 | * 下述雷同不再累赘 708 | */ 709 | if (child == null) { 710 | handled = super.dispatchTouchEvent(transformedEvent); 711 | } else { 712 | final float offsetX = mScrollX - child.mLeft; 713 | final float offsetY = mScrollY - child.mTop; 714 | transformedEvent.offsetLocation(offsetX, offsetY); 715 | if (! child.hasIdentityMatrix()) { 716 | transformedEvent.transform(child.getInverseMatrix()); 717 | } 718 | 719 | handled = child.dispatchTouchEvent(transformedEvent); 720 | } 721 | 722 | transformedEvent.recycle(); 723 | return handled; 724 | } 725 | ``` 726 | 727 | ViewGroup事件投递的递归调用就类似于一条责任链,一旦其寻找到责任者,那么将由责任者持有并消费掉该次事件,具体的体现在View的onTouchEvent方法中返回值的设置(这里介于篇幅就不具体介绍ViewGroup对事件的处理了),如果onTouchEvent返回false那么意味着当前View不会是该次事件的责任人将不会对其持有,如果为true则相反,此时View会持有该事件并不再向外传递。 728 | 729 | ## 4. 杂谈 730 | 世界不是完美的,所以不会有完美的事物存在。就像所有的设计模式一样, 有优点优缺点,但是总的来说优点必定大于缺点或者说缺点相对于优点来说更可控。责任链模式也一样,有点显而易见,可以对请求者和处理者关系的解耦提高代码的灵活性,比如上面我们的例子中如果在主管和经理之间多了一个总监,那么总监可以批复小于7500的经费,这时候根据我们上面重构的模式,仅需新建一个总监类继承Leader即可其它所有的存在类都可保持不变。责任链模式的最大缺点是对链中责任人的遍历,如果责任人太多那么遍历必定会影响性能,特别是在一些递归调用中,要慎重。 731 | 732 | -------------------------------------------------------------------------------- /chain-of-responsibility/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [AigeStudio](https://github.com/AigeStudio) | 2015.3.15 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /command/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/command/.DS_Store -------------------------------------------------------------------------------- /command/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 模式名称 | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| ------------- | 4 | | 命令模式 | [lijunhuayc](https://github.com/lijunhuayc) | 2015.3.18 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /command/lijunhuayc/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/command/lijunhuayc/.DS_Store -------------------------------------------------------------------------------- /command/lijunhuayc/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/command/lijunhuayc/images/.DS_Store -------------------------------------------------------------------------------- /command/lijunhuayc/images/lijunhuayc_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/command/lijunhuayc/images/lijunhuayc_result.png -------------------------------------------------------------------------------- /command/lijunhuayc/images/lijunhuayc_uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/command/lijunhuayc/images/lijunhuayc_uml.png -------------------------------------------------------------------------------- /command/lijunhuayc/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之命令模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 命令模式 分析 4 | > Android系统版本: 2.3 5 | > 分析者:[lijunhuayc](https://github.com/lijunhuayc),分析状态:完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:未开始 6 | 7 | ## 1. 模式介绍 8 | 9 | ### 模式的定义 10 | 将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。 11 | 12 | ### 模式的使用场景 13 | 1. 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。 14 | 2. 系统需要在不同的时间指定请求、将请求排队和执行请求。 15 | 3. 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。 16 | 4. 系统需要将一组操作组合在一起,即支持宏命令。 17 | 18 | ## 2. UML类图 19 | ![UML类图](images/lijunhuayc_uml.png) 20 | 21 | 22 | ### 角色介绍 23 | * 命令角色(Command):定义命令的接口,声明具体命令类需要执行的方法。这是一个抽象角色。 24 | 25 | * 具体命令角色(ConcreteCommand):命令接口的具体实现对象,通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。 26 | 27 | * 调用者角色(Invoker):负责调用命令对象执行请求,通常会持有命令对象(可以持有多个命令对象)。Invoker是Client真正触发命令并要求命令执行相应操作的地方(使用命令对象的入口)。 28 | 29 | * 接受者角色(Receiver):Receiver是真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。 30 | 31 | * 客户角色(Client):Client可以创建具体的命令对象,并且设置命令对象的接收者。Tips:不能把Clinet理解为我们平常说的客户端,这里的Client是一个组装命令对象和接受者对象的角色,或者你把它理解为一个装配者。 32 | 33 | ## 3. 模式的简单实现 34 | ### 简单实现的介绍 35 | 命令模式其实就是对命令进行封装,将命令请求者和命令执行者的责任分离开来实现松耦合。 36 | 这里我们通过一个简单的实例来剖析一下命令模式:命令接收者ReceiverRole拥有一个PeopleBean类型成员,通过Invoker发出不同的命令来修改PeopleBean的相对应的属性,具体命令实现类ConcreteCommandImpl1执行修改年龄命令,ConcreteCommandImpl2执行修改姓名的命令等等,ClientRole负责组装各个部分。 37 | 例子代码如下(resource目录下也可以查看)。 38 | 39 | ### 实现源码 40 | 41 | ```java 42 | package com.command; 43 | /** 44 | * 命令接口 [命令角色] 45 | */ 46 | public interface Command { 47 | public void execute(); 48 | public void undo(); 49 | public void redo(); 50 | } 51 | 52 | ``` 53 | 54 | ConcreteCommandImpl1.java类. 55 | 56 | 57 | ```java 58 | 59 | package com.command; 60 | /** 61 | * 更新年龄的命令类 [ 具体命令角色 ] 62 | */ 63 | public class ConcreteCommandImpl1 implements Command{ 64 | private ReceiverRole receiverRole1; 65 | 66 | public ConcreteCommandImpl1(ReceiverRole receiverRole1) { 67 | this.receiverRole1 = receiverRole1; 68 | } 69 | 70 | @Override 71 | public void execute() { 72 | /* 73 | * 可以加入命令排队等等,未执行的命令支持redo操作 74 | */ 75 | receiverRole1.opActionUpdateAge(1001);//执行具体的命令操作 76 | } 77 | 78 | @Override 79 | public void undo() { 80 | receiverRole1.rollBackAge();//执行具体的撤销回滚操作 81 | } 82 | 83 | @Override 84 | public void redo() { 85 | //在命令执行前可以修改命令的执行 86 | } 87 | } 88 | ``` 89 | 90 | ConcreteCommandImpl2.java类. 91 | 92 | ```java 93 | package com.command; 94 | /** 95 | * 更新姓名的命令类[具体命令角色] 96 | */ 97 | public class ConcreteCommandImpl2 implements Command{ 98 | private ReceiverRole receiverRole1; 99 | 100 | public ConcreteCommandImpl2(ReceiverRole receiverRole1) { 101 | this.receiverRole1 = receiverRole1; 102 | } 103 | 104 | @Override 105 | public void execute() { 106 | /* 107 | * 可以加入命令排队等等,未执行的命令支持redo操作 108 | */ 109 | receiverRole1.opActionUpdateName("lijunhuayc");//执行具体的命令操作 110 | } 111 | 112 | @Override 113 | public void undo() { 114 | receiverRole1.rollBackName();//执行具体的撤销回滚操作 115 | } 116 | 117 | @Override 118 | public void redo() { 119 | //在命令执行前可以修改命令的执行 120 | } 121 | 122 | } 123 | ``` 124 | 125 | InvokerRole.java. 126 | 127 | ```java 128 | package com.command; 129 | /** 130 | * 命令调用[调用者角色] 131 | */ 132 | public class InvokerRole { 133 | private Command command1; 134 | private Command command2; 135 | //持有多个命令对象[实际的情况也可能是一个命令对象的集合来保存命令对象] 136 | 137 | public void setCommand1(Command command1) { 138 | this.command1 = command1; 139 | } 140 | public void setCommand2(Command command2) { 141 | this.command2 = command2; 142 | } 143 | 144 | /** 145 | * 执行正常命令,1执行回滚命令 146 | */ 147 | public void invoke(int args) { 148 | //可以根据具体情况选择执行某些命令 149 | if(args == 0){ 150 | command1.execute(); 151 | command2.execute(); 152 | }else if(args == 1){ 153 | command1.undo(); 154 | command2.undo(); 155 | } 156 | } 157 | 158 | } 159 | ``` 160 | 161 | ReceiverRole.java. 162 | 163 | ```java 164 | package com.command; 165 | /** 166 | * 命令的具体执行类[接收者角色], 命令接收者可以是任意的类,只要实现了命令要求实现的相应功能即可。 167 | */ 168 | public class ReceiverRole { 169 | private PeopleBean people; 170 | //具体命令操作的缓存栈,用于回滚。这里为了方便就用一个PeopleBean来代替 171 | private PeopleBean peopleCache = new PeopleBean(); public ReceiverRole() { 172 | this.people = new PeopleBean(-1, "NULL");//初始化年龄为-1,姓名为NULL 173 | } 174 | 175 | public ReceiverRole(PeopleBean people) { 176 | this.people = people; 177 | } 178 | 179 | /** 180 | * 具体操作方法[修改年龄和姓名] 181 | */ 182 | public void opActionUpdateAge(int age) { 183 | System.out.println("执行命令前:"+people.toString()); 184 | this.people.update(age); 185 | System.out.println("执行命令后:"+people.toString()+"\n"); 186 | } 187 | 188 | //修改姓名 189 | public void opActionUpdateName(String name) { 190 | System.out.println("执行命令前:"+people.toString()); 191 | this.people.update(name); 192 | System.out.println("执行命令后:"+people.toString()+"\n"); 193 | } 194 | 195 | /** 196 | * 回滚操作,用于撤销opAction执行的改变 197 | */ 198 | public void rollBackAge() { 199 | people.setAge(peopleCache.getAge()); 200 | System.out.println("命令回滚后:"+people.toString()+"\n"); 201 | } 202 | public void rollBackName() { 203 | people.setName(peopleCache.getName()); 204 | System.out.println("命令回滚后:"+people.toString()+"\n"); 205 | } 206 | } 207 | ``` 208 | 209 | PeopleBean.java 210 | 211 | ```java 212 | package com.command; 213 | /** 214 | * @Desc: 辅助类,作为接收者Receiver的成员,包含两个属性,用来观察命令的执行情况 215 | * @author ljh 216 | * @date 2015-3-16 上午11:29:11 217 | */ 218 | public class PeopleBean { 219 | private int age = -1; //年龄 220 | private String name = "NULL"; //姓名 221 | public PeopleBean() { 222 | } 223 | public PeopleBean(int age, String name) { 224 | this.age = age; 225 | this.name = name; 226 | } 227 | public void update(int age, String name) { 228 | this.age = age; 229 | this.name = name; 230 | } 231 | public void update(int age) { 232 | this.age = age; 233 | } 234 | public void update(String name) { 235 | this.name = name; 236 | } 237 | /** 238 | * @return 返回一个PeopleBean的克隆对象 239 | */ 240 | protected PeopleBean clone(){ 241 | return new PeopleBean(age, name); 242 | } 243 | @Override 244 | public String toString() { 245 | return " 【年龄:" + age + "\t姓名:" + name + "】"; 246 | } 247 | // setter and getter 248 | 249 | } 250 | ``` 251 | 252 | ClientRole.java 253 | 254 | ```java 255 | package com.command; 256 | /** 257 | * 命令对象和接受者对象的组装类[客户角色]. 258 | * 我这把类名定义成ClientRole更方便读者理解这只是命令模式中的一个客户角色,而不是我们常规意义上说的客户端 259 | */ 260 | public class ClientRole { 261 | /** 262 | * 组装操作 263 | */ 264 | public void assembleAction() { 265 | //创建一个命令接收者 266 | ReceiverRole receiverRole1 = new ReceiverRole(); //创建一个命令的具体实现对象,并指定命令接收者 267 | Command command1 = new ConcreteCommandImpl1(receiverRole1); Command command2 = new ConcreteCommandImpl2(receiverRole1); 268 | 269 | InvokerRole invokerRole = new InvokerRole();//创建一个命令调用者 270 | invokerRole.setCommand1(command1);//为调用者指定命令对象1 271 | invokerRole.setCommand2(command2);//为调用者指定命令对象2 272 | invokerRole.invoke(0); //发起调用命令请求 273 | invokerRole.invoke(1); //发起调用命令请求 274 | } 275 | } 276 | ``` 277 | 278 | 测试类. 279 | 280 | ```java 281 | package com.command; 282 | 283 | public class MainTest { 284 | public static void main(String[] args) { 285 | ClientRole client = new ClientRole(); 286 | client.assembleAction(); 287 | } 288 | } 289 | ``` 290 | 291 | 输出结果如下: 292 | 293 | ![运行结果图](images/lijunhuayc_result.png) 294 | 295 | ### 总结 296 | * 每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。 297 | * 命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。 298 | * 命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。 299 | * 命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。 300 | 301 | ## Android源码中的模式实现 302 | Command接口中定义了一个execute方法,客户端通过Invoker调用命令操作再来调用Recriver执行命令;把对Receiver的操作请求封装在具体的命令中,使得命令发起者和命令接收者解耦。 303 | 以Android中大家常见的Runnable为例:客户端只需要new Thread(new Runnable(){}).start()就开始执行一系列相关的请求,这些请求大部分都是实现Runnable接口的匿名类。 304 | 【O_o 模式就在我们身边~】 305 | 306 | 命令接口Runnable接口定义如下: 307 | 308 | ``` 309 | package java.lang; 310 | /** 311 | * Represents a command that can be executed. Often used to run code in a 312 | * different {@link Thread}. 313 | */ 314 | public interface Runnable { 315 | 316 | /** 317 | * Starts executing the active part of the class' code. This method is 318 | * called when a thread is started that has been created with a class which 319 | * implements {@code Runnable}. 320 | */ 321 | public void run(); 322 | } 323 | ``` 324 | 325 | 调用者Thread源码如下(省略部分代码): 326 | Tips:命令模式在这里本来不需要继承Runnable接口,但为了方便性等,继承了Runnable接口实现了run方法,这个run是Thread自身的运行run的方法,而不是命令Runnable的run。 327 | 328 | ``` 329 | public class Thread implements Runnable { 330 | //省略部分无关代码... 331 | /* some of these are accessed directly by the VM; do not rename them */ 332 | volatile VMThread vmThread; 333 | volatile ThreadGroup group; 334 | volatile boolean daemon; 335 | volatile String name; 336 | volatile int priority; 337 | volatile long stackSize; 338 | Runnable target; 339 | private static int count = 0; 340 | 341 | public synchronized void start() { 342 | if (hasBeenStarted) { 343 | throw new IllegalThreadStateException("Thread already started."); // TODO Externalize? 344 | } 345 | 346 | hasBeenStarted = true; 347 | 348 | VMThread.create(this, stackSize); 349 | } 350 | //省略部分代码... 351 | } 352 | ``` 353 | 354 | 上面可以看到执行start()方法的时候实际执行了VMThread.create(this, stackSize)方法;create是VMThread的本地方法,其JNI实现在 android/dalvik/vm/native/java_lang_VMThread.cpp 中的 Dalvik_java_lang_VMThread_create方法,如下: 355 | 356 | ``` 357 | static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult) 358 | { 359 | Object* threadObj = (Object*) args[0]; 360 | s8 stackSize = GET_ARG_LONG(args, 1); 361 | 362 | /* copying collector will pin threadObj for us since it was an argument */ 363 | dvmCreateInterpThread(threadObj, (int) stackSize); 364 | RETURN_VOID(); 365 | } 366 | ``` 367 | 368 | 而dvmCreateInterpThread的实现在Thread.app中,如下: 369 | 370 | ``` 371 | bool dvmCreateInterpThread(Object* threadObj, int reqStackSize){ 372 | Thread* self = dvmThreadSelf(); 373 | 374 | Thread* newThread = allocThread(stackSize); 375 | newThread->threadObj = threadObj; 376 | 377 | Object* vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT); 378 | dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread); 379 | dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj); 380 | 381 | pthread_t threadHandle; 382 | int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread); 383 | 384 | dvmLockThreadList(self); 385 | 386 | assert(newThread->status == THREAD_STARTING); 387 | newThread->status = THREAD_VMWAIT; 388 | pthread_cond_broadcast(&gDvm.threadStartCond); 389 | 390 | dvmUnlockThreadList(); 391 | 392 | } 393 | 394 | static Thread* allocThread(int interpStackSize) 395 | { 396 | Thread* thread; 397 | thread = (Thread*) calloc(1, sizeof(Thread)); 398 | 399 | thread->status = THREAD_INITIALIZING; 400 | } 401 | ``` 402 | 403 | 这里是底层代码,简单介绍下就行了: 404 | 第4行通过调用 allocThread 创建一个名为newThread的dalvik Thread并设置一些属性,第5行设置其成员变量threadObj为传入的Android Thread,这样dalvik Thread就与Android Thread对象关联起来了;第7行然后创建一个名为vmThreadObj的VMThread对象,设置其成员变量vmData为前面创建的newThread,设置 Android Thread threadObj的成员变量vmThread为这个vmThreadObj,这样Android Thread通过VMThread的成员变量vmData就和dalvik Thread关联起来了。 405 | 406 | 接下来在12行通过pthread_create创建pthread线程,并让这个线程start,这样就会进入该线程的thread entry运行,下来我们来看新线程的thread entry方法 interpThreadStart,同样只列出关键的地方: 407 | 408 | ``` 409 | //pthread entry function for threads started from interpreted code. 410 | static void* interpThreadStart(void* arg){ 411 | Thread* self = (Thread*) arg; 412 | std::string threadName(dvmGetThreadName(self)); 413 | setThreadName(threadName.c_str()); 414 | 415 | //Finish initializing the Thread struct. 416 | dvmLockThreadList(self); 417 | prepareThread(self); 418 | 419 | while (self->status != THREAD_VMWAIT) 420 | pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock); 421 | 422 | dvmUnlockThreadList(); 423 | 424 | /* 425 | * Add a JNI context. 426 | */ 427 | self->jniEnv = dvmCreateJNIEnv(self); 428 | 429 | //修改状态为THREAD_RUNNING 430 | dvmChangeStatus(self, THREAD_RUNNING); 431 | 432 | //执行run方法 433 | Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run]; 434 | 435 | JValue unused; 436 | ALOGV("threadid=%d: calling run()", self->threadId); 437 | assert(strcmp(run->name, "run") == 0); 438 | dvmCallMethod(self, run, self->threadObj, &unused); 439 | ALOGV("threadid=%d: exiting", self->threadId); 440 | 441 | //移出线程并释放资源 442 | dvmDetachCurrentThread(); 443 | return NULL; 444 | } 445 | 446 | //Finish initialization of a Thread struct. 447 | static bool prepareThread(Thread* thread){ 448 | assignThreadId(thread); 449 | thread->handle = pthread_self(); 450 | thread->systemTid = dvmGetSysThreadId(); 451 | setThreadSelf(thread); 452 | return true; 453 | } 454 | 455 | //Explore our sense of self. Stuffs the thread pointer into TLS. 456 | static void setThreadSelf(Thread* thread){ 457 | int cc; 458 | cc = pthread_setspecific(gDvm.pthreadKeySelf, thread); 459 | } 460 | ``` 461 | 462 | 在新线程的interpThreadStart方法中,首先设置线程的名字,然后调用prepareThread设置线程id以及其它一些属性,其中调用了setThreadSelf将新dalvik Thread自身保存在TLS中,这样之后就能通过dvmThreadSelf方法从TLS中获取它。然后在29行处修改状态为THREAD_RUNNING,并在36行调用对应Android Thread的run()方法,其中调用了Runnable的run方法,运行我们自己的代码。 463 | 绕这么深才执行到我们的run方法,累不累? v_v 464 | 465 | ``` 466 | /** 467 | * Calls the run() method of the Runnable object the receiver 468 | * holds. If no Runnable is set, does nothing. 469 | * @see Thread#start 470 | */ 471 | public void run() { 472 | if (target != null) { 473 | target.run(); 474 | } 475 | } 476 | ``` 477 | 478 | 到此我们已经完成一次命令调用,至于底层run调用完毕后续执行代码,读者可以自行跟进看看~~~ 479 | 480 | 481 | ## 4. 杂谈 482 | ###优点与缺点 483 | ####优点 484 | 1. 降低对象之间的耦合度。 485 | 2. 新的命令可以很容易地加入到系统中。 486 | 3. 可以比较容易地设计一个组合命令。 487 | 4. 调用同一方法实现不同的功能 488 | 489 | ####缺点 490 | 使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。 491 | 比如上面的PeopleBean的属性增加,Receiver针对PeopleBean一个属性一个执行方法,一个Command的实现可以调用Receiver的一个执行方法,由此得需要设计多少个具体命令类呀!! 492 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/ClientRole.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | /** 4 | * @Desc: 命令对象和接受者对象的组装类[客户角色] 5 | * ps:我这把类名定义成ClientRole更方便读者理解这只是命令模式中的一个客户角色,而不是我们常规意义上说的客户端 6 | * @author ljh 7 | * @date 2015-3-16 上午11:08:03 8 | */ 9 | public class ClientRole { 10 | 11 | /** 12 | * @Description: 13 | * @author (ljh) @date 2015-3-16 上午11:13:06 14 | * @return void 15 | */ 16 | public void assembleAction() { 17 | ReceiverRole receiverRole1 = new ReceiverRole();//创建一个命令接收者 18 | Command command1 = new ConcreteCommandImpl1(receiverRole1);//创建一个命令的具体实现对象,并指定命令接收者 19 | Command command2 = new ConcreteCommandImpl2(receiverRole1); 20 | 21 | //PS:command1 修改people 年龄 22 | //PS:command2 修改people 姓名 23 | //PS:command23修改people 年龄和姓名 24 | 25 | InvokerRole invokerRole = new InvokerRole();//创建一个命令调用者 26 | invokerRole.setCommand1(command1);//为调用者指定命令对象1 27 | invokerRole.setCommand2(command2);//为调用者指定命令对象2 28 | invokerRole.invoke(0);//发起调用命令请求 29 | invokerRole.invoke(1);//发起调用命令请求 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/Command.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | /** 4 | * @Desc: 命令接口[命令角色] 5 | * ps:你也可以定义成abstract class类型 *_* 6 | * @author ljh 7 | * @date 2015-3-16 上午11:01:01 8 | */ 9 | public interface Command { 10 | public void execute(); 11 | public void undo(); 12 | public void redo(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/ConcreteCommandImpl1.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | /** 4 | * @Desc: 更新年龄的命令类[具体命令角色] 5 | * @author ljh 6 | * @date 2015-3-16 上午11:04:51 7 | */ 8 | public class ConcreteCommandImpl1 implements Command{ 9 | private ReceiverRole receiverRole1; 10 | 11 | public ConcreteCommandImpl1(ReceiverRole receiverRole1) { 12 | this.receiverRole1 = receiverRole1; 13 | } 14 | 15 | @Override 16 | public void execute() { 17 | /* 18 | * 可以加入命令排队等等,未执行的命令支持redo操作 19 | */ 20 | receiverRole1.opActionUpdateAge(1001);//执行具体的命令操作 21 | } 22 | 23 | @Override 24 | public void undo() { 25 | receiverRole1.rollBackAge();//执行具体的撤销回滚操作 26 | } 27 | 28 | @Override 29 | public void redo() { 30 | //在命令执行前可以修改命令的执行 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/ConcreteCommandImpl2.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | /** 4 | * @Desc: 更新姓名的命令类[具体命令角色] 5 | * @author ljh 6 | * @date 2015-3-16 上午11:04:51 7 | */ 8 | public class ConcreteCommandImpl2 implements Command{ 9 | private ReceiverRole receiverRole1; 10 | 11 | public ConcreteCommandImpl2(ReceiverRole receiverRole1) { 12 | this.receiverRole1 = receiverRole1; 13 | } 14 | 15 | @Override 16 | public void execute() { 17 | /* 18 | * 可以加入命令排队等等,未执行的命令支持redo操作 19 | */ 20 | receiverRole1.opActionUpdateName("lijunhuayc");//执行具体的命令操作 21 | } 22 | 23 | @Override 24 | public void undo() { 25 | receiverRole1.rollBackName();//执行具体的撤销回滚操作 26 | } 27 | 28 | @Override 29 | public void redo() { 30 | //在命令执行前可以修改命令的执行 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/ConcreteCommandImpl3.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | /** 4 | * @Desc: 命令接口的实现类[具体命令角色] 5 | * @author ljh 6 | * @date 2015-3-16 上午11:04:51 7 | */ 8 | public class ConcreteCommandImpl3 implements Command{ 9 | private ReceiverRole receiverRole1; 10 | 11 | public ConcreteCommandImpl3(ReceiverRole receiverRole1) { 12 | this.receiverRole1 = receiverRole1; 13 | } 14 | 15 | @Override 16 | public void execute() { 17 | /* 18 | * 可以加入命令排队等等,未执行的命令支持redo操作 19 | */ 20 | receiverRole1.opAction(9999, "神仙");//执行具体的命令操作 21 | } 22 | 23 | @Override 24 | public void undo() { 25 | receiverRole1.rollBack();//执行具体的撤销回滚操作 26 | } 27 | 28 | @Override 29 | public void redo() { 30 | //在命令执行前可以修改命令的执行 31 | } 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/InvokerRole.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | /** 4 | * @Desc: 命令调用[调用者角色] 5 | * ps:使用命令对象的入口,扶着调用命令对象执行请求 6 | * @author ljh 7 | * @date 2015-3-16 上午11:16:15 8 | */ 9 | public class InvokerRole { 10 | private Command command1; 11 | private Command command2; 12 | //持有多个命令对象[实际的情况也可能是一个命令对象的集合来保存命令对象] 13 | 14 | public void setCommand1(Command command1) { 15 | this.command1 = command1; 16 | } 17 | public void setCommand2(Command command2) { 18 | this.command2 = command2; 19 | } 20 | 21 | /** 22 | * @Description: 23 | * @author (ljh) @date 2015-3-16 下午1:40:54 24 | * @param args 0执行正常命令,1执行回滚命令 25 | * @return void 26 | */ 27 | public void invoke(int args) { 28 | //可以根据具体情况选择执行某些命令 29 | if(args == 0){ 30 | command1.execute(); 31 | command2.execute(); 32 | }else if(args == 1){ 33 | command1.undo(); 34 | command2.undo(); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/MainTest.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | public class MainTest { 4 | public static void main(String[] args) { 5 | ClientRole client = new ClientRole(); 6 | client.assembleAction(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/PeopleBean.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | /** 4 | * @Desc: 辅助类,作为接收者Receiver的成员,包含两个属性,用来观察命令的执行情况 5 | * @author ljh 6 | * @date 2015-3-16 上午11:29:11 7 | */ 8 | public class PeopleBean { 9 | private int age = -1; //年龄 10 | private String name = "NULL"; //姓名 11 | public PeopleBean() { 12 | } 13 | public PeopleBean(int age, String name) { 14 | this.age = age; 15 | this.name = name; 16 | } 17 | public void update(int age, String name) { 18 | this.age = age; 19 | this.name = name; 20 | } 21 | public void update(int age) { 22 | this.age = age; 23 | } 24 | public void update(String name) { 25 | this.name = name; 26 | } 27 | /** 28 | * @return 返回一个PeopleBean的克隆对象 29 | */ 30 | protected PeopleBean clone(){ 31 | return new PeopleBean(age, name); 32 | } 33 | @Override 34 | public String toString() { 35 | return " 【年龄:" + age + "\t姓名:" + name + "】"; 36 | } 37 | 38 | public int getAge() { 39 | return age; 40 | } 41 | 42 | public void setAge(int age) { 43 | this.age = age; 44 | } 45 | 46 | public String getName() { 47 | return name; 48 | } 49 | 50 | public void setName(String name) { 51 | this.name = name; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /command/lijunhuayc/resource/command/ReceiverRole.java: -------------------------------------------------------------------------------- 1 | package com.command; 2 | 3 | /** 4 | * @Desc: 命令的具体执行类[接受者角色] 5 | * ps:命令接收者可以是任意的类,只要实现了命令要求实现的相应功能即可。 6 | * @author ljh 7 | * @date 2015-3-16 上午11:06:14 8 | */ 9 | public class ReceiverRole { 10 | private PeopleBean people; 11 | private PeopleBean peopleCache = new PeopleBean(); //具体命令操作的缓存栈,用于回滚。这里为了方便就用一个PeopleBean来代替[实际的使用情况可能是需要回滚多个命令,这里只回滚一次] 12 | 13 | public ReceiverRole() { 14 | this.people = new PeopleBean(-1, "NULL");//初始化年龄为-1,姓名为NULL 15 | } 16 | 17 | public ReceiverRole(PeopleBean people) { 18 | this.people = people; 19 | } 20 | 21 | /** 22 | * @Description: 具体操作方法[修改年龄和姓名] 23 | * @author (ljh) @date 2015-3-16 上午11:07:32 24 | * @return void 25 | */ 26 | //修改年龄 27 | public void opActionUpdateAge(int age) { 28 | System.out.println("执行命令前:"+people.toString()); 29 | this.people.update(age); 30 | System.out.println("执行命令后:"+people.toString()+"\n"); 31 | } 32 | //修改姓名 33 | public void opActionUpdateName(String name) { 34 | System.out.println("执行命令前:"+people.toString()); 35 | this.people.update(name); 36 | System.out.println("执行命令后:"+people.toString()+"\n"); 37 | } 38 | 39 | /** 40 | * @Description: 回滚操作,用于撤销opAction执行的改变 41 | * @author (ljh) @date 2015-3-16 上午11:34:41 42 | * @return void 43 | */ 44 | public void rollBackAge() { 45 | people.setAge(peopleCache.getAge()); 46 | System.out.println("命令回滚后:"+people.toString()+"\n"); 47 | } 48 | public void rollBackName() { 49 | people.setName(peopleCache.getName()); 50 | System.out.println("命令回滚后:"+people.toString()+"\n"); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /composite/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [tiny-times](https://github.com/tiny-times) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /composite/tiny-times/readme.md: -------------------------------------------------------------------------------- 1 | 本文为 Android设计模式源码中 组合模式 分析 2 | Android系统版本: 5.0 3 | 分析者:Tiny-Times,分析状态:未完成,校对者:无,校对状态:未开始 4 | 5 | ## 1\. 模式介绍 6 | 7 | ### 模式的定义 8 | 9 |         组合模式(Composite Pattern)又叫作部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。 GoF在《设计模式》一书中这样定义组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。使得用户对单个对象和组合对象的使用具有一致性。 10 | 11 | ### 模式的使用场景 12 | 13 | * 表示对象的部分-整体层次结构。 14 | * 从一个整体中能够独立出部分模块或功能的场景。 15 | 16 | ## 2\. UML类图 17 | 18 | * * * 19 | 20 | ![组合模式通用类图][1] 21 | 22 | ### 角色分析 23 | 24 | * Component抽象构件角色 :定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。 25 | * Leaf叶子构件 : 叶子对象,其下再也没有其他的分支。 26 | * Composite树枝构件 :树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。 27 | 28 | ## 3\. 该模式的实现实例 29 | 30 | * * * 31 | 32 | 抽象构件 Component.java: 33 | 34 | public abstract class Component { 35 | //个体和整体都具有的共享 36 | public void doSomething(){ 37 | //业务逻辑 38 | } 39 | } 40 | 41 | 42 | 树枝构件 Composite.java 43 | 44 | public class Composite extends Component { 45 | //构件容器 46 | private ArrayList componentArrayList = new ArrayList(); 47 | //增加一个叶子构件或树枝构件 48 | public void add(Component component){ 49 | this.componentArrayList.add(component); 50 | } 51 | //删除一个叶子构件或树枝构件 52 | public void remove(Component component){ 53 | this.componentArrayList.remove(component); 54 | } 55 | //获得分支下的所有叶子构件和树枝构件 56 | public ArrayList getChildren(){ 57 | return this.componentArrayList; 58 | } 59 | } 60 | 61 | 62 | 树叶构件 Leaf.java 63 | 64 | public class Leaf extends Component { 65 | 66 | //可以覆写父类方法 67 | public void doSomething(){ 68 | } 69 | 70 | } 71 | 72 | 73 | 场景类 Client.java 74 | 75 | public class Client { 76 | public static void main(String[] args) { 77 | //创建一个根节点 78 | Composite root = new Composite(); 79 | root.doSomething(); 80 | //创建一个树枝构件 81 | Composite branch = new Composite(); 82 | //创建一个叶子节点 83 | Leaf leaf = new Leaf(); 84 | //建立整体 85 | root.add(branch); 86 | branch.add(leaf); 87 | } 88 | //通过递归遍历树 89 | public static void display(Composite root){ 90 | for(Component c:root.getChildren()){ 91 | if(c instanceof Leaf){ //叶子节点 92 | c.doSomething(); 93 | }else{ //树枝节点 94 | display((Composite)c); 95 | } 96 | } 97 | } 98 | } 99 | 100 | 101 | ### 组合模式在Android源码中的应用 102 | 103 | * * * 104 | 105 | **Adnroid系统中采用组合模式的组合视图类图:** 106 | 107 | ![enter image description here][2] 108 | 109 | **具体实现代码** 110 | 111 | View.java 112 | 113 | public class View ....{ 114 | //此处省略无关代码... 115 | } 116 | 117 | 118 | ViewGroup.java 119 | 120 | public abstract class ViewGroup extends View ...{ 121 | 122 | //增加子节点 123 | public void addView(View child, int index) { 124 | 125 | } 126 | //删除子节点 127 | public void removeView(View view) { 128 | 129 | } 130 | //查找子节点 131 | public View getChildAt(int index) { 132 | try { 133 | return mChildren[index]; 134 | } catch (IndexOutOfBoundsException ex) { 135 | return null; 136 | } 137 | } 138 | } 139 | 140 | 141 | ## 4\. 注意事项 142 | 143 | * * * 144 | 145 | 使用组合模式组织起来的对象具有出色的层次结构,每当对顶层组合对象执行一个操作的时候,实际上是在对整个结构进行深度优先的节点搜索。但是这些优点都是用操作的代价换取的,比如顶级每执行一次 store.show 实际的操作就是整一颗树形结构的节点均遍历执行一次。 146 | 147 | ## 5\. 杂谈 148 | 149 | * * * 150 | 151 | **优点** 152 | 153 | * 不破坏封装,整体类与局部类之间松耦合,彼此相对独立 。 154 | * 具有较好的可扩展性。 155 | * 支持动态组合。在运行时,整体对象可以选择不同类型的局部对象。 156 | * 整体类可以对局部类进行包装,封装局部类的接口,提供新的接口。 157 | 158 | **缺点** 159 | 160 | * 整体类不能自动获得和局部类同样的接口。 161 | * 创建整体类的对象时,需要创建所有局部类的对象 。 162 | 163 | [1]: http://belial.me/wp-content/uploads/2015/03/QQ截图20150318225518.png 164 | [2]: http://belial.me/wp-content/uploads/2015/03/QQ截图20150315115212.png -------------------------------------------------------------------------------- /decorator/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [tiny-times](https://github.com/tiny-times) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /decorator/tiny-times/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之${模式名} 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 ${模式名} 分析 4 | > Android系统版本: ${系统版本号,例如 4.2.1} 5 | > 分析者:[${分析者}](${分析者 Github 地址}),分析状态:未完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:未开始 6 | 7 | 8 | `复制一份到自己的项目文件夹下,然后根据自己项目替换掉 ${} 内容,删掉本行及上面两行。` 9 | 10 | 该任务不仅要用java写出该模式的一个简单示例,还有分析该模式在Android源码中的应用,可以参考[Mr.Simple的单例模式](singleton/mr.simple/readme.md)、[Mr.Simple的观察者模式](observer/mr.simple/readme.md)。 11 | 12 | 13 | ## 1. 模式介绍 14 | 15 | ### 模式的定义 16 | `模式的一句话定义` 17 | 18 | 19 | ### 模式的使用场景 20 | 21 | 22 | 23 | ## 2. UML类图 24 | `这里是该设计模式的经典UML图` 25 | 26 | ### 角色介绍 27 | `对UML图中的各个角色进行介绍` 28 | 29 | 30 | 31 | 32 | ## 3. 模式的简单实现 33 | ### 简单实现的介绍 34 | `自己实现一个小型模式案例,通过这个案例让读者了解这个模式的一般应用` 35 | 36 | ### 实现源码 37 | `上述案例的源码实现` 38 | 39 | 40 | ### 总结 41 | `对上述的简单示例进行总结说明` 42 | 43 | 44 | 45 | 46 | ## Android源码中的模式实现 47 | `分析源码中的模式实现,列出相关源码,以及使用该模式原因等` 48 | 49 | 50 | 51 | 52 | ## 4. 杂谈 53 | 该模式的优缺点以及自己的一些感悟,非所有项目必须。 54 | 55 | 56 | 57 | `写完相关内容之后到开发群告知管理员,管理员安排相关人员进行审核,审核通过之后即可。` 58 | 59 | -------------------------------------------------------------------------------- /facade/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/facade/.DS_Store -------------------------------------------------------------------------------- /facade/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 模式名 | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| ------------- | 4 | | 外观模式 | [elsdnwn](https://github.com/elsdnwn) | 2015.3.7 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /facade/elsdnwn/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/facade/elsdnwn/.DS_Store -------------------------------------------------------------------------------- /facade/elsdnwn/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/facade/elsdnwn/images/.DS_Store -------------------------------------------------------------------------------- /facade/elsdnwn/images/contextimpl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/facade/elsdnwn/images/contextimpl.png -------------------------------------------------------------------------------- /facade/elsdnwn/images/facade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/facade/elsdnwn/images/facade.png -------------------------------------------------------------------------------- /facade/elsdnwn/images/no-facade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/facade/elsdnwn/images/no-facade.png -------------------------------------------------------------------------------- /facade/elsdnwn/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之外观模式(Facade) 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 外观模式 分析 4 | > Android系统版本: 2.3 5 | > 分析者:[elsdnwn](https://github.com/elsdnwn)、[Mr.Simple](https://github.com/bboyfeiyu),分析状态:已完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:未开始 6 | 7 | 8 | 9 | ## 1. 模式介绍 10 | 11 | ### 模式的定义 12 | 外观模式(也成为门面模式)要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。它提供一个高层次的接口,使得子系统更易于使用。 13 | 14 | ### 模式的使用场景 15 | 1. 在设计初期阶段,将不同的两个层分离; 16 | 2. 在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用它们的用户程序带来了使用上的困难,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。 17 | 3. 在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含非常重要的功能,新的需求开发必须依赖于它。 18 | 19 | ## 2. UML类图 20 | ![url](images/facade.png) 21 | 22 | ### 角色介绍 23 | * Client : 客户端程序。 24 | * Facade : 对外的统一入口,即外观对象。 25 | * SubSystemA : 子系统A。 26 | * SubSystemB : 子系统B。 27 | * SubSystemC : 子系统C。 28 | * SubSystemD : 子系统D。 29 | 30 | ## 不使用外观模式 31 | ![url](images/no-facade.png) 32 | 如上述所说,门面模式提供一个高层次的接口,使得子系统更易于使用。因此在不使用该模式的情况下,客户端程序使用相关功能的成本就会比较的复杂,需要和各个子系统进行交互 ( 如上图 ),这样就使得系统的稳定性受到影响,用户的使用成本也相对较高。 33 | 34 | 35 | ## 3. 模式的简单实现 36 | ### 简单实现的介绍 37 | 电视遥控器是现实生活中一个比较好的外观模式的运用,遥控器可以控制电源的开源、声音的调整、频道的切换等。这个遥控器就是我们这里说的外观或者门面,而电源、声音、频道切换系统就是我们的子系统。遥控器统一对这些子模块的控制,我想你没有用过多个遥控器来分别控制电源开关、声音控制等功能。下面我们就来简单模拟一下这个系统。 38 | 39 | ### 实现源码 40 | TvController.java 41 | 42 | ```java 43 | public class TvController { 44 | private PowerSystem mPowerSystem = new PowerSystem(); 45 | private VoiceSystem mVoiceSystem = new VoiceSystem(); 46 | private ChannelSystem mChannelSystem = new ChannelSystem(); 47 | 48 | public void powerOn() { 49 | mPowerSystem.powerOn(); 50 | } 51 | 52 | public void powerOff() { 53 | mPowerSystem.powerOff(); 54 | } 55 | 56 | public void turnUp() { 57 | mVoiceSystem.turnUp(); 58 | } 59 | 60 | public void turnDown() { 61 | mVoiceSystem.turnDown(); 62 | } 63 | 64 | public void nextChannel() { 65 | mChannelSystem.next(); 66 | } 67 | 68 | public void prevChannel() { 69 | mChannelSystem.prev(); 70 | } 71 | } 72 | ``` 73 | PowerSystem.java 74 | 75 | ```java 76 | /** 77 | * 电源控制系统 78 | */ 79 | class PowerSystem { 80 | public void powerOn() { 81 | System.out.println("开机"); 82 | } 83 | 84 | public void powerOff() { 85 | System.out.println("关机"); 86 | } 87 | } 88 | ``` 89 | 90 | VoiceSystem.java 91 | 92 | ```java 93 | /** 94 | * 声音控制系统 95 | */ 96 | class VoiceSystem { 97 | public void turnUp() { 98 | System.out.println("音量增大"); 99 | } 100 | 101 | public void turnDown() { 102 | System.out.println("音量减小"); 103 | } 104 | } 105 | ``` 106 | 107 | 108 | ChannelSystem.java 109 | 110 | ```java 111 | /** 112 | * 频道控制系统 113 | */ 114 | class ChannelSystem { 115 | public void next() { 116 | System.out.println("下一频道"); 117 | } 118 | 119 | public void prev() { 120 | System.out.println("上一频道"); 121 | } 122 | } 123 | ``` 124 | 125 | 测试代码 : 126 | 127 | ```java 128 | public class TvController { 129 | private PowerSystem mPowerSystem = new PowerSystem(); 130 | private VoiceSystem mVoiceSystem = new VoiceSystem(); 131 | private ChannelSystem mChannelSystem = new ChannelSystem(); 132 | 133 | public void powerOn() { 134 | mPowerSystem.powerOn(); 135 | } 136 | 137 | public void powerOff() { 138 | mPowerSystem.powerOff(); 139 | } 140 | 141 | public void turnUp() { 142 | mVoiceSystem.turnUp(); 143 | } 144 | 145 | public void turnDown() { 146 | mVoiceSystem.turnDown(); 147 | } 148 | 149 | public void nextChannel() { 150 | mChannelSystem.next(); 151 | } 152 | 153 | public void prevChannel() { 154 | mChannelSystem.prev(); 155 | } 156 | } 157 | 158 | ``` 159 | 160 | 输出结果: 161 | 162 | ``` 163 | 开机 164 | 下一频道 165 | 音量增大 166 | 关机 167 | ``` 168 | 上面的TvController封装了对电源、声音、频道切换的操作,为用户提供了一个统一的接口。使得用户控制电视机更加的方便、更易于使用。 169 | 170 | ## Android源码中的模式实现 171 | 在开发过程中,Context是最重要的一个类型。它封装了很多重要的操作,比如startActivity()、sendBroadcast()等,几乎是开发者对应用操作的统一入口。Context是一个抽象类,它只是定义了抽象接口,真正的实现在ContextImpl类中。它就是今天我们要分析的外观类。 172 | 173 | 在应用启动时,首先会fork一个子进程,并且调用ActivityThread.main方法启动该进程。ActivityThread又会构建Application对象,然后和Activity、ContextImpl关联起来,然后再调用Activity的onCreate、onStart、onResume函数使Activity运行起来。我们看看下面的相关代码: 174 | 175 | ``` 176 | private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { 177 | // 代码省略 178 | 179 | // 1、创建并且加载Activity,调用其onCreate函数 180 | Activity a = performLaunchActivity(r, customIntent); 181 | 182 | if (a != null) { 183 | r.createdConfig = new Configuration(mConfiguration); 184 | Bundle oldState = r.state; 185 | // 2、调用Activity的onResume方法,使Activity变得可见 186 | handleResumeActivity(r.token, false, r.isForward); 187 | 188 | } 189 | } 190 | 191 | 192 | private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { 193 | // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); 194 | // 代码省略 195 | 196 | Activity activity = null; 197 | try { 198 | java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); 199 | // 1、创建Activity 200 | activity = mInstrumentation.newActivity( 201 | cl, component.getClassName(), r.intent); 202 | r.intent.setExtrasClassLoader(cl); 203 | if (r.state != null) { 204 | r.state.setClassLoader(cl); 205 | } 206 | } catch (Exception e) { 207 | if (!mInstrumentation.onException(activity, e)) { 208 | throw new RuntimeException( 209 | "Unable to instantiate activity " + component 210 | + ": " + e.toString(), e); 211 | } 212 | } 213 | 214 | try { 215 | // 2、创建Application 216 | Application app = r.packageInfo.makeApplication(false, mInstrumentation); 217 | 218 | if (activity != null) { 219 | // ***** 构建ContextImpl ****** 220 | ContextImpl appContext = new ContextImpl(); 221 | appContext.init(r.packageInfo, r.token, this); 222 | appContext.setOuterContext(activity); 223 | // 获取Activity的title 224 | CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); 225 | Configuration config = new Configuration(mConfiguration); 226 | 227 | // 3、Activity与context, Application关联起来 228 | activity.attach(appContext, this, getInstrumentation(), r.token, 229 | r.ident, app, r.intent, r.activityInfo, title, r.parent, 230 | r.embeddedID, r.lastNonConfigurationInstance, 231 | r.lastNonConfigurationChildInstances, config); 232 | // 代码省略 233 | 234 | // 4、回调Activity的onCreate方法 235 | mInstrumentation.callActivityOnCreate(activity, r.state); 236 | 237 | // 代码省略 238 | } 239 | r.paused = true; 240 | 241 | mActivities.put(r.token, r); 242 | 243 | } catch (SuperNotCalledException e) { 244 | throw e; 245 | 246 | } catch (Exception e) { 247 | 248 | } 249 | 250 | return activity; 251 | } 252 | 253 | 254 | final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { 255 | 256 | unscheduleGcIdler(); 257 | 258 | // 1、最终调用Activity的onResume方法 259 | ActivityClientRecord r = performResumeActivity(token, clearHide); 260 | // 代码省略 261 | // 2、这里是重点,在这里使DecorView变得可见 262 | if (r.window == null && !a.mFinished && willBeVisible) { 263 | // 获取Window,即PhoneWindow类型 264 | r.window = r.activity.getWindow(); 265 | // 3、获取Window的顶级视图,并且使它可见 266 | View decor = r.window.getDecorView(); 267 | decor.setVisibility(View.INVISIBLE); 268 | // 4、获取WindowManager 269 | ViewManager wm = a.getWindowManager(); 270 | // 5、构建LayoutParams参数 271 | WindowManager.LayoutParams l = r.window.getAttributes(); 272 | a.mDecor = decor; 273 | l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 274 | l.softInputMode |= forwardBit; 275 | if (a.mVisibleFromClient) { 276 | a.mWindowAdded = true; 277 | // 6、将DecorView添加到WindowManager中,最终的操作是通过WindowManagerService的addView来操作 278 | wm.addView(decor, l); 279 | } 280 | } else if (!willBeVisible) { 281 | if (localLOGV) Slog.v( 282 | TAG, "Launch " + r + " mStartedActivity set"); 283 | r.hideForNow = true; 284 | } 285 | // 代码省略 286 | } 287 | 288 | public final ActivityClientRecord performResumeActivity(IBinder token, 289 | boolean clearHide) { 290 | ActivityClientRecord r = mActivities.get(token); 291 | 292 | if (r != null && !r.activity.mFinished) { 293 | try { 294 | // 代码省略 295 | // 执行onResume 296 | r.activity.performResume(); 297 | // 代码省略 298 | } catch (Exception e) { 299 | 300 | } 301 | } 302 | return r; 303 | } 304 | ``` 305 | 306 | Activity启动之后,Android给我们提供了操作系统服务的统一入口,也就是Activity本身。这些工作并不是Activity自己实现的,而是将操作委托给Activity父类ContextThemeWrapper的mBase对象,这个对象的实现类就是ContextImpl ( 也就是performLaunchActivity方法中构建的ContextImpl )。 307 | 308 | 309 | ```java 310 | class ContextImpl extends Context { 311 | private final static String TAG = "ApplicationContext"; 312 | private final static boolean DEBUG = false; 313 | private final static boolean DEBUG_ICONS = false; 314 | 315 | private static final Object sSync = new Object(); 316 | private static AlarmManager sAlarmManager; 317 | private static PowerManager sPowerManager; 318 | private static ConnectivityManager sConnectivityManager; 319 | private AudioManager mAudioManager; 320 | LoadedApk mPackageInfo; 321 | private Resources mResources; 322 | private PackageManager mPackageManager; 323 | private NotificationManager mNotificationManager = null; 324 | private ActivityManager mActivityManager = null; 325 | 326 | // 代码省略 327 | 328 | @Override 329 | public void sendBroadcast(Intent intent) { 330 | String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); 331 | try { 332 | ActivityManagerNative.getDefault().broadcastIntent( 333 | mMainThread.getApplicationThread(), intent, resolvedType, null, 334 | Activity.RESULT_OK, null, null, null, false, false); 335 | } catch (RemoteException e) { 336 | } 337 | } 338 | 339 | 340 | @Override 341 | public void startActivity(Intent intent) { 342 | if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { 343 | throw new AndroidRuntimeException( 344 | "Calling startActivity() from outside of an Activity " 345 | + " context requires the FLAG_ACTIVITY_NEW_TASK flag." 346 | + " Is this really what you want?"); 347 | } 348 | mMainThread.getInstrumentation().execStartActivity( 349 | getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1); 350 | } 351 | 352 | 353 | @Override 354 | public ComponentName startService(Intent service) { 355 | try { 356 | ComponentName cn = ActivityManagerNative.getDefault().startService( 357 | mMainThread.getApplicationThread(), service, 358 | service.resolveTypeIfNeeded(getContentResolver())); 359 | if (cn != null && cn.getPackageName().equals("!")) { 360 | throw new SecurityException( 361 | "Not allowed to start service " + service 362 | + " without permission " + cn.getClassName()); 363 | } 364 | return cn; 365 | } catch (RemoteException e) { 366 | return null; 367 | } 368 | } 369 | 370 | @Override 371 | public String getPackageName() { 372 | if (mPackageInfo != null) { 373 | return mPackageInfo.getPackageName(); 374 | } 375 | throw new RuntimeException("Not supported in system context"); 376 | } 377 | } 378 | ``` 379 | 可以看到,ContextImpl内部有很多xxxManager类的对象,也就是我们上文所说的各种子系统的角色。ContextImpl内部封装了一些系统级别的操作,有的子系统功能虽然没有实现,但是也提供了访问该子系统的接口,比如获取ActivityManager的getActivityManager方法。 380 | 381 | 比如我们要启动一个Activity的时候,我们调用的是startActivity方法,这个功能的内部实现实际上是Instrumentation完成的。ContextImpl封装了这个功能,使得用户根本不需要知晓Instrumentation相关的信息,直接使用startActivity即可完成相应的工作。其他的子系统功能也是类似的实现,比如启动Service和发送广播内部使用的是ActivityManagerNative等。ContextImpl的结构图如下 : 382 | ![context](images/contextimpl.png) 383 | 384 | 外观模式非常的简单,只是封装了子系统的操作,并且暴露接口让用户使用,避免了用户需要与多个子系统进行交互,降低了系统的耦合度、复杂度。如果没有外观模式的封装,那么用户就必须知道各个子系统的相关细节,子系统之间的交互必然造成纠缠不清的关系,影响系统的稳定性、复杂度。 385 | 386 | 387 | ## 4. 杂谈 388 | ### 优点与缺点 389 | #### 优点 390 | * 使用方便,使用外观模式客户端完全不需要知道子系统的实现过程; 391 | * 降低客户端与子系统的耦合; 392 | * 更好的划分访问层次; 393 | 394 | #### 缺点 395 | * 减少了可变性和灵活性; 396 | * 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”; 397 | 398 | 399 | 400 | -------------------------------------------------------------------------------- /factory-method/AigeStudio/images/factory-method.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/factory-method/AigeStudio/images/factory-method.jpg -------------------------------------------------------------------------------- /factory-method/AigeStudio/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之工厂方法模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中工厂方法模式分析 4 | > Android系统版本:4.4.4 5 | > 分析者:[Aige](https://github.com/AigeStudio),分析状态:未完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:未开始 6 | 7 | ## 1. 模式介绍 8 | 9 | ### 模式的定义 10 | 你要什么工厂造给你就是了,你不用管我是怎么造的,造好你拿去用就是了,奏是介么任性。 11 | 12 | ### 模式的使用场景 13 | 任何需要生成对象的情况都可使用工厂方法替代生成。 14 | 15 | ## 2. UML类图 16 | ![](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis/blob/master/factory-method/AigeStudio/images/factory-method.jpg?raw=true) 17 | 18 | ### 角色介绍 19 | 如图 20 | 21 | ## 3. 模式的简单实现 22 | ### 简单实现的介绍 23 | 工厂方法相对来说比较简单也很常用,如上所说,任何需要生成对象的情况都可使用工厂方法替代生成。我们知道在Java中生成对象一般会使用关键字new: 24 | 25 | ```java 26 | Client client = new Client(); 27 | ``` 28 | 29 | 如果使用工厂方法来替代,我们则可以先声明一个工厂类: 30 | 31 | ```java 32 | /** 33 | * 工厂类 34 | * 35 | * @author Aige{@link https://github.com/AigeStudio} 36 | * 37 | */ 38 | public class Factory { 39 | public static Client getClient() { 40 | return new Client(); 41 | } 42 | } 43 | ``` 44 | 45 | 然后呢就可以使用这个工厂类来生成Client对象: 46 | 47 | ```java 48 | Client client = Factory.getClient(); 49 | ``` 50 | 51 | 但是,相信即便是新手也会觉得这套路感觉不对啊是吧,生成个对象居然这么麻烦我也是醉了。So,我们极少这么使用,生成某个具体的对象直接new就是了对吧。那好,假定我们这有三个类:香蕉类、黄瓜类、甘蔗类,我们将其统称为水果类,额某种意义上来说黄瓜也算是水果,为其定义一个抽象父类: 52 | 53 | ```java 54 | /** 55 | * 抽象水果类 56 | * 57 | * @author Aige{@link https://github.com/AigeStudio} 58 | * 59 | */ 60 | public abstract class Fruits { 61 | /** 62 | * 水果的颜色 63 | */ 64 | public abstract void color(); 65 | 66 | /** 67 | * 水果的重量 68 | */ 69 | public abstract void weight(); 70 | } 71 | ``` 72 | 73 | 然后呢则是各个具体的水果类: 74 | 75 | ```java 76 | /** 77 | * 香蕉类 78 | * 79 | * @author Aige{@link https://github.com/AigeStudio} 80 | * 81 | */ 82 | public class Banana extends Fruits { 83 | @Override 84 | public void color() { 85 | System.out.println("Banana is red"); 86 | } 87 | 88 | @Override 89 | public void weight() { 90 | System.out.println("Weight 0.3kg"); 91 | } 92 | } 93 | ``` 94 | 95 | ```java 96 | /** 97 | * 黄瓜类 98 | * 99 | * @author Aige{@link https://github.com/AigeStudio} 100 | * 101 | */ 102 | public class Cucumber extends Fruits { 103 | @Override 104 | public void color() { 105 | System.out.println("Cucumber is green"); 106 | } 107 | 108 | @Override 109 | public void weight() { 110 | System.out.println("Weight 0.5kg"); 111 | } 112 | } 113 | ``` 114 | 115 | ```java 116 | /** 117 | * 甘蔗类 118 | * 119 | * @author Aige{@link https://github.com/AigeStudio} 120 | * 121 | */ 122 | public class Sugarcane extends Fruits { 123 | @Override 124 | public void color() { 125 | System.out.println("Sugarcane is purple"); 126 | } 127 | 128 | @Override 129 | public void weight() { 130 | System.out.println("Weight 1.3kg"); 131 | } 132 | } 133 | ``` 134 | 135 | 最后,上场景: 136 | 137 | ```java 138 | /** 139 | * 场景模拟类 140 | * 141 | * @author Aige{@link https://github.com/AigeStudio} 142 | * 143 | */ 144 | public class Client { 145 | public static void main(String[] args) { 146 | Fruits banana = new Banana(); 147 | banana.color(); 148 | banana.weight(); 149 | 150 | Fruits cucumber = new Cucumber(); 151 | cucumber.color(); 152 | cucumber.weight(); 153 | 154 | Fruits sugarcane = new Sugarcane(); 155 | sugarcane.color(); 156 | sugarcane.weight(); 157 | } 158 | } 159 | ``` 160 | 161 | 具体的运行结果就不贴了,不用运行你也该知道是啥结果 = = 。这样一段代码其实已经算过得去了,抽象有了具现也有,也确实没啥问题对吧,可是仔细想想每个类我们都要通过具体的类去new生成,能不能进一步解耦将生成具体对象的工作交由第三方去做呢?答案是肯定的,我们来定义一个果农类,你要啥水果给果农说一声,让它给你就是了: 162 | 163 | ```java 164 | /** 165 | * 果农类 166 | * 167 | * @author Aige{@link https://github.com/AigeStudio} 168 | * 169 | */ 170 | public class Grower { 171 | public T getFruits(Class clz) { 172 | try { 173 | Fruits fruits = (Fruits) Class.forName(clz.getName()).newInstance(); 174 | return (T) fruits; 175 | } catch (Exception e) { 176 | return null; 177 | } 178 | } 179 | } 180 | ``` 181 | 182 | 这样一来,我们要什么水果直接跟果农报个名(Class clz),然后果农给你就是了,OK,修改下场景类: 183 | 184 | ```java 185 | /** 186 | * 场景模拟类 187 | * 188 | * @author Aige{@link https://github.com/AigeStudio} 189 | * 190 | */ 191 | public class Client { 192 | public static void main(String[] args) { 193 | Grower grower = new Grower(); 194 | 195 | Fruits banana = grower.getFruits(Banana.class); 196 | banana.color(); 197 | banana.weight(); 198 | 199 | Fruits cucumber = grower.getFruits(Cucumber.class); 200 | cucumber.color(); 201 | cucumber.weight(); 202 | 203 | Fruits sugarcane = grower.getFruits(Sugarcane.class); 204 | sugarcane.color(); 205 | sugarcane.weight(); 206 | } 207 | } 208 | ``` 209 | 210 | 具体的水果类不用变,运行结果什么的就不扯了,自己去试。恩,这样就差不多,更进一步地,我们可以考虑将果农类进一步抽象,在抽象类中定义方法: 211 | 212 | ```java 213 | /** 214 | * 抽象果农类 215 | * 216 | * @author Aige{@link https://github.com/AigeStudio} 217 | * 218 | */ 219 | public abstract class AGrower { 220 | /** 221 | * 获取水果 222 | * 223 | * @param clz 224 | * 具体水果类型 225 | * @return 具体水果的对象 226 | */ 227 | public abstract T getFruits(Class clz); 228 | } 229 | ``` 230 | 231 | 然后让Grower extends AGrower就行了其他不变,最终的结构就与上面我们给出的UML类图一致了,香蕉、甘蔗、黄瓜什么的为具体的产品类而水果则作为产品类的抽象,Grower和AGrower的关系亦如此。然而很多时候其实我们没必要多个工厂类,一个足以: 232 | 233 | ```java 234 | /** 235 | * 果农类 236 | * 237 | * @author Aige{@link https://github.com/AigeStudio} 238 | * 239 | */ 240 | public class Grower { 241 | public static T getFruits(Class clz) { 242 | try { 243 | Fruits fruits = (Fruits) Class.forName(clz.getName()).newInstance(); 244 | return (T) fruits; 245 | } catch (Exception e) { 246 | return null; 247 | } 248 | } 249 | } 250 | ``` 251 | 252 | 如代码所示,去掉了抽象父类的继承使getFruits变为静态方法在Client中直接调用不再生成类了。具体代码就不贴了。 253 | 254 | ### 实现源码 255 | `上述案例的源码实现` 256 | 257 | 258 | ### 总结 259 | `对上述的简单示例进行总结说明` 260 | 261 | 262 | 263 | 264 | ## Android源码中的模式实现 265 | `分析源码中的模式实现,列出相关源码,以及使用该模式原因等` 266 | 267 | 268 | 269 | 270 | ## 4. 杂谈 271 | 该模式的优缺点以及自己的一些感悟,非所有项目必须。 272 | 273 | 274 | 275 | `写完相关内容之后到开发群告知管理员,管理员安排相关人员进行审核,审核通过之后即可。` 276 | 277 | -------------------------------------------------------------------------------- /factory-method/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [AigeStudio](https://github.com/AigeStudio) | 2015.3.18 | -------------------------------------------------------------------------------- /flyweight/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [用户名](git地址) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /flyweight/lvtea0105/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之享元模式 2 | 3 | 本文为 Android 设计模式源码解析 中 享元模式 分析 4 | 5 | Android系统版本: 4.0 6 | 7 | 分析者:lvtea0105,分析状态:未完成,校对者:Mr.Simple,校对状态:未开始 8 | 9 | 1. 模式介绍 10 | 11 | 模式的定义 12 | 13 | 通过共享有效支持大量的细粒度对象,节省系统中重复创建相同内容对象的性能消耗,进而提高应用程序的性能。 14 | 享元模式可分为单纯享元模式和复合享元模式。 15 | 16 | 模式的使用场景 17 | 18 | 面向对象编程在某些情况下会创建大量的细粒度对象,它们的产生,存储,销毁都会造成资源和性能上的损耗, 19 | 可能会在程序运行时形成效率瓶颈,在遇到以下情况时,即可考虑使用享元模式: 20 | (1)一个应用程序使用了大量的对象,耗费大量的内存,降低了系统的效率。 21 | (2)这些对象的状态可以分离出内外两部分。 22 | (3)这些对象按照状态分成很多的组,当把删除对象的外部状态时,可以用相对较少的共享对象取代很多组对象。 23 | (4)应用程序不依赖这些对象的身份,即这些对象是不可分辨的。 24 | 在一般的开发中享元模式并不常用,其常常应用于系统底层的开发,以便解决系统的性能问题。 25 | 26 | 2. UML类图 27 | 28 | 3. 模式的简单实现 29 | 30 | 简单实现的介绍 31 | 32 | (1)享元模式如何实现共享 33 | 34 | 将事物的共性共享,同时又保留它的个性。为了做到这点,享元模式中区分了内蕴状态(Internal State)和 35 | 外蕴状态(External State)。内蕴状态就是共性,外蕴状态就是个性了。 36 | 内蕴状态存储在享元内部,不会随环境改变而变化,是可以共享的; 37 | 外蕴状态是不可以共享的,它随环境的改变而变化,通常外蕴状态是由客户端来保持的(因为环境的变化是由客户端引起的)。 38 | 39 | 40 | 单纯享元模式 41 | 42 | ————所有的享元对象都是可以共享的 43 | 44 | 抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法,外蕴状态以参数形式传入此方法。 45 | 具体享元(ConcreteFlyweight)角色:实现抽象享元角色定义的接口。如果有内蕴状态的话,则必须为内蕴状态提供存储空间。 46 | 享元工厂(FlyweightFactory)角色:负责创建和管理享元角色,保证享元对象可以被系统适当地共享。 47 | 当客户端调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。 48 | 如果已经有了,就提供这个已有的享元对象;如果没有,就创建一个合适的享元对象。 49 | 客户端角色:维护所有享元对象的引用,同时还需要存储享元对象所对应的外蕴状态。 50 | 51 | 52 | 复合享元模式 53 | 54 | ————将一些单纯享元使用合成模式加以复合,形成复合享元对象。复合享元对象本身是不能共享的, 55 | 但是它们可以分解成能够进行共享的单纯享元对象。 56 | 57 | 抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法,外蕴状态以参数形式传入此方法。 58 | 具体享元(ConcreteFlyweight)角色:实现抽象享元角色定义的接口。如果有内蕴状态的话,则必须为内蕴状态提供存储空间。 59 | 复合享元(ConcreteCompositeFlyweight)角色:复合享元角色所代表的对象是不可以共享的, 60 | 但是一个复合享元对象可以分解成能够进行共享的单纯享元对象。 61 | 享元工厂(FlyweightFactory)角色:负责创建和管理享元角色,保证享元对象可以被系统适当地共享。 62 | 当客户端调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。 63 | 如果已经有了,就提供这个已有的享元对象;如果没有,就创建一个合适的享元对象。 64 | 客户端角色:维护所有享元对象的引用,同时还需要存储享元对象所对应的外蕴状态。 65 | 66 | 实现源码 67 | 68 | Android源码中的模式实现 69 | 70 | 4. 优点与缺点 71 | 优点————大幅度地降低内存中对象的数量,节省系统资源的开销 72 | 73 | 缺点————1、为了使对象可以共享,享元模式需要将部分状态外部化,使得系统的逻辑变得复杂。 74 | 2、读取状态外部化的享元对象,影响了系统速度,使运行时间有所加长。 75 | -------------------------------------------------------------------------------- /interpreter/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [用户名](git地址) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /iterator/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/iterator/.DS_Store -------------------------------------------------------------------------------- /iterator/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [haoxiqiang](https://github.com/Haoxiqiang) | 2015年3月13日 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /iterator/haoxiqiang/images/Iterator_UML_class_diagram.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/iterator/haoxiqiang/images/Iterator_UML_class_diagram.svg.png -------------------------------------------------------------------------------- /iterator/haoxiqiang/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之迭代器(Iterator)模式 2 | ======================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 迭代器模式 分析 4 | > Android系统版本: 5.0 5 | > 分析者: [haoxiqiang](https://github.com/Haoxiqiang),分析状态:完成,校对者:,校对状态:未完成 6 | 7 | 8 | 9 | ## 1. 模式介绍 10 | ### 模式的定义 11 | 迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。 12 | 13 | ### 模式的使用场景 14 |   Java JDK 1.2 版开始支持迭代器。每一个迭代器提供next()以及hasNext()方法,同时也支持remove()(1.8的时候remove已经成为default throw new UnsupportedOperationException("remove"))。对Android来说,集合Collection实现了Iterable接口,就是说,无论是List的一大家子还是Map的一大家子,我们都可以使用Iterator来遍历里面的元素,[可以使用Iterator的集合](http://docs.oracle.com/javase/8/docs/api/java/util/package-tree.html) 15 | 16 | ## 2. UML类图 17 |     18 |  ![iterator](images/Iterator_UML_class_diagram.svg.png) 19 | 20 | ### 角色介绍   21 | * 迭代器接口Iterator:该接口必须定义实现迭代功能的最小定义方法集比如提供hasNext()和next()方法。 22 | * 迭代器实现类:迭代器接口Iterator的实现类。可以根据具体情况加以实现。 23 | * 容器接口:定义基本功能以及提供类似Iterator iterator()的方法。 24 | * 容器实现类:容器接口的实现类。必须实现Iterator iterator()方法。 25 | 26 | 27 | ## 3. 模式的简单实现 28 | ### 简单实现的介绍 29 | 我们有一个数组,对其遍历的过程我们希望使用者像ArrayList一样的使用,我们就可以用过iterator来实现. 30 | 31 | ### 实现源码 32 | 下面我们自己实现一个Iterator的集合 33 | 34 | ``` 35 | ... 36 | public Iterator iterator() { 37 | return new ArrayIterator(); 38 | } 39 | 40 | private class ArrayIterator implements Iterator { 41 | /** 42 | * Number of elements remaining in this iteration 43 | */ 44 | private int remaining = size; 45 | 46 | /** 47 | * Index of element that remove() would remove, or -1 if no such elt 48 | */ 49 | private int removalIndex = -1; 50 | 51 | @Override 52 | public boolean hasNext() { 53 | return remaining != 0; 54 | } 55 | 56 | @Override 57 | public Mileage next() { 58 | Mileage mileage = new Mileage(); 59 | removalIndex = size-remaining; 60 | mileage.name = String.valueOf(versionCodes[removalIndex]); 61 | mileage.value = versionMileages[removalIndex]; 62 | remaining-=1; 63 | return mileage; 64 | } 65 | 66 | @Override 67 | public void remove() { 68 | versionCodes[removalIndex]=-1; 69 | versionMileages[removalIndex]="It was set null"; 70 | } 71 | } 72 | ... 73 | ``` 74 | 使用的过程如下,我们特意使用了remove方法,注意这个只是一个示例,和大多数的集合相比,该实现并不严谨 75 | 76 | ``` 77 | AndroidMileage androidMileage = new AndroidMileage(); 78 | Iterator iterator =androidMileage.iterator(); 79 | while (iterator.hasNext()){ 80 | AndroidMileage.Mileage mileage = iterator.next(); 81 | if(mileage.name.equalsIgnoreCase("16")){ 82 | //remove掉的是当前的这个,暂时只是置空,并未真的移掉 83 | iterator.remove(); 84 | } 85 | Log.e("mileage",mileage.toString()); 86 | } 87 | ``` 88 | 89 | 下面直接写出几种集合的遍历方式,大家可以对比一下 90 | 91 | * HashMap的遍历 92 | ``` 93 | HashMap colorMap=new HashMap<>(); 94 | colorMap.put("Color1","Red"); 95 | colorMap.put("Color2","Blue"); 96 | Iterator iterator = colorMap.keySet().iterator(); 97 | while( iterator. hasNext() ){ 98 | String key = (String) iterator.next(); 99 | String value = colorMap.get(key); 100 | } 101 | ``` 102 | * JSONObject的遍历 103 | ``` 104 | String paramString = "{menu:{\"1\":\"sql\", \"2\":\"android\", \"3\":\"mvc\"}}"; 105 | JSONObject menuJSONObj = new JSONObject(paramString); 106 | JSONObject menuObj = menuJSONObj.getJSONObject("menu"); 107 | Iterator iter = menuObj.keys(); 108 | while(iter.hasNext()){ 109 | String key = (String)iter.next(); 110 | String value = menuObj.getString(key); 111 | } 112 | ``` 113 | 就目前而言,各种高级语言都有对迭代器的基本实现,没必要自己实现迭代器,使用内置的迭代器即可满足日常需求。 114 | 115 | ## Android源码中的模式实现 116 | 一个集合想要实现Iterator很是很简单的,需要注意的是每次需要重新生成一个Iterator来进行遍历.且遍历过程是单方向的,HashMap是通过一个类似HashIterator来实现的,我们为了解释简单,这里只是研究ArrayList(此处以Android L源码为例,其他版本略有不同) 117 | 118 | ``` 119 | @Override public Iterator iterator() { 120 | return new ArrayListIterator(); 121 | } 122 | 123 | private class ArrayListIterator implements Iterator { 124 | /** Number of elements remaining in this iteration */ 125 | private int remaining = size; 126 | 127 | /** Index of element that remove() would remove, or -1 if no such elt */ 128 | private int removalIndex = -1; 129 | 130 | /** The expected modCount value */ 131 | private int expectedModCount = modCount; 132 | 133 | public boolean hasNext() { 134 | return remaining != 0; 135 | } 136 | 137 | @SuppressWarnings("unchecked") public E next() { 138 | ArrayList ourList = ArrayList.this; 139 | int rem = remaining; 140 | if (ourList.modCount != expectedModCount) { 141 | throw new ConcurrentModificationException(); 142 | } 143 | if (rem == 0) { 144 | throw new NoSuchElementException(); 145 | } 146 | remaining = rem - 1; 147 | return (E) ourList.array[removalIndex = ourList.size - rem]; 148 | } 149 | 150 | public void remove() { 151 | Object[] a = array; 152 | int removalIdx = removalIndex; 153 | if (modCount != expectedModCount) { 154 | throw new ConcurrentModificationException(); 155 | } 156 | if (removalIdx < 0) { 157 | throw new IllegalStateException(); 158 | } 159 | System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining); 160 | a[--size] = null; // Prevent memory leak 161 | removalIndex = -1; 162 | expectedModCount = ++modCount; 163 | } 164 | } 165 | ``` 166 | 167 | * java中的写法一般都是通过iterator()来生成Iterator,保证iterator()每次生成新的实例 168 | * remaining初始化使用整个list的size大小,removalIndex表示remove掉的位置,modCount在集合大小发生变化的时候后都会进行一次modCount++操作,避免数据不一致,前面我写的例子这方面没有写,请务必注意这点 169 | * hasNext方法中,因为remaining是一个size->0的变化过程,这样只需要判断非0就可以得知当前遍历的是否还有未完成的元素 170 | * next,第一次调用的时候返回array[0]的元素,这个过程中removalIndex会被设置成当前array的index 171 | * remove的实现是直接操作的内存中的数据,是能够直接删掉元素的,不展开了 172 | 173 | 174 | 175 | ## 4. 杂谈 176 | ### 优点与缺点 177 | #### 优点 178 | * 面向对象设计原则中的单一职责原则,对于不同的功能,我们要尽可能的把这个功能分解出单一的职责,不同的类去承担不同的职责。Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。 179 | 180 | #### 缺点 181 | * 会产生多余的对象,消耗内存; 182 | * 遍历过程是一个单向且不可逆的遍历 183 | * 如果你在遍历的过程中,集合发生改变,变多变少,内容变化都是算,就会抛出来ConcurrentModificationException异常. 184 | 185 | 186 | -------------------------------------------------------------------------------- /iterator/haoxiqiang/resource/AndroidMileage.java: -------------------------------------------------------------------------------- 1 | package com.yuexue.tifenapp.wxapi; 2 | 3 | import android.os.Build; 4 | 5 | import org.apache.http.NameValuePair; 6 | 7 | import java.util.Iterator; 8 | 9 | /** 10 | * Created by haoxiqiang on 15/3/13. 11 | */ 12 | public class AndroidMileage { 13 | 14 | final static int[] versionCodes; 15 | final static String[] versionMileages; 16 | final static int size; 17 | 18 | static { 19 | versionCodes = new int[]{ 20 | Build.VERSION_CODES.BASE, Build.VERSION_CODES.BASE_1_1, Build.VERSION_CODES.CUPCAKE, 21 | Build.VERSION_CODES.DONUT, Build.VERSION_CODES.ECLAIR, Build.VERSION_CODES.ECLAIR_0_1, 22 | Build.VERSION_CODES.ECLAIR_MR1, Build.VERSION_CODES.FROYO, Build.VERSION_CODES.GINGERBREAD, 23 | Build.VERSION_CODES.GINGERBREAD_MR1, Build.VERSION_CODES.HONEYCOMB, Build.VERSION_CODES.HONEYCOMB_MR1, 24 | Build.VERSION_CODES.HONEYCOMB_MR2, Build.VERSION_CODES.ICE_CREAM_SANDWICH, Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1, 25 | Build.VERSION_CODES.JELLY_BEAN, Build.VERSION_CODES.JELLY_BEAN_MR1, Build.VERSION_CODES.JELLY_BEAN_MR2, 26 | Build.VERSION_CODES.KITKAT, Build.VERSION_CODES.KITKAT_WATCH, Build.VERSION_CODES.LOLLIPOP 27 | }; 28 | versionMileages = new String[]{ 29 | "October 2008: The original, first, version of Android. Yay!", 30 | "February 2009: First Android update, officially called 1.1.", 31 | " May 2009: Android 1.5.", 32 | "September 2009: Android 1.6.", 33 | "November 2009: Android 2.0", 34 | "December 2009: Android 2.0.1", 35 | "January 2010: Android 2.1", 36 | "June 2010: Android 2.2", 37 | "November 2010: Android 2.3", 38 | "February 2011: Android 2.3.3.", 39 | "February 2011: Android 3.0.", 40 | "May 2011: Android 3.1.", 41 | "June 2011: Android 3.2.", 42 | "October 2011: Android 4.0.", 43 | "December 2011: Android 4.0.3.", 44 | "June 2012: Android 4.1.", 45 | "November 2012: Android 4.2, Moar jelly beans!", 46 | "July 2013: Android 4.3, the revenge of the beans.", 47 | "October 2013: Android 4.4, KitKat, another tasty treat.", 48 | "Android 4.4W: KitKat for watches, snacks on the run.", 49 | "Lollipop. A flat one with beautiful shadows. But still tasty.", 50 | }; 51 | 52 | size = Math.min(versionCodes.length, versionMileages.length); 53 | } 54 | 55 | public static class Mileage implements NameValuePair { 56 | 57 | public String name; 58 | public String value; 59 | 60 | @Override 61 | public String getName() { 62 | return name; 63 | } 64 | 65 | @Override 66 | public String getValue() { 67 | return value; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "versionCode:" + name + " desc:" + value; 73 | } 74 | } 75 | 76 | 77 | public Iterator iterator() { 78 | return new ArrayIterator(); 79 | } 80 | 81 | private class ArrayIterator implements Iterator { 82 | /** 83 | * Number of elements remaining in this iteration 84 | */ 85 | private int remaining = size; 86 | 87 | /** 88 | * Index of element that remove() would remove, or -1 if no such elt 89 | */ 90 | private int removalIndex = -1; 91 | 92 | @Override 93 | public boolean hasNext() { 94 | return remaining != 0; 95 | } 96 | 97 | @Override 98 | public Mileage next() { 99 | Mileage mileage = new Mileage(); 100 | removalIndex = size-remaining; 101 | mileage.name = String.valueOf(versionCodes[removalIndex]); 102 | mileage.value = versionMileages[removalIndex]; 103 | remaining-=1; 104 | return mileage; 105 | } 106 | 107 | @Override 108 | public void remove() { 109 | versionCodes[removalIndex]=-1; 110 | versionMileages[removalIndex]="It was set null"; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /mediator/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [用户名](git地址) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /memento/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [用户名](git地址) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /observer/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/observer/.DS_Store -------------------------------------------------------------------------------- /observer/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [Mr.Simple](https://github.com/bboyfeiyu) | 2015.3.3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /oop-principles/oop-principles.md: -------------------------------------------------------------------------------- 1 | 公共技术点之面向对象六大原则 2 | --------- 3 | 4 | ## 概述 5 | 在工作初期,我们可能会经常会有这样的感觉,自己的代码接口设计混乱、代码耦合较为严重、一个类的代码过多等等,自己回头看的时候都觉得汗颜。再看那些知名的开源库,它们大多有着整洁的代码、清晰简单的接口、职责单一的类,这个时候我们通常会捶胸顿足而感叹:什么时候老夫才能写出这样的代码! 6 | 7 | 在做开发的这些年中,我渐渐的感觉到,其实国内的一些初、中级工程师写的东西不规范或者说不够清晰的原因是缺乏一些指导原则。他们手中挥舞着面向对象的大旗,写出来的东西却充斥着面向过程的气味。也许是他们不知道有这些原则,也许是他们知道但是不能很好运用到实际代码中,亦或是他们没有在实战项目中体会到这些原则能够带来的优点,以至于他们对这些原则并没有足够的重视。 8 | 9 | 今天,我们就是以剖析优秀的Android网络框架Volley为例来学习这六大面向对象的基本原则,体会它们带来的强大能量。 10 | 11 | 在此之前,有一点需要大家知道,熟悉这些原则不会让你写出优秀的代码,只是为你的优秀代码之路铺上了一层栅栏,在这些原则的指导下你才能避免陷入一些常见的代码泥沼,从而让你专心写出优秀的东西。另外,我是个新人,以下只是我个人的观点。如果你觉得还行,可以顶个帖支持一下;如果你觉得它烂透了,那请分享你的经验。 12 | 13 | 14 | ## 一、单一职责原则 ( Single Responsibility Principle ) 15 | 16 | ### 1.1 简述 17 | 单一职责原则的英文名称是Single Responsibility Principle,简称是SRP,简单来说一个类只做一件事。这个设计原则备受争议却又及其重要的原则。只要你想和别人争执、怄气或者是吵架,这个原则是屡试不爽的。因为单一职责的划分界限并不是如马路上的行车道那么清晰,很多时候都是需要靠个人经验来界定。当然最大的问题就是对职责的定义,什么是类的职责,以及怎么划分类的职责。 18 | 19 | 试想一下,如果你遵守了这个原则,那么你的类就会划分得很细,每个类都有自己的职责。恩,这不就是高内聚、低耦合么! 当然,如何界定类的职责这需要你的个人经验了。 20 | 21 | 22 | ### 1.2 示例 23 | 24 | 在Volley中,我觉得很能够体现SRP原则的就是HttpStack这个类族了。HttpStack定义了一个执行网络请求的接口,代码如下 : 25 | 26 | ```java 27 | /** 28 | * An HTTP stack abstraction. 29 | */ 30 | public interface HttpStack { 31 | /** 32 | * 执行Http请求,并且返回一个HttpResponse 33 | */ 34 | public HttpResponse performRequest(Request request, Map additionalHeaders) 35 | throws IOException, AuthFailureError; 36 | 37 | } 38 | ``` 39 | 可以看到,HttpStack只有一个函数,清晰明了,它的职责就是执行网络请求并且返回一个Response。它的职责很单一,这样在需要修改执行网络请求的相关代码时我们只需要修改实现HttpStack接口的类,而不会影响其它的类的代码。如果某个类的职责包含有执行网络请求、解析网络请求、进行gzip压缩、封装请求参数等等,那么在你修改某处代码时你就必须谨慎,以免修改的代码影响了其它的功能。但是当职责单一的时候,你修改的代码能够基本上不影响其它的功能。这就在一定程度上保证了代码的可维护性。**注意,单一职责原则并不是说一个类只有一个函数,而是说这个类中的函数所做的工作必须要是高度相关的,也就是高内聚**。HttpStack抽象了执行网络请求的具体过程,接口简单清晰,也便于扩展。 40 | 41 | 我们知道,Api 9以下使用HttpClient执行网络请求会好一些,api 9及其以上则建议使用HttpURLConnection。这就需要执行网络请求的具体实现能够可扩展、可替换,因此我们对于执行网络请求这个功能必须要抽象出来,HttpStack就是这个职责的抽象。 42 | 43 | 44 | ### 1.3 优点 45 | * 类的复杂性降低,实现什么职责都有清晰明确的定义; 46 | * 可读性提高,复杂性降低,那当然可读性提高了; 47 | * 可维护性提高,可读性提高,那当然更容易维护了; 48 | * 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。 49 | 50 | 51 | 52 | 53 | ## 里氏替换原则 ( Liskov Substitution Principle) 54 | 55 | ### 2.1 简述 56 | 面向对象的语言的三大特点是继承、封装、多态,里氏替换原则就是依赖于继承、多态这两大特性。里氏替换原则简单来说就是所有引用基类的地方必须能透明地使用其子类的对象。通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。 57 | 58 | 59 | ### 2.2 示例 60 | 还是以HttpStack为例,Volley定义了HttpStack来表示执行网络请求这个抽象概念。在执行网络请求时,我们只需要定义一个HttpStack对象,然后调用performRequest即可。至于HttpStack的具体实现由更高层的调用者给出。示例如下 : 61 | 62 | ```java 63 | 64 | public static RequestQueue newRequestQueue(Context context, HttpStack stack) { 65 | File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); 66 | 67 | String userAgent = "volley/0"; 68 | // 代码省略 69 | // 1、构造HttpStack对象 70 | if (stack == null) { 71 | if (Build.VERSION.SDK_INT >= 9) { 72 | stack = new HurlStack(); 73 | } else { 74 | // Prior to Gingerbread, HttpUrlConnection was unreliable. 75 | // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html 76 | stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); 77 | } 78 | } 79 | // 2、将HttpStack对象传递给Network对象 80 | Network network = new BasicNetwork(stack); 81 | // 3、将network对象传递给网络请求队列 82 | RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); 83 | queue.start(); 84 | 85 | return queue; 86 | } 87 | ``` 88 | 89 | BasicNetwork的代码如下: 90 | 91 | ```java 92 | /** 93 | * A network performing Volley requests over an {@link HttpStack}. 94 | */ 95 | public class BasicNetwork implements Network { 96 | // HttpStack抽象对象 97 | protected final HttpStack mHttpStack; 98 | 99 | protected final ByteArrayPool mPool; 100 | 101 | public BasicNetwork(HttpStack httpStack) { 102 | 103 | this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE)); 104 | } 105 | 106 | 107 | public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) { 108 | mHttpStack = httpStack; 109 | mPool = pool; 110 | } 111 | } 112 | ``` 113 | 114 | 上述代码中,BasicNetwork构造函数依赖的是HttpStack抽象接口,任何实现了HttpStack接口的类型都可以作为参数传递给BasicNetwork用以执行网络请求。这就是所谓的里氏替换原则,任何父类出现的地方子类都可以出现,这不就保证了可扩展性吗?! 此时,用手撑着你的小脑门作思考状... 任何实现HttpStack接口的类的对象都可以传递给BasicNetwork实现网络请求的功能,这样Volley执行网络请求的方法就有很多种可能性,而不是只有HttpClient和HttpURLConnection。 115 | 116 | 喔,原来是这样!此时我们放下装逼的模样,呈现一副若有所得的样子。细想一下,框架可不就是这样吗? 框架不就是定义一系列相关的逻辑骨架与抽象,使得用户可以将自己的实现传递进给框架,从而实现变化万千的功能。 117 | 118 | ### 2.3 优点 119 | * 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性; 120 | * 提高代码的重用性; 121 | * 提高代码的可扩展性,实现父类的方法就可以“为所欲为”了,很多开源框架的扩展接口都是通过继承父类来完成的; 122 | * 提高产品或项目的开放性。 123 | 124 | ### 2.4 缺点 125 | * 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法; 126 | * 降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束; 127 | * 增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大片的代码需要重构。 128 | 129 | 130 | ## 依赖倒置原则 (Dependence Inversion Principle) 131 | 132 | ### 3.1 简述 133 | 依赖倒置原则这个名字看着有点不好理解,“依赖”还要“倒置”,这到底是什么意思? 134 | 依赖倒置原则的几个关键点如下: 135 | 136 | * 高层模块不应该依赖低层模块,两者都应该依赖其抽象; 137 | * 抽象不应该依赖细节; 138 | * 细节应该依赖抽象。 139 | 140 | 在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化,也就是可以加上一个关键字 new 产生一个对象。依赖倒置原则在 Java 语言中的表现就是:**模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。**软件先驱们总是喜欢将一些理论定义得很抽象,弄得神不楞登的,其实就是一句话:面向接口编程,或者说是面向抽象编程,这里的抽象指的是接口或者抽象类。面向接口编程是面向对象精髓之一。 141 | 142 | 143 | ### 3.2 示例 144 | 采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。 145 | 第二章节中的BasicNetwork实现类依赖于HttpStack接口( 抽象 ),而不依赖于HttpClientStack与HurlStack实现类 ( 细节 ),这就是典型的依赖倒置原则的体现。假如BasicNetwork直接依赖了HttpClientStack,那么HurlStack就不能传递给了,除非HurlStack继承自HttpClientStack。但这么设计明显不符合逻辑,HurlStack与HttpClientStack并没有任何的is-a的关系,而且即使有也不能这么设计,因为HttpClientStack是一个具体类而不是抽象,如果HttpClientStack作为BasicNetwork构造函数的参数,那么以为这后续的扩展都需要继承自HttpClientStack。这简直是一件不可忍受的事了! 146 | 147 | 148 | ### 3.3 优点 149 | * 可扩展性好; 150 | * 耦合度低; 151 | 152 | 153 | 154 | ## 开闭原则 ( Open-Close Principle ) 155 | 156 | ### 4.1 简述 157 | 开闭原则是 Java 世界里最基础的设计原则,它指导我们如何建立一个稳定的、灵活的系统。开闭原则的定义是 : 一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误。因此当软件需要变化时,我们应该尽量通过扩展的方式来实现变化,而不是通过修改已有的代码来实现。 158 | 159 | 160 | ### 4.2 示例 161 | 162 | 在软件开发过程中,永远不变的就是变化。开闭原则是使我们的软件系统拥抱变化的核心原则之一。对扩展可放,对修改关闭给出了高层次的概括,即在需要对软件进行升级、变化时应该通过扩展的形式来实现,而非修改原有代码。当然这只是一种比较理想的状态,是通过扩展还是通过修改旧代码需要根据代码自身来定。 163 | 164 | 在Volley中,开闭原则体现得比较好的是Request类族的设计。我们知道,在开发C/S应用时,服务器返回的数据格式多种多样,有字符串类型、xml、json等。而解析服务器返回的Response的原始数据类型则是通过Request类来实现的,这样就使得Request类对于服务器返回的数据格式有良好的扩展性,即Request的可变性太大。 165 | 166 | 例如我们返回的数据格式是Json,那么我们使用JsonObjectRequest请求来获取数据,它会将结果转成JsonObject对象,我们看看JsonObjectRequest的核心实现。 167 | 168 | ```java 169 | /** 170 | * A request for retrieving a {@link JSONObject} response body at a given URL, allowing for an 171 | * optional {@link JSONObject} to be passed in as part of the request body. 172 | */ 173 | public class JsonObjectRequest extends JsonRequest { 174 | 175 | // 代码省略 176 | @Override 177 | protected Response parseNetworkResponse(NetworkResponse response) { 178 | try { 179 | String jsonString = 180 | new String(response.data, HttpHeaderParser.parseCharset(response.headers)); 181 | return Response.success(new JSONObject(jsonString), 182 | HttpHeaderParser.parseCacheHeaders(response)); 183 | } catch (UnsupportedEncodingException e) { 184 | return Response.error(new ParseError(e)); 185 | } catch (JSONException je) { 186 | return Response.error(new ParseError(je)); 187 | } 188 | } 189 | } 190 | ``` 191 | JsonObjectRequest通过实现Request抽象类的parseNetworkResponse解析服务器返回的结果,这里将结果转换为JSONObject,并且封装到Response类中。 192 | 193 | 例如Volley添加对图片请求的支持,即ImageLoader( 已内置 )。这个时候我的请求返回的数据是Bitmap图片。因此我需要在该类型的Request得到的结果是Request,但支持一种数据格式不能通过修改源码的形式,这样可能会为旧代码引入错误。但是你又需要支持新的数据格式,此时我们的开闭原则就很重要了,对扩展开放,对修改关闭。我们看看Volley是如何做的。 194 | 195 | ```java 196 | 197 | /** 198 | * A canned request for getting an image at a given URL and calling 199 | * back with a decoded Bitmap. 200 | */ 201 | public class ImageRequest extends Request { 202 | // 代码省略 203 | 204 | // 将结果解析成Bitmap,并且封装套Response对象中 205 | @Override 206 | protected Response parseNetworkResponse(NetworkResponse response) { 207 | // Serialize all decode on a global lock to reduce concurrent heap usage. 208 | synchronized (sDecodeLock) { 209 | try { 210 | return doParse(response); 211 | } catch (OutOfMemoryError e) { 212 | VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl()); 213 | return Response.error(new ParseError(e)); 214 | } 215 | } 216 | } 217 | 218 | /** 219 | * The real guts of parseNetworkResponse. Broken out for readability. 220 | */ 221 | private Response doParse(NetworkResponse response) { 222 | byte[] data = response.data; 223 | BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); 224 | Bitmap bitmap = null; 225 | if (mMaxWidth == 0 && mMaxHeight == 0) { 226 | decodeOptions.inPreferredConfig = mDecodeConfig; 227 | bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 228 | } else { 229 | // If we have to resize this image, first get the natural bounds. 230 | decodeOptions.inJustDecodeBounds = true; 231 | BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 232 | int actualWidth = decodeOptions.outWidth; 233 | int actualHeight = decodeOptions.outHeight; 234 | 235 | // Then compute the dimensions we would ideally like to decode to. 236 | int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, 237 | actualWidth, actualHeight); 238 | int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, 239 | actualHeight, actualWidth); 240 | 241 | // Decode to the nearest power of two scaling factor. 242 | decodeOptions.inJustDecodeBounds = false; 243 | // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it? 244 | // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED; 245 | decodeOptions.inSampleSize = 246 | findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); 247 | Bitmap tempBitmap = 248 | BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 249 | 250 | // If necessary, scale down to the maximal acceptable size. 251 | if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || 252 | tempBitmap.getHeight() > desiredHeight)) { 253 | bitmap = Bitmap.createScaledBitmap(tempBitmap, 254 | desiredWidth, desiredHeight, true); 255 | tempBitmap.recycle(); 256 | } else { 257 | bitmap = tempBitmap; 258 | } 259 | } 260 | 261 | if (bitmap == null) { 262 | return Response.error(new ParseError(response)); 263 | } else { 264 | return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response)); 265 | } 266 | } 267 | } 268 | ``` 269 | 需要添加某种数据格式的Request时,只需要继承自Request类,并且实现相应的方法即可。这样通过扩展的形式来应对软件的变化或者说用户需求的多样性,即避免了破坏原有系统,又保证了软件系统的可扩展性。 270 | 271 | ### 4.3 优点 272 | * 增加稳定性; 273 | * 可扩展性高。 274 | 275 | 276 | ## 接口隔离原则 (Interface Segregation Principle) 277 | 278 | ### 5.1 简述 279 | 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。 280 | 281 | 可能描述起来不是很好理解,我们还是以示例来加强理解吧。 282 | 283 | 284 | ### 5.2 示例 285 | 我们知道,在Volley的网络队列中是会对请求进行排序的。Volley内部使用PriorityBlockingQueue来维护网络请求队列,PriorityBlockingQueue需要调用Request类的compareTo函数来进行排序。试想一下,PriorityBlockingQueue其实只需要调用Request类的排序方法就可以了,其他的接口它根本不需要,即PriorityBlockingQueue只需要compareTo这个接口,而这个compareTo方法就是我们上述所说的最小接口。当然compareTo这个方法并不是Volley本身定义的接口方法,而是Java中的Comparable接口,但我们这里只是为了学习本身,至于哪里定义的无关紧要。 286 | 287 | ```java 288 | public abstract class Request implements Comparable> { 289 | 290 | /** 291 | * 排序方法,PriorityBlockingQueue只需要调用元素的compareTo即可进行排序 292 | */ 293 | @Override 294 | public int compareTo(Request other) { 295 | Priority left = this.getPriority(); 296 | Priority right = other.getPriority(); 297 | 298 | // High-priority requests are "lesser" so they are sorted to the front. 299 | // Equal priorities are sorted by sequence number to provide FIFO ordering. 300 | return left == right ? 301 | this.mSequence - other.mSequence : 302 | right.ordinal() - left.ordinal(); 303 | } 304 | } 305 | ``` 306 | 307 | PriorityBlockingQueue类相关代码 : 308 | 309 | ```java 310 | 311 | public class PriorityBlockingQueue extends AbstractQueue 312 | implements BlockingQueue, java.io.Serializable { 313 | 314 | // 代码省略 315 | 316 | // 添加元素的时候进行排序 317 | public boolean offer(E e) { 318 | if (e == null) 319 | throw new NullPointerException(); 320 | final ReentrantLock lock = this.lock; 321 | lock.lock(); 322 | int n, cap; 323 | Object[] array; 324 | while ((n = size) >= (cap = (array = queue).length)) 325 | tryGrow(array, cap); 326 | try { 327 | Comparator cmp = comparator; 328 | // 没有设置Comparator则使用元素本身的compareTo方法进行排序 329 | if (cmp == null) 330 | siftUpComparable(n, e, array); 331 | else 332 | siftUpUsingComparator(n, e, array, cmp); 333 | size = n + 1; 334 | notEmpty.signal(); 335 | } finally { 336 | lock.unlock(); 337 | } 338 | return true; 339 | } 340 | 341 | private static void siftUpComparable(int k, T x, Object[] array) { 342 | Comparable key = (Comparable) x; 343 | while (k > 0) { 344 | int parent = (k - 1) >>> 1; 345 | Object e = array[parent]; 346 | // 调用元素的compareTo方法进行排序 347 | if (key.compareTo((T) e) >= 0) 348 | break; 349 | array[k] = e; 350 | k = parent; 351 | } 352 | array[k] = key; 353 | } 354 | } 355 | ``` 356 | 从PriorityBlockingQueue的代码可知,在元素排序时,PriorityBlockingQueue只需要知道元素是个Comparable对象即可,不需要知道这个对象是不是Request类以及这个类的其他接口。它只需要排序,因此我只要知道它是实现了Comparable接口的对象即可,Comparable就是它的最小接口,也是通过Comparable隔离了PriorityBlockingQueue类对Request类的其他方法的可见性。妙哉,妙哉! 357 | 358 | ### 5.3 优点 359 | * 降低耦合性; 360 | * 提升代码的可读性; 361 | * 隐藏实现细节。 362 | 363 | 364 | ## 迪米特原则 ( Law of Demeter ) 365 | ### 6.1 简述 366 | 迪米特法则也称为最少知识原则(Least Knowledge Principle),虽然名字不同,但描述的是同一个原则:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,这有点类似接口隔离原则中的最小接口的概念。类的内部如何实现、如何复杂都与调用者或者依赖者没关系,调用者或者依赖者只需要知道他需要的方法即可,其他的我一概不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。 367 | 368 | 迪米特法则还有一个英文解释是: Only talk to your immedate friends( 只与直接的朋友通信。)什么叫做直接的朋友呢?每个对象都必然会与其他对象有耦合关系,两个对象之间的耦合就成为朋友关系,这种关系的类型有很多,例如组合、聚合、依赖等。 369 | 370 | 371 | ### 6.2 示例 372 | 例如,Volley中的Response缓存接口的设计。 373 | 374 | ```java 375 | /** 376 | * An interface for a cache keyed by a String with a byte array as data. 377 | */ 378 | public interface Cache { 379 | /** 380 | * 获取缓存 381 | */ 382 | public Entry get(String key); 383 | 384 | /** 385 | * 添加一个缓存元素 386 | */ 387 | public void put(String key, Entry entry); 388 | 389 | /** 390 | * 初始化缓存 391 | */ 392 | public void initialize(); 393 | 394 | /** 395 | * 标识某个缓存过期 396 | */ 397 | public void invalidate(String key, boolean fullExpire); 398 | 399 | /** 400 | * 移除缓存 401 | */ 402 | public void remove(String key); 403 | 404 | /** 405 | * 清空缓存 406 | */ 407 | public void clear(); 408 | 409 | } 410 | ``` 411 | Cache接口定义了缓存类需要实现的最小接口,依赖缓存类的对象只需要知道这些接口即可。例如缓存的具体实现类DiskBasedCache,该缓存类将Response序列化到本地,这就需要操作File以及相关的类。代码如下 : 412 | 413 | ```java 414 | 415 | public class DiskBasedCache implements Cache { 416 | 417 | /** Map of the Key, CacheHeader pairs */ 418 | private final Map mEntries = 419 | new LinkedHashMap(16, .75f, true); 420 | 421 | /** The root directory to use for the cache. */ 422 | private final File mRootDirectory; 423 | 424 | public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { 425 | mRootDirectory = rootDirectory; 426 | mMaxCacheSizeInBytes = maxCacheSizeInBytes; 427 | } 428 | 429 | public DiskBasedCache(File rootDirectory) { 430 | this(rootDirectory, DEFAULT_DISK_USAGE_BYTES); 431 | } 432 | 433 | // 代码省略 434 | } 435 | ``` 436 | 在这里,Volley的直接朋友就是DiskBasedCache,间接朋友就是mRootDirectory、mEntries等。Volley只需要直接和Cache类交互即可,并不需要知道File、mEntries等对象的存在。这就是迪米特原则,尽量少的知道对象的信息,只与直接的朋友交互。 437 | 438 | 439 | ### 6.3 优点 440 | * 降低复杂度; 441 | * 降低耦合度; 442 | * 增加稳定性。 443 | 444 | 445 | ## 杂谈 446 | 面向对象六大原则在开发过程中极为重要,如果能够很好地将这些原则运用到项目中,再在一些合适的场景运用一些前人验证过的模式,那么开发出来的软件在一定程度上能够得到质量保证。其实稍微一想,这几大原则最终就化为这么几个关键词: 抽象、单一职责、最小化。那么在实际开发过程中如何权衡、实践这些原则,笔者也在不断地学习、摸索。我想学习任何的事物莫过于实践、经验与领悟,在这个过程中希望能够与大家分享知识、共同进步。 447 | -------------------------------------------------------------------------------- /prototype/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/prototype/.DS_Store -------------------------------------------------------------------------------- /prototype/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [用户名](git地址) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /prototype/mr.simple/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/prototype/mr.simple/.DS_Store -------------------------------------------------------------------------------- /prototype/mr.simple/images/prototype-uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/prototype/mr.simple/images/prototype-uml.png -------------------------------------------------------------------------------- /prototype/mr.simple/images/result-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/prototype/mr.simple/images/result-2.png -------------------------------------------------------------------------------- /prototype/mr.simple/images/result-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/prototype/mr.simple/images/result-3.png -------------------------------------------------------------------------------- /prototype/mr.simple/images/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/prototype/mr.simple/images/result.png -------------------------------------------------------------------------------- /prototype/mr.simple/images/sms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/prototype/mr.simple/images/sms.png -------------------------------------------------------------------------------- /prototype/mr.simple/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之原型模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 原型模式 分析 4 | > Android系统版本: 2.3 5 | > 分析者:[Mr.Simple](https://github.com/bboyfeiyu),分析状态:未完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:完成 6 | 7 | ## 1. 模式介绍 8 | 9 | ### 模式的定义 10 | 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。 11 | 12 | 13 | ### 模式的使用场景 14 | 1. 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗; 15 | 2. 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式; 16 | 3. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。 17 | 18 | 19 | ## 2. UML类图 20 | ![uml](images/prototype-uml.png) 21 | 22 | ### 角色介绍 23 | * Client : 客户端用户。 24 | * Prototype : 抽象类或者接口,声明具备clone能力。 25 | * ConcretePrototype : 具体的原型类. 26 | 27 | 28 | ## 3. 模式的简单实现 29 | ### 简单实现的介绍 30 | 31 | 下面我们以简单的文档拷贝为例来演示一下简单的原型模式模式。 32 | 33 | ### 实现源码 34 | 35 | ```java 36 | package com.dp.example.builder; 37 | 38 | 39 | package com.dp.example.prototype; 40 | 41 | import java.util.ArrayList; 42 | import java.util.List; 43 | 44 | /** 45 | * 文档类型, 扮演的是ConcretePrototype角色,而cloneable是代表prototype角色 46 | * 47 | * @author mrsimple 48 | */ 49 | public class WordDocument implements Cloneable { 50 | /** 51 | * 文本 52 | */ 53 | private String mText; 54 | /** 55 | * 图片名列表 56 | */ 57 | private ArrayList mImages = new ArrayList(); 58 | 59 | public WordDocument() { 60 | System.out.println("----------- WordDocument构造函数 -----------"); 61 | } 62 | 63 | /** 64 | * 克隆对象 65 | */ 66 | @Override 67 | protected WordDocument clone() { 68 | try { 69 | WordDocument doc = (WordDocument) super.clone(); 70 | doc.mText = this.mText; 71 | doc.mImages = this.mImages; 72 | return doc; 73 | } catch (Exception e) { 74 | } 75 | 76 | return null; 77 | } 78 | 79 | public String getText() { 80 | return mText; 81 | } 82 | 83 | public void setText(String mText) { 84 | this.mText = mText; 85 | } 86 | 87 | public List getImages() { 88 | return mImages; 89 | } 90 | 91 | /** 92 | * @param img 93 | */ 94 | public void addImage(String img) { 95 | this.mImages.add(img); 96 | } 97 | 98 | /** 99 | * 打印文档内容 100 | */ 101 | public void showDocument() { 102 | System.out.println("----------- Word Content Start -----------"); 103 | System.out.println("Text : " + mText); 104 | System.out.println("Images List: "); 105 | for (String imgName : mImages) { 106 | System.out.println("image name : " + imgName); 107 | } 108 | System.out.println("----------- Word Content End -----------"); 109 | } 110 | } 111 | ``` 112 | 113 | 通过WordDocument类模拟了word文档中的基本元素,即文字和图片。WordDocument的在该原型模式示例中扮演的角色为ConcretePrototype, 而Cloneable的角色则为Prototype。WordDocument实现了clone方法以实现对象克隆。下面我们看看Client端的使用 : 114 | 115 | ```java 116 | public class Client { 117 | public static void main(String[] args) { 118 | WordDocument originDoc = new WordDocument(); 119 | originDoc.setText("这是一篇文档"); 120 | originDoc.addImage("图片1"); 121 | originDoc.addImage("图片2"); 122 | originDoc.addImage("图片3"); 123 | originDoc.showDocument(); 124 | 125 | WordDocument doc2 = originDoc.clone(); 126 | doc2.showDocument(); 127 | 128 | doc2.setText("这是修改过的Doc2文本"); 129 | doc2.showDocument(); 130 | 131 | originDoc.showDocument(); 132 | } 133 | 134 | } 135 | ``` 136 | 输出结果如下 : 137 | ![result](images/result.png) 138 | 139 | 可以看到,doc2是通过originDoc.clone()创建的,并且doc2第一次输出的时候和originDoc输出是一样的。即doc2是originDoc的一份拷贝,他们的内容是一样的,而doc2修改了文本内容以后并不会影响originDoc的文本内容。需要注意的是通过clone拷贝对象的时候并不会执行构造函数! 140 | 141 | 142 | ### 浅拷贝和深拷贝 143 | 将main函数的内容修改为如下 : 144 | 145 | ```java 146 | public static void main(String[] args) { 147 | WordDocument originDoc = new WordDocument(); 148 | originDoc.setText("这是一篇文档"); 149 | originDoc.addImage("图片1"); 150 | originDoc.addImage("图片2"); 151 | originDoc.addImage("图片3"); 152 | originDoc.showDocument(); 153 | 154 | WordDocument doc2 = originDoc.clone(); 155 | 156 | doc2.showDocument(); 157 | 158 | doc2.setText("这是修改过的Doc2文本"); 159 | doc2.addImage("哈哈.jpg"); 160 | doc2.showDocument(); 161 | 162 | originDoc.showDocument(); 163 | } 164 | ``` 165 | 166 | 输出结果如下 : 167 | ![result](images/result-2.png) 168 | 细心的朋友可能发现了,在doc2添加了一张名为"哈哈.jpg"的照片,但是却也显示在originDoc中?这是怎么回事呢? 其实学习过C++的朋友都知道,这是因为上文中WordDocument的clone方法中只是简单的进行浅拷贝,引用类型的新对象doc2的mImages只是单纯的指向了this.mImages引用,而并没有进行拷贝。doc2的mImages添加了新的图片,实际上也就是往originDoc里添加了新的图片,所以originDoc里面也有"哈哈.jpg" 。那如何解决这个问题呢? 那就是采用深拷贝,即在拷贝对象时,对于引用型的字段也要采用拷贝的形式,而不是单纯引用的形式。示例如下 : 169 | 170 | ```java 171 | /** 172 | * 克隆对象 173 | */ 174 | @Override 175 | protected WordDocument clone() { 176 | try { 177 | WordDocument doc = (WordDocument) super.clone(); 178 | doc.mText = this.mText; 179 | // doc.mImages = this.mImages; 180 | doc.mImages = (ArrayList) this.mImages.clone(); 181 | return doc; 182 | } catch (Exception e) { 183 | } 184 | 185 | return null; 186 | } 187 | ``` 188 | 189 | 如上代码所示,将doc.mImages指向this.mImages的一份拷贝, 而不是this.mImages本身,这样在doc2添加图片时并不会影响originDoc,如图所示 : 190 | ![result](images/result-3.png) 191 | 192 | 193 | ## Android源码中的模式实现 194 | 在Android源码中,我们以熟悉的Intent来分析源码中的原型模式。简单示例如下 : 195 | 196 | ```java 197 | Uri uri = Uri.parse("smsto:0800000123"); 198 | Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri); 199 | shareIntent.putExtra("sms_body", "The SMS text"); 200 | 201 | Intent intent = (Intent)shareIntent.clone() ; 202 | startActivity(intent); 203 | ``` 204 | 205 | 结果如下 : 206 | 207 | ![result](images/sms.png) 208 | 209 | 可以看到,我们通过shareIntent.clone方法拷贝了一个对象intent, 然后执行startActivity(intent), 随即就进入了短信页面,号码为0800000123,文本内容为The SMS text,即这些内容都与shareIntent一致。 210 | 211 | ```java 212 | @Override 213 | public Object clone() { 214 | return new Intent(this); 215 | } 216 | 217 | /** 218 | * Copy constructor. 219 | */ 220 | public Intent(Intent o) { 221 | this.mAction = o.mAction; 222 | this.mData = o.mData; 223 | this.mType = o.mType; 224 | this.mPackage = o.mPackage; 225 | this.mComponent = o.mComponent; 226 | this.mFlags = o.mFlags; 227 | if (o.mCategories != null) { 228 | this.mCategories = new ArraySet(o.mCategories); 229 | } 230 | if (o.mExtras != null) { 231 | this.mExtras = new Bundle(o.mExtras); 232 | } 233 | if (o.mSourceBounds != null) { 234 | this.mSourceBounds = new Rect(o.mSourceBounds); 235 | } 236 | if (o.mSelector != null) { 237 | this.mSelector = new Intent(o.mSelector); 238 | } 239 | if (o.mClipData != null) { 240 | this.mClipData = new ClipData(o.mClipData); 241 | } 242 | } 243 | ``` 244 | 245 | 可以看到,clone方法实际上在内部调用了new Intent(this); 这就和C++中的拷贝构造函数完全一致了,而且是深拷贝。由于该模式比较简单,就不做太多说明。 246 | 247 | 248 | ## 4. 杂谈 249 | ### 优点与缺点 250 | * 优点 251 | 原型模式是在内存二进制流的拷贝,要比直接 new 一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。 252 | 253 | * 缺点 254 | 这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发当中应该注意这个潜在的问题。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。 255 | 256 | -------------------------------------------------------------------------------- /proxy/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/proxy/.DS_Store -------------------------------------------------------------------------------- /proxy/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [Mr.Simple](https://github.com/bboyfeiyu) | 2015.3.5 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /proxy/singwhatiwanna/README.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之Proxy模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 Proxy模式 分析 4 | > Android系统版本: 5.0 5 | > 分析者:[singwhatiwanna](https://github.com/singwhatiwanna),分析状态:完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:未校对 6 | 7 | #Binder中的代理模式 8 | 9 | 再说Binder中的代理模式之前,我们需要先看看代理模式的简单实现,这一部分内容采用了[《JAVA与模式》之代理模式](http://www.cnblogs.com/java-my-life/archive/2012/04/23/2466712.html)这篇文章中的代码示例和uml类图。 10 | 11 | ## 1. 模式介绍 12 | 代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。 13 | 14 | ### 模式的使用场景 15 | 就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 16 | 17 | ## 2. UML类图 18 | ![url](images/proxy-uml.png) 19 | 20 | ### 角色介绍 21 | * 抽象对象角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。 22 | 23 | * 目标对象角色:定义了代理对象所代表的目标对象。 24 | 25 | * 代理对象角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。 26 | 27 | ## 3. 模式的简单实现 28 | ### 简单实现的介绍 29 | 下面通过一种抽象的方式来实现下代理模式 30 | 31 | ### 实现源码 32 | 抽象对象角色 33 | 34 | ``` 35 | public abstract class AbstractObject { 36 | //操作 37 | public abstract void operation(); 38 | } 39 | ``` 40 | 41 | 目标对象角色 42 | ``` 43 | public class RealObject extends AbstractObject { 44 | @Override 45 | public void operation() { 46 | //一些操作 47 | System.out.println("一些操作"); 48 | } 49 | } 50 | ``` 51 | 52 | 代理对象角色 53 | ``` 54 | public class ProxyObject extends AbstractObject{ 55 | RealObject realObject = new RealObject(); 56 | @Override 57 | public void operation() { 58 | //调用目标对象之前可以做相关操作 59 | System.out.println("before"); 60 | realObject.operation(); 61 | //调用目标对象之后可以做相关操作 62 | System.out.println("after"); 63 | } 64 | } 65 | ``` 66 | 67 | 客户端 68 | ``` 69 | public class Client { 70 | public static void main(String[] args) { 71 | AbstractObject obj = new ProxyObject(); 72 | obj.operation(); 73 | } 74 | } 75 | ``` 76 | ## 4. 代理模式在Binder中的使用 77 | 直观来说,Binder是Android中的一个类,它继承了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在linux中没有;从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,etc)和相应ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。 78 | 79 | Binder一个很重要的作用是:将客户端的请求参数通过Parcel包装后传到远程服务端,远程服务端解析数据并执行对应的操作,同时客户端线程挂起,当服务端方法执行完毕后,再将返回结果写入到另外一个Parcel中并将其通过Binder传回到客户端,客户端接收到返回数据的Parcel后,Binder会解析数据包中的内容并将原始结果返回给客户端,至此,整个Binder的工作过程就完成了。由此可见,Binder更像一个数据通道,Parcel对象就在这个通道中跨进程传输,至于双方如何通信,这并不负责,只需要双方按照约定好的规范去打包和解包数据即可。 80 | 81 | 为了更好地说明Binder,这里我们先手动实现了一个Binder。为了使得逻辑更清晰,这里简化一下,我们来模拟一个银行系统,这个银行提供的功能只有一个:即查询余额,只有传递一个int的id过来,银行就会将你的余额设置为id*10,满足下大家的发财梦。 82 | 83 | 1. 先定义一个Binder接口 84 | ``` 85 | package com.ryg.design.manualbinder; 86 | 87 | import android.os.IBinder; 88 | import android.os.IInterface; 89 | import android.os.RemoteException; 90 | 91 | public interface IBank extends IInterface { 92 | 93 | static final String DESCRIPTOR = "com.ryg.design.manualbinder.IBank"; 94 | 95 | static final int TRANSACTION_queryMoney = (IBinder.FIRST_CALL_TRANSACTION + 0); 96 | 97 | public long queryMoney(int uid) throws RemoteException; 98 | 99 | } 100 | ``` 101 | 102 | 2.创建一个Binder并实现这个上述接口 103 | ``` 104 | package com.ryg.design.manualbinder; 105 | 106 | import android.os.Binder; 107 | import android.os.IBinder; 108 | import android.os.Parcel; 109 | import android.os.RemoteException; 110 | 111 | public class BankImpl extends Binder implements IBank { 112 | 113 | public BankImpl() { 114 | this.attachInterface(this, DESCRIPTOR); 115 | } 116 | 117 | public static IBank asInterface(IBinder obj) { 118 | if ((obj == null)) { 119 | return null; 120 | } 121 | android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 122 | if (((iin != null) && (iin instanceof IBank))) { 123 | return ((IBank) iin); 124 | } 125 | return new BankImpl.Proxy(obj); 126 | } 127 | 128 | @Override 129 | public IBinder asBinder() { 130 | return this; 131 | } 132 | 133 | @Override 134 | public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 135 | throws RemoteException { 136 | switch (code) { 137 | case INTERFACE_TRANSACTION: { 138 | reply.writeString(DESCRIPTOR); 139 | return true; 140 | } 141 | case TRANSACTION_queryMoney: { 142 | data.enforceInterface(DESCRIPTOR); 143 | int uid = data.readInt(); 144 | long result = this.queryMoney(uid); 145 | reply.writeNoException(); 146 | reply.writeLong(result); 147 | return true; 148 | } 149 | } 150 | return super.onTransact(code, data, reply, flags); 151 | } 152 | 153 | @Override 154 | public long queryMoney(int uid) throws RemoteException { 155 | return uid * 10l; 156 | } 157 | 158 | private static class Proxy implements IBank { 159 | private IBinder mRemote; 160 | 161 | Proxy(IBinder remote) { 162 | mRemote = remote; 163 | } 164 | 165 | @Override 166 | public IBinder asBinder() { 167 | return mRemote; 168 | } 169 | 170 | public java.lang.String getInterfaceDescriptor() { 171 | return DESCRIPTOR; 172 | } 173 | 174 | @Override 175 | public long queryMoney(int uid) throws RemoteException { 176 | Parcel data = Parcel.obtain(); 177 | Parcel reply = Parcel.obtain(); 178 | long result; 179 | try { 180 | data.writeInterfaceToken(DESCRIPTOR); 181 | data.writeInt(uid); 182 | mRemote.transact(TRANSACTION_queryMoney, data, reply, 0); 183 | reply.readException(); 184 | result = reply.readLong(); 185 | } finally { 186 | reply.recycle(); 187 | data.recycle(); 188 | } 189 | return result; 190 | } 191 | 192 | } 193 | 194 | } 195 | ``` 196 | ok,到此为止,我们的Binder就完成了,这里只要创建服务端和客户端,二者就能通过我们的Binder来通信了。这里就不做这个示例了,我们的目的是分析代理模式在Binder中的使用。 197 | 198 | 我们看上述Binder的实现中,有一个叫做“Proxy”的类,它的构造方法如下: 199 | ``` 200 | Proxy(IBinder remote) { 201 | mRemote = remote; 202 | } 203 | ``` 204 | Proxy类接收一个IBinder参数,这个参数实际上就是服务端Service中的onBind方法返回的Binder对象在客户端重新打包后的结果,因为客户端无法直接通过这个打包的Binder和服务端通信,因此客户端必须借助Proxy类来和服务端通信,这里Proxy的作用就是代理的作用,客户端所有的请求全部通过Proxy来代理,具体工作流程为:Proxy接收到客户端的请求后,会将客户端的请求参数打包到Parcel对象中,然后将Parcel对象通过它内部持有的Ibinder对象传送到服务端,服务端接收数据、执行方法后返回结果给客户端的Proxy,Proxy解析数据后返回给客户端的真正调用者。很显然,上述所分析的就是典型的代理模式。至于Binder如何传输数据,这涉及到很底层的知识,这个很难搞懂,但是数据传输的核心思想是共享内存。 205 | 206 | ## 5. 杂谈 207 | ### 优点与缺点 208 | #### 优点 209 | * 给对象增加了本地化的扩展性,增加了存取操作控制 210 | 211 | #### 缺点 212 | * 会产生多余的代理类 213 | -------------------------------------------------------------------------------- /proxy/singwhatiwanna/images/proxy-uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/proxy/singwhatiwanna/images/proxy-uml.png -------------------------------------------------------------------------------- /singleton/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/singleton/.DS_Store -------------------------------------------------------------------------------- /singleton/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 模式名称 | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| ------------- | 4 | | 单例模式 | [Mr.Simple](https://github.com/bboyfeiyu) | 2015.3.2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /singleton/mr.simple/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/singleton/mr.simple/.DS_Store -------------------------------------------------------------------------------- /singleton/mr.simple/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/singleton/mr.simple/images/.DS_Store -------------------------------------------------------------------------------- /singleton/mr.simple/images/singleton-mr.simple-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/singleton/mr.simple/images/singleton-mr.simple-result.png -------------------------------------------------------------------------------- /singleton/mr.simple/images/singleton-mr.simple-uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/singleton/mr.simple/images/singleton-mr.simple-uml.png -------------------------------------------------------------------------------- /singleton/mr.simple/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之单例模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 单例模式 分析 4 | > Android系统版本: 2.3 5 | > 分析者:[Mr.Simple](https://github.com/bboyfeiyu),分析状态:完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:完成 6 | 7 | 8 | ## 1. 模式介绍 9 | 10 | ### 模式的定义 11 | 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 12 | 13 | 14 | ### 模式的使用场景 15 | 确保某个类有且只有一个对象的场景,例如创建一个对象需要消耗的资源过多,如要访问 IO 和数据库等资源。 16 | 17 | 18 | ## 2. UML类图 19 | ![url](images/singleton-mr.simple-uml.png) 20 | 21 | ### 角色介绍 22 | * Client : 高层客户端。 23 | * Singleton : 单例类。 24 | 25 | 26 | 27 | ## 3. 模式的简单实现 28 | ### 简单实现的介绍 29 | 单例模式是设计模式中最简单的,只有一个单例类,没有其他的层次结构与抽象。该模式需要确保该类只能生成一个对象,通常是该类需要消耗太多的资源或者没有没有多个实例的理由。例如一个公司只有一个CEO、一台电脑通常只有一个显示器等。下面我们以公司里的CEO为例来简单演示一下,一个公司可以有几个VP,无数个员工,但是CEO只有一个,请看下面示例。 30 | 31 | ### 实现源码 32 | 33 | ```java 34 | package com.dp.example.singleton; 35 | /** 36 | * 人的基类 37 | * @author mrsimple 38 | * 39 | */ 40 | public abstract class Person { 41 | public abstract void talk() ; 42 | } 43 | 44 | // 普通员工 45 | public class Staff extends Person { 46 | 47 | @Override 48 | public void talk() { 49 | 50 | } 51 | 52 | } 53 | 54 | // 副总裁 55 | public class VP extends Person { 56 | 57 | @Override 58 | public void talk() { 59 | 60 | } 61 | } 62 | 63 | // CEO, 单例模式 64 | public class CEO extends Person { 65 | 66 | private static final CEO mCeo = new CEO(); 67 | 68 | private CEO() { 69 | } 70 | 71 | public static CEO getCeo() { 72 | return mCeo; 73 | } 74 | 75 | @Override 76 | public void talk() { 77 | System.out.println("CEO发表讲话"); 78 | } 79 | 80 | } 81 | 82 | // 公司类 83 | import java.util.ArrayList; 84 | import java.util.List; 85 | 86 | public class Company { 87 | private List allPersons = new ArrayList(); 88 | 89 | public void addStaff(Person per) { 90 | allPersons.add(per); 91 | } 92 | 93 | public void showAllStaffs() { 94 | for (Person per : allPersons) { 95 | System.out.println("Obj : " + per.toString()); 96 | } 97 | } 98 | } 99 | 100 | // test 101 | public class Test { 102 | public static void main(String[] args) { 103 | Company cp = new Company() ; 104 | Person ceo1 = CEO.getCeo() ; 105 | Person ceo2 = CEO.getCeo() ; 106 | cp.addStaff(ceo1); 107 | cp.addStaff(ceo2); 108 | 109 | Person vp1 = new VP() ; 110 | Person vp2 = new VP() ; 111 | 112 | Person staff1 = new Staff() ; 113 | Person staff2 = new Staff() ; 114 | Person staff3 = new Staff() ; 115 | 116 | cp.addStaff(vp1); 117 | cp.addStaff(vp2); 118 | cp.addStaff(staff1); 119 | cp.addStaff(staff2); 120 | cp.addStaff(staff3); 121 | 122 | cp.showAllStaffs(); 123 | } 124 | } 125 | ``` 126 | 127 | 输出结果如下 : 128 | ![result](images/singleton-mr.simple-result.png) 129 | 130 | 可以看到, CEO两次输出的CEO对象的文字描述都是一样的,而VP、Staff类的对象都是不同的。即CEO是唯一实例,而其他类型都是不同的实例。这个实现的核心在于将CEO类的构造方法私有化,使得外部程序不能通过构造函数来构造CEO对象,而CEO类通过一个静态方法返回一个唯一的对象。 131 | 132 | 133 | ### 单例模式的其他实现 134 | 135 | ```java 136 | package com.dp.example.singleton; 137 | 138 | public class Singleton { 139 | private static Singleton mInstance = null; 140 | 141 | private Singleton() { 142 | 143 | } 144 | 145 | public void doSomething() { 146 | System.out.println("do sth."); 147 | } 148 | 149 | /** 150 | * 方式二、double-check, 避免并发时创建了多个实例, 该方式不能完全避免并发带来的破坏. 151 | * 152 | * @return 153 | */ 154 | public static Singleton getInstance() { 155 | if (mInstance == null) { 156 | synchronized (Singleton.class) { 157 | if (mInstance == null) { 158 | mInstance = new Singleton(); 159 | } 160 | } 161 | } 162 | return mInstance; 163 | } 164 | 165 | /** 166 | * 方式三 : 在第一次加载SingletonHolder时初始化一次mOnlyInstance对象, 保证唯一性, 也延迟了单例的实例化, 167 | * 如果该单例比较耗资源可以使用这种模式. 168 | * 169 | * @return 170 | */ 171 | public static Singleton getInstanceFromHolder() { 172 | return SingletonHolder.mOnlyInstance; 173 | } 174 | 175 | /** 176 | * 静态内部类 177 | * 178 | * @author mrsimple 179 | * 180 | */ 181 | private static class SingletonHolder { 182 | private static final Singleton mOnlyInstance = new Singleton(); 183 | } 184 | 185 | /** 186 | * 方式四 : 枚举单例, 线程安全 187 | * @author mrsimple 188 | * 189 | */ 190 | enum SingletonEnum { 191 | INSTANCE; 192 | public void doSomething() { 193 | System.out.println("do sth."); 194 | } 195 | } 196 | 197 | /** 198 | * 方式五 : 注册到容器, 根据key获取对象.一般都会有多种相同属性类型的对象会注册到一个map中 199 | * instance容器 200 | */ 201 | private static Map objMap = new HashMap(); 202 | /** 203 | * 注册对象到map中 204 | * @param key 205 | * @param instance 206 | */ 207 | public static void registerService(String key, Singleton instance) { 208 | if (!objMap.containsKey(key) ) { 209 | objMap.put(key, instance) ; 210 | } 211 | } 212 | 213 | /** 214 | * 根据key获取对象 215 | * @param key 216 | * @return 217 | */ 218 | public static Singleton getService(String key) { 219 | return objMap.get(key) ; 220 | } 221 | 222 | } 223 | ``` 224 | 不管以哪种形式实现单例模式,它们的核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例,在这个获取的过程中你必须保证线程安全、反序列化导致重新生成实例对象等问题,该模式简单,但使用率较高。 225 | 226 | ## Android源码中的模式实现 227 | 在Android系统中,我们经常会通过Context获取系统级别的服务,比如WindowsManagerService, ActivityManagerService等,更常用的是一个叫LayoutInflater的类。这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过Context的getSystemService(String name)获取。我们以LayoutInflater为例来说明, 平时我们使用LayoutInflater较为常见的地方是在ListView的getView方法中。 228 | 229 | ```java 230 | @Override 231 | public View getView(int position, View convertView, ViewGroup parent) 232 | View itemView = null; 233 | if (convertView == null) { 234 | itemView = LayoutInflater.from(mContext).inflate(mLayoutId, null); 235 | // 其他代码 236 | } else { 237 | itemView = convertView; 238 | } 239 | // 获取Holder 240 | // 初始化每项的数据 241 | return itemView; 242 | } 243 | ``` 244 | 245 | 通常我们使用LayoutInflater.from(Context)来获取LayoutInflater服务, 下面我们看看LayoutInflater.from(Context)的实现。 246 | 247 | ``` 248 | /** 249 | * Obtains the LayoutInflater from the given context. 250 | */ 251 | public static LayoutInflater from(Context context) { 252 | LayoutInflater LayoutInflater = 253 | (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 254 | if (LayoutInflater == null) { 255 | throw new AssertionError("LayoutInflater not found."); 256 | } 257 | return LayoutInflater; 258 | } 259 | ``` 260 | 261 | 可以看到from(Context)函数内部调用的是Context类的getSystemService(String key)方法,我们跟踪到Context类看到, 该类是抽象类。 262 | 263 | ```java 264 | public abstract class Context { 265 | // 省略 266 | } 267 | ``` 268 | 269 | 使用的getView中使用的Context对象的具体实现类是什么呢 ?其实在Application,Activity, Service,中都会存在一个Context对象,即Context的总个数为Activity个数 + Service个数 + 1。而ListView通常都是显示在Activity中,那么我们就以Activity中的Context来分析。 270 | 271 | 我们知道,一个Activity的入口是ActivityThread的main函数。在该main函数中创建一个新的ActivityThread对象,并且启动消息循环(UI线程),创建新的Activity、新的Context对象,然后将该Context对象传递给Activity。下面我们看看ActivityThread源码。 272 | 273 | ```java 274 | public static void main(String[] args) { 275 | SamplingProfilerIntegration.start(); 276 | 277 | // CloseGuard defaults to true and can be quite spammy. We 278 | // disable it here, but selectively enable it later (via 279 | // StrictMode) on debug builds, but using DropBox, not logs. 280 | CloseGuard.setEnabled(false); 281 | 282 | Environment.initForCurrentUser(); 283 | 284 | // Set the reporter for event logging in libcore 285 | EventLogger.setReporter(new EventLoggingReporter()); 286 | Process.setArgV0(""); 287 | // 主线程消息循环 288 | Looper.prepareMainLooper(); 289 | // 创建ActivityThread对象 290 | ActivityThread thread = new ActivityThread(); 291 | thread.attach(false); 292 | 293 | if (sMainThreadHandler == null) { 294 | sMainThreadHandler = thread.getHandler(); 295 | } 296 | 297 | AsyncTask.init(); 298 | 299 | if (false) { 300 | Looper.myLooper().setMessageLogging(new 301 | LogPrinter(Log.DEBUG, "ActivityThread")); 302 | } 303 | 304 | Looper.loop(); 305 | 306 | throw new RuntimeException("Main thread loop unexpectedly exited"); 307 | } 308 | 309 | private void attach(boolean system) { 310 | sThreadLocal.set(this); 311 | mSystemThread = system; 312 | if (!system) { 313 | ViewRootImpl.addFirstDrawHandler(new Runnable() { 314 | public void run() { 315 | ensureJitEnabled(); 316 | } 317 | }); 318 | android.ddm.DdmHandleAppName.setAppName("", 319 | UserHandle.myUserId()); 320 | RuntimeInit.setApplicationObject(mAppThread.asBinder()); 321 | IActivityManager mgr = ActivityManagerNative.getDefault(); 322 | try { 323 | mgr.attachApplication(mAppThread); 324 | } catch (RemoteException ex) { 325 | // Ignore 326 | } 327 | } else { 328 | // 省略 329 | } 330 | } 331 | ``` 332 | 333 | 在main方法中,我们创建一个ActivityThread对象后,调用了其attach函数,并且参数为false. 在attach函数中, 参数为false的情况下, 会通过Binder机制与ActivityManagerService通信,并且最终调用handleLaunchActivity函数 ( 具体分析请参考老罗的博客 : [Activity的启动流程](http://blog.csdn.net/luoshengyang/article/details/6689748)),我们看看该函数的实现 。 334 | 335 | ```java 336 | 337 | private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { 338 | // 代码省略 339 | Activity a = performLaunchActivity(r, customIntent); 340 | // 代码省略 341 | } 342 | 343 | private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { 344 | // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); 345 | // 代码省略 346 | Activity activity = null; 347 | try { 348 | java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); 349 | activity = mInstrumentation.newActivity( // 1 : 创建Activity 350 | cl, component.getClassName(), r.intent); 351 | // 代码省略 352 | } catch (Exception e) { 353 | // 省略 354 | } 355 | 356 | try { 357 | Application app = r.packageInfo.makeApplication(false, mInstrumentation); 358 | 359 | if (activity != null) { 360 | Context appContext = createBaseContextForActivity(r, activity); // 2 : 获取Context对象 361 | CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); 362 | Configuration config = new Configuration(mCompatConfiguration); 363 | // 3: 将appContext等对象attach到activity中 364 | activity.attach(appContext, this, getInstrumentation(), r.token, 365 | r.ident, app, r.intent, r.activityInfo, title, r.parent, 366 | r.embeddedID, r.lastNonConfigurationInstances, config); 367 | 368 | // 代码省略 369 | // 4 : 调用Activity的onCreate方法 370 | mInstrumentation.callActivityOnCreate(activity, r.state); 371 | // 代码省略 372 | } catch (SuperNotCalledException e) { 373 | throw e; 374 | } catch (Exception e) { 375 | // 代码省略 376 | } 377 | 378 | return activity; 379 | } 380 | 381 | 382 | private Context createBaseContextForActivity(ActivityClientRecord r, 383 | final Activity activity) { 384 | // 5 : 创建Context对象, 可以看到实现类是ContextImpl 385 | ContextImpl appContext = new ContextImpl(); appContext.init(r.packageInfo, r.token, this); 386 | appContext.setOuterContext(activity); 387 | 388 | // 代码省略 389 | return baseContext; 390 | } 391 | 392 | ``` 393 | 394 | 通过上面1~5的代码分析可以知道, Context的实现类为ComtextImpl类。我们继续跟踪到ContextImpl类。 395 | 396 | ```java 397 | class ContextImpl extends Context { 398 | 399 | // 代码省略 400 | /** 401 | * Override this class when the system service constructor needs a 402 | * ContextImpl. Else, use StaticServiceFetcher below. 403 | */ 404 | static class ServiceFetcher { 405 | int mContextCacheIndex = -1; 406 | 407 | /** 408 | * Main entrypoint; only override if you don't need caching. 409 | */ 410 | public Object getService(ContextImpl ctx) { 411 | ArrayList cache = ctx.mServiceCache; 412 | Object service; 413 | synchronized (cache) { 414 | if (cache.size() == 0) { 415 | for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) { 416 | cache.add(null); 417 | } 418 | } else { 419 | service = cache.get(mContextCacheIndex); 420 | if (service != null) { 421 | return service; 422 | } 423 | } 424 | service = createService(ctx); 425 | cache.set(mContextCacheIndex, service); 426 | return service; 427 | } 428 | } 429 | 430 | /** 431 | * Override this to create a new per-Context instance of the 432 | * service. getService() will handle locking and caching. 433 | */ 434 | public Object createService(ContextImpl ctx) { 435 | throw new RuntimeException("Not implemented"); 436 | } 437 | } 438 | 439 | // 1 : service容器 440 | private static final HashMap SYSTEM_SERVICE_MAP = 441 | new HashMap(); 442 | 443 | private static int sNextPerContextServiceCacheIndex = 0; 444 | // 2: 注册服务器 445 | private static void registerService(String serviceName, ServiceFetcher fetcher) { 446 | if (!(fetcher instanceof StaticServiceFetcher)) { 447 | fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++; 448 | } 449 | SYSTEM_SERVICE_MAP.put(serviceName, fetcher); 450 | } 451 | 452 | 453 | // 3: 静态语句块, 第一次加载该类时执行 ( 只执行一次, 保证实例的唯一性. ) 454 | static { 455 | // 代码省略 456 | // 注册Activity Servicer 457 | registerService(ACTIVITY_SERVICE, new ServiceFetcher() { 458 | public Object createService(ContextImpl ctx) { 459 | return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler()); 460 | }}); 461 | 462 | // 注册LayoutInflater service 463 | registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { 464 | public Object createService(ContextImpl ctx) { 465 | return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); 466 | }}); 467 | // 代码省略 468 | } 469 | 470 | // 4: 根据key获取对应的服务, 471 | @Override 472 | public Object getSystemService(String name) { 473 | // 根据name来获取服务 474 | ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name); 475 | return fetcher == null ? null : fetcher.getService(this); 476 | } 477 | 478 | // 代码省略 479 | } 480 | 481 | ``` 482 | 483 | 从ContextImpl类的部分代码中可以看到,在虚拟机第一次加载该类时会注册各种服务,其中就包含了LayoutInflater Service, 将这些服务以键值对的形式存储在一个HashMap中,用户使用时只需要根据key来获取到对应的服务,从而达到单例的效果。这种模式就是上文中提到的“单例模式的实现方式5”。系统核心服务以单例形式存在,减少了资源消耗。 484 | 485 | 486 | ## 4. 杂谈 487 | ### 优点与缺点 488 | #### 优点 489 | * 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。 490 | * 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决; 491 | * 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。 492 | * 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。 493 | 494 | #### 缺点 495 | * 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。 496 | -------------------------------------------------------------------------------- /state/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [Thinan](https://www.github.com/Thinan) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /state/Thinan/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之${模式名} 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 ${模式名} 分析 4 | > Android系统版本: ${系统版本号,例如 4.2.1} 5 | > 分析者:[${分析者}](${分析者 Github 地址}),分析状态:未完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:未开始 6 | 7 | 8 | `复制一份到自己的项目文件夹下,然后根据自己项目替换掉 ${} 内容,删掉本行及上面两行。` 9 | 10 | 该任务不仅要用java写出该模式的一个简单示例,还有分析该模式在Android源码中的应用,可以参考[Mr.Simple的单例模式](singleton/mr.simple/readme.md)、[Mr.Simple的观察者模式](observer/mr.simple/readme.md)。 11 | 12 | 13 | ## 1. 模式介绍 14 | 15 | ### 模式的定义 16 | `模式的一句话定义` 17 | 18 | 19 | ### 模式的使用场景 20 | 21 | 22 | 23 | ## 2. UML类图 24 | `这里是该设计模式的经典UML图` 25 | 26 | ### 角色介绍 27 | `对UML图中的各个角色进行介绍` 28 | 29 | 30 | 31 | 32 | ## 3. 模式的简单实现 33 | ### 简单实现的介绍 34 | `自己实现一个小型模式案例,通过这个案例让读者了解这个模式的一般应用` 35 | 36 | ### 实现源码 37 | `上述案例的源码实现` 38 | 39 | 40 | ### 总结 41 | `对上述的简单示例进行总结说明` 42 | 43 | 44 | 45 | 46 | ## Android源码中的模式实现 47 | `分析源码中的模式实现,列出相关源码,以及使用该模式原因等` 48 | 49 | 50 | 51 | 52 | ## 4. 杂谈 53 | 该模式的优缺点以及自己的一些感悟,非所有项目必须。 54 | 55 | 56 | 57 | `写完相关内容之后到开发群告知管理员,管理员安排相关人员进行审核,审核通过之后即可。` 58 | 59 | -------------------------------------------------------------------------------- /strategy/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/strategy/.DS_Store -------------------------------------------------------------------------------- /strategy/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 |预计完成时间 | 3 | | ------------- |:---------------------:| 4 | | GKerison | 2015.3.15 | 5 | -------------------------------------------------------------------------------- /strategy/gkerison/README.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之策略模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework/android_design_patterns_analysis) 中策略模式分析 4 | > Android系统版本:4.4.2 5 | > 分析者:[GKerison](https://github.com/GKerison),分析状态:已完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:完成 6 | 7 | ## 1. 模式介绍 8 | 9 | ### 模式的定义 10 | **策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。** 11 | 12 | `注:针对同一类型操作,将复杂多样的处理方式分别开来,有选择的实现各自特有的操作。` 13 | 14 | ### 模式的使用场景 15 | * 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。 16 | * 需要安全的封装多种同一类型的操作时。 17 | * 出现同一抽象多个子类,而又需要使用if-else 或者 switch-case来选择时。 18 | 19 | 20 | ## 2. UML类图 21 | ![url](images/strategy-kerison-uml.png) 22 | 23 | ### 角色介绍 24 | * Context:用来操作策略的上下文环境。 25 | * Strategy : 策略的抽象。 26 | * ConcreteStrategyA、ConcreteStrategyB : 具体的策略实现。 27 | 28 | 29 | 30 | ## 3. 模式的简单实现 31 | ### 简单实现的介绍 32 | 通常如果一个问题有多个解决方案或者稍有区别的操作时,最简单的方式就是利用if-else or switch-case方式来解决,对于简单的解决方案这样做无疑是比较简单、方便、快捷的,但是如果解决方案中包括大量的处理逻辑需要封装,或者处理方式变动较大的时候则就显得混乱、复杂,而策略模式则很好的解决了这样的问题,它将各种方案分离开来,让操作者根据具体的需求来动态的选择不同的策略方案。 33 | 这里以简单的计算操作(+、-、*、/)作为示例: 34 | 35 | ### 未使用策略模式 36 | 37 | ```java 38 | public static double calc(String op, double paramA, double paramB) { 39 | if ("+".equals(op)) { 40 | System.out.println("执行加法..."); 41 | return paramA + paramB; 42 | } else if ("-".equals(op)) { 43 | System.out.println("执行减法..."); 44 | return paramA - paramB; 45 | } else if ("*".equals(op)) { 46 | System.out.println("执行乘法..."); 47 | return paramA * paramB; 48 | } else if ("/".equals(op)) { 49 | System.out.println("执行除法..."); 50 | if (paramB == 0) { 51 | throw new IllegalArgumentException("除数不能为0!"); 52 | } 53 | return paramA / paramB; 54 | } else { 55 | throw new IllegalArgumentException("未找到计算方法!"); 56 | } 57 | } 58 | ``` 59 | 60 | ### 使用策略模式 61 | UML类图 62 | ![url](images/strategy-kerison-uml-calc.png) 63 | 64 | * Calc:进行计算操作的上下文环境。 65 | * Strategy : 计算操作的抽象。 66 | * AddStrategy、SubStrategy、MultiStrategy、DivStrategy : 具体的 +、-、*、/ 实现。 67 | 68 | 具体实现代码如下: 69 | 70 | ```java 71 | //针对操作进行抽象 72 | public interface Strategy { 73 | public double calc(double paramA, double paramB); 74 | } 75 | 76 | //加法的具体实现策略 77 | public class AddStrategy implements Strategy { 78 | @Override 79 | public double calc(double paramA, double paramB) { 80 | // TODO Auto-generated method stub 81 | System.out.println("执行加法策略..."); 82 | return paramA + paramB; 83 | } 84 | } 85 | 86 | //减法的具体实现策略 87 | public class SubStrategy implements Strategy { 88 | @Override 89 | public double calc(double paramA, double paramB) { 90 | // TODO Auto-generated method stub 91 | System.out.println("执行减法策略..."); 92 | return paramA - paramB; 93 | } 94 | } 95 | 96 | //乘法的具体实现策略 97 | public class MultiStrategy implements Strategy { 98 | @Override 99 | public double calc(double paramA, double paramB) { 100 | // TODO Auto-generated method stub 101 | System.out.println("执行乘法策略..."); 102 | return paramA * paramB; 103 | } 104 | } 105 | 106 | //除法的具体实现策略 107 | public class DivStrategy implements Strategy { 108 | @Override 109 | public double calc(double paramA, double paramB) { 110 | // TODO Auto-generated method stub 111 | System.out.println("执行除法策略..."); 112 | if (paramB == 0) { 113 | throw new IllegalArgumentException("除数不能为0!"); 114 | } 115 | return paramA / paramB; 116 | } 117 | } 118 | 119 | //上下文环境的实现 120 | public class Calc { 121 | private Strategy strategy; 122 | public void setStrategy(Strategy strategy) { 123 | this.strategy = strategy; 124 | } 125 | 126 | public double calc(double paramA, double paramB) { 127 | // TODO Auto-generated method stub 128 | // doing something 129 | if (this.strategy == null) { 130 | throw new IllegalStateException("你还没有设置计算的策略"); 131 | } 132 | return this.strategy.calc(paramA, paramB); 133 | } 134 | } 135 | 136 | 137 | //执行方法 138 | public static double calc(Strategy strategy, double paramA, double paramB) { 139 | Calc calc = new Calc(); 140 | calc.setStrategy(strategy); 141 | return calc.calc(paramA, paramB); 142 | } 143 | ``` 144 | 145 | 二者运行: 146 | 147 | ```java 148 | public static void main(String[] args) { 149 | double paramA = 5; 150 | double paramB = 21; 151 | 152 | System.out.println("------------- 普通形式 ----------------"); 153 | System.out.println("加法结果是:" + calc("+", paramA, paramB)); 154 | System.out.println("减法结果是:" + calc("-", paramA, paramB)); 155 | System.out.println("乘法结果是:" + calc("*", paramA, paramB)); 156 | System.out.println("除法结果是:" + calc("/", paramA, paramB)); 157 | 158 | System.out.println("------------ 策略模式 ----------------"); 159 | System.out.println("加法结果是:" + calc(new AddStrategy(), paramA, paramB)); 160 | System.out.println("减法结果是:" + calc(new SubStrategy(), paramA, paramB)); 161 | System.out.println("乘法结果是:" + calc(new MultiStrategy(), paramA, paramB)); 162 | System.out.println("除法结果是:" + calc(new DivStrategy(), paramA, paramB)); 163 | } 164 | ``` 165 | 166 | 结果为: 167 | 168 | ![url](images/strategy-kerison-uml-calc-result.png) 169 | 170 | ### 总结 171 | 172 | 通过简单的代码可以清晰的看出二者的优势所在,前者通过简单的if-else来解决问题,在解决简单问题事会更简单、方便,后者则是通过给予不同的具体策略来获取不同的结果,对于较为复杂的业务逻辑显得更为直观,扩展也更为方便。 173 | 174 | 175 | ## Android源码中的模式实现 176 | 日常的Android开发中经常会用到动画,Android中最简单的动画就是Tween Animation了,当然帧动画和属性动画也挺方便的,但是基本原理都类似,毕竟动画的本质都是一帧一帧的展现给用户的,只不要当fps小于60的时候,人眼基本看不出间隔,也就成了所谓的流畅动画。(注:属性动画是3.0以后才有的,低版本可采用[NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids)来兼容。而动画的动态效果往往也取决于插值器Interpolator不同,我们只需要对Animation对象设置不同的Interpolator就可以实现不同的效果,这是怎么实现的呢? 177 | 178 | 首先要想知道动画的执行流程,还是得从View入手,因为Android中主要针对的操作对象还是View,所以我们首先到View中查找,我们找到了View.startAnimation(Animation animation)这个方法。 179 | 180 | ```java 181 | public void startAnimation(Animation animation) { 182 | //初始化动画开始时间 183 | animation.setStartTime(Animation.START_ON_FIRST_FRAME); 184 | //对View设置动画 185 | setAnimation(animation); 186 | //刷新父类缓存 187 | invalidateParentCaches(); 188 | //刷新View本身及子View 189 | invalidate(true); 190 | } 191 | ``` 192 | 考虑到View一般不会单独存在,都是存在于某个ViewGroup中,所以google使用动画绘制的地方选择了在ViewGroup中的drawChild(Canvas canvas, View child, long drawingTime)方法中进行调用子View的绘制。 193 | 194 | ```java 195 | protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 196 | return child.draw(canvas, this, drawingTime); 197 | } 198 | ``` 199 | 200 | 再看下View中的draw(Canvas canvas, ViewGroup parent, long drawingTime)方法中是如何调用使用Animation的 201 | 202 | ```java 203 | boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { 204 | //... 205 | 206 | //查看是否需要清除动画信息 207 | final int flags = parent.mGroupFlags; 208 | if ((flags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) == ViewGroup.FLAG_CLEAR_TRANSFORMATION) { 209 | parent.getChildTransformation().clear(); 210 | parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION; 211 | } 212 | 213 | //获取设置的动画信息 214 | final Animation a = getAnimation(); 215 | if (a != null) { 216 | //绘制动画 217 | more = drawAnimation(parent, drawingTime, a, scalingRequired); 218 | concatMatrix = a.willChangeTransformationMatrix(); 219 | if (concatMatrix) { 220 | mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; 221 | } 222 | transformToApply = parent.getChildTransformation(); 223 | } else { 224 | //... 225 | } 226 | } 227 | ``` 228 | 229 | 可以看出在父类调用View的draw方法中,会先判断是否设置了清除到需要做该表的标记,然后再获取设置的动画的信息,如果设置了动画,就会调用View中的drawAnimation方法,具体如下: 230 | 231 | ```java 232 | private boolean drawAnimation(ViewGroup parent, long drawingTime, 233 | Animation a, boolean scalingRequired) { 234 | 235 | Transformation invalidationTransform; 236 | final int flags = parent.mGroupFlags; 237 | //判断动画是否已经初始化过 238 | final boolean initialized = a.isInitialized(); 239 | if (!initialized) { 240 | a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight()); 241 | a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop); 242 | if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler); 243 | onAnimationStart(); 244 | } 245 | 246 | //判断View是否需要进行缩放 247 | final Transformation t = parent.getChildTransformation(); 248 | boolean more = a.getTransformation(drawingTime, t, 1f); 249 | if (scalingRequired && mAttachInfo.mApplicationScale != 1f) { 250 | if (parent.mInvalidationTransformation == null) { 251 | parent.mInvalidationTransformation = new Transformation(); 252 | } 253 | invalidationTransform = parent.mInvalidationTransformation; 254 | a.getTransformation(drawingTime, invalidationTransform, 1f); 255 | } else { 256 | invalidationTransform = t; 257 | } 258 | 259 | if (more) { 260 | //根据具体实现,判断当前动画类型是否需要进行调整位置大小,然后刷新不同的区域 261 | if (!a.willChangeBounds()) { 262 | //... 263 | 264 | }else{ 265 | //... 266 | } 267 | } 268 | return more; 269 | } 270 | ``` 271 | 272 | 其中主要的操作是动画始化、动画操作、界面刷新。动画的具体实现是调用了Animation中的getTransformation(long currentTime, Transformation outTransformation,float scale)方法。 273 | 274 | ```java 275 | 276 | public boolean getTransformation(long currentTime, Transformation outTransformation, 277 | float scale) { 278 | mScaleFactor = scale; 279 | return getTransformation(currentTime, outTransformation); 280 | } 281 | ``` 282 | 283 | 在上面的方法中主要是获取缩放系数和调用Animation.getTransformation(long currentTime, Transformation outTransformation)来计算和应用动画效果。 284 | 285 | ```java 286 | Interpolator mInterpolator; //成员变量 287 | public boolean getTransformation(long currentTime, Transformation outTransformation) { 288 | //计算处理当前动画的时间点... 289 | final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); 290 | //后续处理,以此来应用动画效果... 291 | applyTransformation(interpolatedTime, outTransformation); 292 | return mMore; 293 | } 294 | ``` 295 | 296 | 很容易发现Android系统中在处理动画的时候会调用插值器中的getInterpolation(float input)方法来获取当前的时间点,依次来计算当前变化的情况。这就不得不说到Android中的插值器Interpolator,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator(减速插值器:动画越来越慢)等,如图: 297 | 298 | ![url](images/strategy-kerison-uml-android-interpolator.png) 299 | 300 | 由于初期比较旧的版本采用的插值器是TimeInterpolator抽象,google采用了多加一层接口继承来实现兼容也不足为怪了。很显然策略模式在这里作了很好的实现,Interpolator就是处理动画时间的抽象,LinearInterpolator、CycleInterpolator等插值器就是具体的实现策略。插值器与Animation的关系图如下: 301 | 302 | ![url](images/strategy-kerison-uml-android.png) 303 | 304 | 这里以LinearInterpolator和CycleInterpolator为例: 305 | 306 | - LinearInterpolator 307 | 308 | public float getInterpolation(float input) { 309 | return input; 310 | } 311 | 312 | - CycleInterpolator 313 | 314 | public float getInterpolation(float input) { 315 | return (float)(Math.sin(2 * mCycles * Math.PI * input)); 316 | } 317 | 318 | 可以看出LinearInterpolator中计算当前时间的方法是做线性运算,也就是返回input*1,所以动画会成直线匀速播放出来,而CycleInterpolator是按照正弦运算,所以动画会正反方向跑一次,其它插值器依次类推。不同的插值器的计算方法都有所差别,用户设置插值器以实现动画速率的算法替换。 319 | 320 | 321 | ## 4. 杂谈 322 | 策略模式主要用来分离算法,根据相同的行为抽象来做不同的具体策略实现。 323 | 324 | 通过以上也可以看出策略模式的优缺点: 325 | 326 | 优点: 327 | 328 | * 结构清晰明了、使用简单直观。 329 | * 耦合度相对而言较低,扩展方便。 330 | * 操作封装也更为彻底,数据更为安全。 331 | 332 | 缺点: 333 | 334 | * 随着策略的增加,子类也会变得繁多。 335 | -------------------------------------------------------------------------------- /strategy/gkerison/images/strategy-kerison-uml-android-animation-matrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/strategy/gkerison/images/strategy-kerison-uml-android-animation-matrix.png -------------------------------------------------------------------------------- /strategy/gkerison/images/strategy-kerison-uml-android-animation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/strategy/gkerison/images/strategy-kerison-uml-android-animation.png -------------------------------------------------------------------------------- /strategy/gkerison/images/strategy-kerison-uml-android-interpolator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/strategy/gkerison/images/strategy-kerison-uml-android-interpolator.png -------------------------------------------------------------------------------- /strategy/gkerison/images/strategy-kerison-uml-android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/strategy/gkerison/images/strategy-kerison-uml-android.png -------------------------------------------------------------------------------- /strategy/gkerison/images/strategy-kerison-uml-calc-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/strategy/gkerison/images/strategy-kerison-uml-calc-result.png -------------------------------------------------------------------------------- /strategy/gkerison/images/strategy-kerison-uml-calc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/strategy/gkerison/images/strategy-kerison-uml-calc.png -------------------------------------------------------------------------------- /strategy/gkerison/images/strategy-kerison-uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/strategy/gkerison/images/strategy-kerison-uml.png -------------------------------------------------------------------------------- /template-method/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/template-method/.DS_Store -------------------------------------------------------------------------------- /template-method/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 模式名 | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| ------------- | 4 | | 模板方法 | [Mr.Simple](https://github.com/bboyfeiyu) | 2015.3.2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /template-method/mr.simple/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/template-method/mr.simple/.DS_Store -------------------------------------------------------------------------------- /template-method/mr.simple/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/template-method/mr.simple/images/.DS_Store -------------------------------------------------------------------------------- /template-method/mr.simple/images/async-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/template-method/mr.simple/images/async-flow.png -------------------------------------------------------------------------------- /template-method/mr.simple/images/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/template-method/mr.simple/images/flow.png -------------------------------------------------------------------------------- /template-method/mr.simple/images/uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/template-method/mr.simple/images/uml.png -------------------------------------------------------------------------------- /template-method/mr.simple/readme.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之模板方法模式 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 模板方法模式 分析 4 | > Android系统版本: 2.3 5 | > 分析者:[Mr.Simple](https://github.com/bboyfeiyu),分析状态:完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:完成 6 | 7 | 8 | ## 1. 模式介绍 9 | 10 | ### 模式的定义 11 | 定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 12 | 13 | 14 | ### 模式的使用场景 15 | 1. 多个子类有公有的方法,并且逻辑基本相同时。 16 | 2. 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。 17 | 3. 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。 18 | 19 | 20 | ## 2. UML类图 21 | ![url](images/uml.png) 22 | 23 | ### 角色介绍 24 | * AbstractClass : 抽象类,定义了一套算法框架。 25 | * ConcreteClass1 : 具体实现类1; 26 | * ConcreteClass2: 具体实现类2; 27 | 28 | 29 | 30 | ## 3. 模式的简单实现 31 | ### 简单实现的介绍 32 | 模板方法实际上是封装一个算法框架,就像是一套模板一样。而子类可以有不同的算法实现,在框架不被修改的情况下实现算法的替换。下面我们以开电脑这个动作来简单演示一下模板方法。开电脑的整个过程都是相对稳定的,首先打开电脑电源,电脑检测自身状态没有问题时将进入操作系统,对用户进行验证之后即可登录电脑,下面我们使用模板方法来模拟一下这个过程。 33 | 34 | ### 实现源码 35 | 36 | ```java 37 | package com.dp.example.templatemethod; 38 | 39 | /** 40 | * 抽象的Computer 41 | * @author mrsimple 42 | * 43 | */ 44 | public abstract class AbstractComputer { 45 | 46 | protected void powerOn() { 47 | System.out.println("开启电源"); 48 | } 49 | 50 | protected void checkHardware() { 51 | System.out.println("硬件检查"); 52 | } 53 | 54 | protected void loadOS() { 55 | System.out.println("载入操作系统"); 56 | } 57 | 58 | protected void login() { 59 | System.out.println("小白的电脑无验证,直接进入系统"); 60 | } 61 | 62 | /** 63 | * 启动电脑方法, 步骤固定为开启电源、系统检查、加载操作系统、用户登录。该方法为final, 防止算法框架被覆写. 64 | */ 65 | public final void startUp() { 66 | System.out.println("------ 开机 START ------"); 67 | powerOn(); 68 | checkHardware(); 69 | loadOS(); 70 | login(); 71 | System.out.println("------ 开机 END ------"); 72 | } 73 | } 74 | 75 | 76 | package com.dp.example.templatemethod; 77 | 78 | /** 79 | * 码农的计算机 80 | * 81 | * @author mrsimple 82 | */ 83 | public class CoderComputer extends AbstractComputer { 84 | @Override 85 | protected void login() { 86 | System.out.println("码农只需要进行用户和密码验证就可以了"); 87 | } 88 | } 89 | 90 | 91 | package com.dp.example.templatemethod; 92 | 93 | /** 94 | * 军用计算机 95 | * 96 | * @author mrsimple 97 | */ 98 | public class MilitaryComputer extends AbstractComputer { 99 | 100 | 101 | @Override 102 | protected void checkHardware() { 103 | super.checkHardware(); 104 | System.out.println("检查硬件防火墙"); 105 | } 106 | 107 | @Override 108 | protected void login() { 109 | System.out.println("进行指纹之别等复杂的用户验证"); 110 | } 111 | } 112 | 113 | 114 | package com.dp.example.templatemethod; 115 | 116 | public class Test { 117 | public static void main(String[] args) { 118 | AbstractComputer comp = new CoderComputer(); 119 | comp.startUp(); 120 | 121 | comp = new MilitaryComputer(); 122 | comp.startUp(); 123 | 124 | } 125 | } 126 | 127 | ``` 128 | 129 | 输出结果如下 : 130 | 131 | ``` 132 | ------ 开机 START ------ 133 | 开启电源 134 | 硬件检查 135 | 载入操作系统 136 | 码农只需要进行用户和密码验证就可以了 137 | ------ 开机 END ------ 138 | ------ 开机 START ------ 139 | 开启电源 140 | 硬件检查 141 | 检查硬件防火墙 142 | 载入操作系统 143 | 进行指纹之别等复杂的用户验证 144 | ------ 开机 END ------ 145 | ``` 146 | 147 | 通过上面的例子可以看到,在startUp方法中有一些固定的步骤,依次为开启电源、检查硬件、加载系统、用户登录四个步骤,这四个步骤是电脑开机过程中不会变动的四个过程。但是不同用户的这几个步骤的实现可能各不相同,因此他们可以用不同的实现。而startUp为final方法,即保证了算法框架不能修改,具体算法实现却可以灵活改变。startUp中的这几个算法步骤我们可以称为是一个套路,即可称为模板方法。因此,模板方法是定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。如图 : 148 | 149 | ![flow](images/flow.png) 150 | 151 | 152 | ## Android源码中的模式实现 153 | 在Android中,使用了模板方法且为我们熟知的一个典型类就是AsyncTask了,关于AsyncTask的更详细的分析请移步Android中AsyncTask的使用与源码分析,我们这里只分析在该类中使用的模板方法模式。 154 | 155 | 在使用AsyncTask时,我们都有知道耗时的方法要放在doInBackground(Params... params)中,在doInBackground之前如果还想做一些类似初始化的操作可以写在onPreExecute方法中,当doInBackground方法执行完成后,会执行onPostExecute方法,而我们只需要构建AsyncTask对象,然后执行execute方法即可。我们可以看到,它整个执行过程其实是一个框架,具体的实现都需要子类来完成。而且它执行的算法框架是固定的,调用execute后会依次执行onPreExecute,doInBackground,onPostExecute,当然你也可以通过onProgressUpdate来更新进度。我们可以简单的理解为如下图的模式 : 156 | 157 | ![async-flow](images/async-flow.png) 158 | 159 | 下面我们看源码,首先我们看执行异步任务的入口, 即execute方法 : 160 | 161 | 162 | ```java 163 | public final AsyncTask execute(Params... params) { 164 | return executeOnExecutor(sDefaultExecutor, params); 165 | } 166 | 167 | public final AsyncTask executeOnExecutor(Executor exec, 168 | Params... params) { 169 | if (mStatus != Status.PENDING) { 170 | switch (mStatus) { 171 | case RUNNING: 172 | throw new IllegalStateException("Cannot execute task:" 173 | + " the task is already running."); 174 | case FINISHED: 175 | throw new IllegalStateException("Cannot execute task:" 176 | + " the task has already been executed " 177 | + "(a task can be executed only once)"); 178 | } 179 | } 180 | 181 | mStatus = Status.RUNNING; 182 | 183 | onPreExecute(); 184 | 185 | mWorker.mParams = params; 186 | exec.execute(mFuture); 187 | 188 | return this; 189 | } 190 | 191 | ``` 192 | 193 | 可以看到execute方法(为final类型的方法)调用了executeOnExecutor方法,在该方法中会判断该任务的状态,如果不是PENDING状态则抛出异常,这也解释了为什么AsyncTask只能被执行一次,因此如果该任务已经被执行过的话那么它的状态就会变成FINISHED。继续往下看,我们看到在executeOnExecutor方法中首先执行了onPreExecute方法,并且该方法执行在UI线程。然后将params参数传递给了mWorker对象的mParams字段,然后执行了exec.execute(mFuture)方法。 194 | 195 | mWorker和mFuture又是什么呢?其实mWorker只是实现了Callable接口,并添加了一个参数数组字段,关于Callable和FutureTask的资料请参考[Java中的Runnable、Callable、Future、FutureTask的区别与示例](http://blog.csdn.net/bboyfeiyu/article/details/24851847),我们挨个来分析吧,跟踪代码我们可以看到,这两个字段都是在构造函数中初始化。 196 | 197 | ```java 198 | public AsyncTask() { 199 | mWorker = new WorkerRunnable() { 200 | public Result call() throws Exception { 201 | mTaskInvoked.set(true); 202 | 203 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 204 | return postResult(doInBackground(mParams)); 205 | } 206 | }; 207 | 208 | mFuture = new FutureTask(mWorker) { 209 | @Override 210 | protected void done() { 211 | try { 212 | final Result result = get(); 213 | 214 | postResultIfNotInvoked(result); 215 | } catch (InterruptedException e) { 216 | android.util.Log.w(LOG_TAG, e); 217 | } catch (ExecutionException e) { 218 | throw new RuntimeException("An error occured while executing doInBackground()", 219 | e.getCause()); 220 | } catch (CancellationException e) { 221 | postResultIfNotInvoked(null); 222 | } catch (Throwable t) { 223 | throw new RuntimeException("An error occured while executing " 224 | + "doInBackground()", t); 225 | } 226 | } 227 | }; 228 | } 229 | ``` 230 | 231 | 简单的说就是mFuture就包装了这个mWorker对象,会调用mWorker对象的call方法,并且将之返回给调用者。 232 | 关于AsyncTask的更详细的分析请移步[Android中AsyncTask的使用与源码分析](http://blog.csdn.net/bboyfeiyu/article/details/8973058),我们这里只分析模板方法模式。总之,call方法会在子线程中调用,而在call方法中又调用了doInBackground方法,因此doInBackground会执行在子线程。doInBackground会返回结果,最终通过postResult投递给UI线程。 233 | 我们再看看postResult的实现 : 234 | 235 | ```java 236 | private Result postResult(Result result) { 237 | Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, 238 | new AsyncTaskResult(this, result)); 239 | message.sendToTarget(); 240 | return result; 241 | } 242 | 243 | private static class InternalHandler extends Handler { 244 | @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) 245 | @Override 246 | public void handleMessage(Message msg) { 247 | AsyncTaskResult result = (AsyncTaskResult) msg.obj; 248 | switch (msg.what) { 249 | case MESSAGE_POST_RESULT: 250 | // There is only one result 251 | result.mTask.finish(result.mData[0]); 252 | break; 253 | case MESSAGE_POST_PROGRESS: 254 | result.mTask.onProgressUpdate(result.mData); 255 | break; 256 | } 257 | } 258 | } 259 | 260 | 261 | private void finish(Result result) { 262 | if (isCancelled()) { 263 | onCancelled(result); 264 | } else { 265 | onPostExecute(result); 266 | } 267 | mStatus = Status.FINISHED; 268 | } 269 | 270 | ``` 271 | 272 | 可以看到,postResult就是把一个消息( msg.what == MESSAGE_POST_RESULT)发送给sHandler,sHandler类型为InternalHandler类型,当InternalHandler接到MESSAGE_POST_RESULT类型的消息时就会调用result.mTask.finish(result.mData[0])方法。我们可以看到result为AsyncTaskResult类型,源码如下 : 273 | 274 | 275 | ```java 276 | @SuppressWarnings({"RawUseOfParameterizedType"}) 277 | private static class AsyncTaskResult { 278 | final AsyncTask mTask; 279 | final Data[] mData; 280 | 281 | AsyncTaskResult(AsyncTask task, Data... data) { 282 | mTask = task; 283 | mData = data; 284 | } 285 | } 286 | ``` 287 | 288 | **可以看到mTask就是AsyncTask对象**,调用AsyncTask对象的finish方法时又调用了onPostExecute,这个时候整个执行过程就完成了。 289 | 总之,execute方法内部封装了onPreExecute, doInBackground, onPostExecute这个算法框架,用户可以根据自己的需求来在覆写这几个方法,使得用户可以很方便的使用异步任务来完成耗时操作,又可以通过onPostExecute来完成更新UI线程的工作。 290 | 另一个比较好的模板方法示例就是Activity的声明周期函数,例如Activity从onCreate、onStart、onResume这些程式化的执行模板,这就是一个Activity的模板方法。 291 | 292 | ## 4. 杂谈 293 | ### 优点与缺点 294 | #### 优点 295 | * 封装不变部分,扩展可变部分 296 | * 提取公共部分代码,便于维护 297 | 298 | #### 缺点 299 | * 模板方法会带来代码阅读的难度,会让心觉得难以理解。 300 | -------------------------------------------------------------------------------- /template.md: -------------------------------------------------------------------------------- 1 | Android设计模式源码解析之${模式名} 2 | ==================================== 3 | > 本文为 [Android 设计模式源码解析](https://github.com/simple-android-framework-exchange/android_design_patterns_analysis) 中 ${模式名} 分析 4 | > Android系统版本: ${系统版本号,例如 4.2.1} 5 | > 分析者:[${分析者}](${分析者 Github 地址}),分析状态:未完成,校对者:[Mr.Simple](https://github.com/bboyfeiyu),校对状态:未开始 6 | 7 | 8 | `复制一份到自己的项目文件夹下,然后根据自己项目替换掉 ${} 内容,删掉本行及上面两行。` 9 | 10 | 该任务不仅要用java写出该模式的一个简单示例,还有分析该模式在Android源码中的应用,可以参考[Mr.Simple的单例模式](singleton/mr.simple/readme.md)、[Mr.Simple的观察者模式](observer/mr.simple/readme.md)。 11 | 12 | 13 | ## 1. 模式介绍 14 | 15 | ### 模式的定义 16 | `模式的一句话定义` 17 | 18 | 19 | ### 模式的使用场景 20 | 21 | 22 | 23 | ## 2. UML类图 24 | `这里是该设计模式的经典UML图` 25 | 26 | ### 角色介绍 27 | `对UML图中的各个角色进行介绍` 28 | 29 | 30 | 31 | 32 | ## 3. 模式的简单实现 33 | ### 简单实现的介绍 34 | `自己实现一个小型模式案例,通过这个案例让读者了解这个模式的一般应用` 35 | 36 | ### 实现源码 37 | `上述案例的源码实现` 38 | 39 | 40 | ### 总结 41 | `对上述的简单示例进行总结说明` 42 | 43 | 44 | 45 | 46 | ## Android源码中的模式实现 47 | `分析源码中的模式实现,列出相关源码,以及使用该模式原因等` 48 | 49 | 50 | 51 | 52 | ## 4. 杂谈 53 | 该模式的优缺点以及自己的一些感悟,非所有项目必须。 54 | 55 | 56 | 57 | `写完相关内容之后到开发群告知管理员,管理员安排相关人员进行审核,审核通过之后即可。` 58 | 59 | -------------------------------------------------------------------------------- /visitor/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simple-android-framework-exchange/android_design_patterns_analysis/0c9bd8752090b327354e751425f1c61098145043/visitor/.DS_Store -------------------------------------------------------------------------------- /visitor/README.md: -------------------------------------------------------------------------------- 1 | # 任务表 2 | | 作者 | 预计完成时间 | 3 | | ------------- |:-------------:| 4 | | [用户名](git地址) | 完成时间 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | --------------------------------------------------------------------------------