转载请表明出处:http://write.blog.csdn.net/postedit/43411673
先解释一下设计模式之美,美体现在哪里?
任何实现代码的方法都没有美丑之分,只有复杂与简单之分,人们喜欢简单的,害怕、厌烦、排斥复杂的。
设计模式之美就体现在可以把复杂的东西简单化,让人们用起来很简单,不再惧怕复杂,这就是设计模式美的体现。
Android框架乍一看很复杂,实际上应用了很多设计模式,所以如果我们从设计模式角度来分析Android框架,也就很简单了。
Template Method 即 模板方法
谈到模板方法,首先我们要了解 “变与不变的原则”,何谓“变与不变的原则”,我用一个例子来说明一下。
大家都用过一种多功能的螺丝刀吧,手柄部分只有一个,但是却有很多很多的头。
很显然,这个手柄部分就是一直不变的不分,各种各样的头就是变的部分,这样就明白了“变与不变的原则”。
而在程序中,变的部分是需要不用场景不同实现的,不变的部分是一直不变,需要抽离出来的。
在螺丝刀中,插头的地方叫卡槽,卡槽属于手柄的一部分。
相对应到程序中,插头的地方叫卡榫函数,手柄是Tempalte函数。
而“变与不变”,无关乎本质,只在于看事请的角度。
通过一大推的描述来解释一个定义,我觉得这种学习方法不适合程序员。我还是直接用代码来解释吧。
首先来看没有应用任何设计模式的案例。
public class AA {
private String x;
AA(String str){ x = str; }
public void print(){ System.out.println(x); }
}
public class BB {
private int x;
BB(int k){ x = k; }
public void print(){ System.out.println(x); }
}
public class JMain {
public static void main(String[] args) {
AA a = new AA("hello");
BB b = new BB("hello girl");
a.print();
b.print();}}
下面就应用“变与不变的原则”,来优化上面的案例。
将变与不变的地方分离开来,然后将会变得地方写入卡榫函数,比如卡榫函数命名为“hook_getData()”。
private String x;
public final void template_print() {
System.out.println( hook_getData() ); }
public String hook_getData() { return x;
}
private int x;
public final void template_print() {
System.out.println( hook_getData() ); }
public String hook_getData() { return String.valueOf(x);
}
分离之后,template_print()函数含有不变的部分,而hook_getData()函数会含有变得部分。
接着再引入父子继承关系,将不变的template_print()函数提到父类中。
在父类中,定义抽奖的hook_getData()卡榫函数。
在父类中,在template_print()函数中调用抽象的卡榫函数hook_getData(),此时由继承机制反向(一般认为由子类调用父类的方法为正向)呼叫子类别的hook_getData()函数,实现了父、子类别之间的不同调用。
相对于Android框架来讲,temlate_print()函数将会吸收在框架里,hook_getData()函数将会被纳入到应用程序里。
这就是框架与应用程序之间的沟通了,hook_getData()卡榫函数起着桥梁的作用。
程序如下:
public abstract class SuperAB {
public void template_print(){
System.out.println(hook_getData());
}
protected abstract String hook_getData();
}
public class AA extends SuperAB{
private String x;
AA(String str){ x = str; }
@Override protected String hook_getData() {
return x;
}}
public class BB extends SuperAB{
private int x;
BB(int k){ x = k; }
@Override protected String hook_getData() {
return String.valueOf(x);
}}
public class JMain {
public static void main(String[] args) {
AA a = new AA("hello");
a.template_print();
}}
a.template_print();
呼叫了AF里的template.print()函数。
AF里的template.print()函数调用了SuperAB类里面的hook_getData()函数,进而反向呼叫了AP(SuperAB的子类)
里的hook_getData()函数。这也就是反向控制,或者说是控制反转。
到这里就明白了“变与不变的原则”,明白了控制反转。
对外的接口是tempalte_print(),
对内的接口是hook_getData(),
这样就屏屏蔽掉了传统父类和子类用一个接口产生的依赖性,提高了应用框架的弹性。这就是Template Method模式的用处所在。
接着就可以看看Template Method 模式在Android中的应用了。
public class ac01 extends Activity implements View.OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
private final int FP = LinearLayout.LayoutParams.FILL_PARENT;
private static ac01 appRef = null;
private myButton btn, btn2, btn3;
public TextView tv;
private IBinder ib;
public static ac01 getApp() {
return appRef;
}
public void btEvent(String data) {
setTitle(data);
}
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
appRef = this;
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
btn = new myButton(this);
btn.setId(101);
btn.setText("play");
btn.setOnClickListener(this);
LinearLayout.LayoutParams param =
new LinearLayout.LayoutParams(btn.get_width(), btn.get_height());
param.topMargin = 10;
layout.addView(btn, param);
btn2 = new myButton(this);
btn2.setId(102);
btn2.setText("stop");
btn2.setOnClickListener(this);
layout.addView(btn2, param);
btn3 = new myButton(this);
btn3.setId(103);
btn3.setText("exit");
btn3.setOnClickListener(this);
layout.addView(btn3, param);
tv = new TextView(this);
tv.setTextColor(Color.WHITE);
tv.setText("Ready");
LinearLayout.LayoutParams param2 = new LinearLayout.LayoutParams(FP, WC);
param2.topMargin = 10;
layout.addView(tv, param2);
setContentView(layout);
bindService(new Intent(ac01.this, mp3Service.class), mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder ibinder) {
ib = ibinder;
}
public void onServiceDisconnected(ComponentName className) {
}
};
public void onClick(View v) {
switch (v.getId()) {
case 101:
tv.setText("Playing audio...");
setTitle("MP3 Music");
try {
ib.transact(1, null, null, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case 102:
tv.setText("Stop");
try {
ib.transact(2, null, null, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case 103:
finish();
break;
}
}
}
public class mp3Service extends Service {
private IBinder mBinder = null;
@Override public void onCreate() {
mBinder = new mp3PlayerBinder(getApplicationContext());
}
@Override public IBinder onBind(Intent intent) { return mBinder; }
}
public class mp3PlayerBinder extends Binder {
private MediaPlayer mPlayer = null;
private Context ctx;
public mp3PlayerBinder(Context cx) {
ctx = cx;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws android.os.RemoteException {
if (code == 1)
this.play();
else if (code == 2)
this.stop();
return true;
}
public void play() {
if (mPlayer != null)
return;
mPlayer = MediaPlayer.create(ctx, 0);
try {
mPlayer.start();
} catch (Exception e) {
Log.e("StartPlay", "error: " + e.getMessage(), e);
}
}
public void stop() {
if (mPlayer != null) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
}
}
public class myButton extends Button {
public myButton(Context ctx) {
super(ctx);
}
public int get_width() {
return 80;
}
public int get_height() {
return 50;
}
}
整好借这个例子,也能学习一下Binder的用处。如果想学习Binder的用法,我推荐《Android内核剖析》中的第五章。
Tempate Method模式就好比一只乌龟,轻易看不到乌龟的头,template函数就是乌龟的头,hook函数就是乌龟的尾巴,
我们一般只能看到乌龟的尾巴。
在Android的Binder父类中定义了一个transact()函数,这就是乌龟的头,它会呼叫onTransact()卡榫函数,也就是尾巴。
这样我们就明白了Android Binder 虽然看似复杂,但是从Template Method角度来看,是简单而美的一种很有序的设计实现,
而不是杂乱无章,很高深莫测的。
好人做到底,提供一下最后一个Android Binder 案例 demo的下载地址:
https://github.com/nicewarm/EIT_Template_Method