首先了解下钩子函数:
钩子是一种被声明在抽象类中的方法,但只有空的或默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。
比如:
声明有钩子的抽象类:
package com.dlo.designpatterns.hook;
import android.util.Log;
public abstract class CaffeineBeverageWithHook {
String TAG = "CaffeineBeverageWithHook";
public final void prepareRecipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
Log.d(TAG,"Boiling water");
}
void pourInCup() {
Log.d(TAG,"pouring into cup");
}
boolean customerWantsCondiments() {
Log.d(TAG,"customerWantsCondiments");
return true;
}
}
实现钩子的具体派生类1:
package com.dlo.designpatterns.hook;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import android.util.Log;
public class CoffeeWithHook extends CaffeineBeverageWithHook {
String TAG = "CoffeeWithHook";
@Override
void brew() {
// TODO Auto-generated method stub
Log.d(TAG,"Adding Sugar and Milk");
}
@Override
void addCondiments() {
// TODO Auto-generated method stub
Log.d(TAG,"addCondiments");
}
public boolean customerWantsCondiments() {
String answer = getUserInput();
if (answer.toLowerCase().startsWith("y")) {
return true;
} else {
return false;
}
}
private String getUserInput() {
String answer = null;
Log.d(TAG,"Would you like milk and sugar with your coffee(y/n)?");
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
try {
answer = in.readLine();
} catch (IOException ioe) {
System.err.println("IO error trying to read your answer");
}
if (answer == null) {
return "no";
}
return answer;
}
}
实现钩子的具体派生类2:
package com.dlo.designpatterns.hook;
import android.util.Log;
public class TeaWithHook extends CaffeineBeverageWithHook {
String TAG = "TeaWithHook";
@Override
void brew() {
// TODO Auto-generated method stub
Log.d(TAG,"brew");
}
@Override
void addCondiments() {
// TODO Auto-generated method stub
Log.d(TAG,"addCondiments");
}
}
测试类,分别测试类1和类2的行为:
package com.dlo.designpatterns;
import com.dlo.designpatterns.hook.CoffeeWithHook;
import com.dlo.designpatterns.hook.TeaWithHook;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TeaWithHook teaHook = new TeaWithHook();
CoffeeWithHook coffeeHook = new CoffeeWithHook();
Log.d(TAG,"\nMaking tea...");
teaHook.prepareRecipe();
Log.d(TAG,"\n Making coffee...");
coffeeHook.prepareRecipe();
}
}
我 们在prepareRecipe函数中加入条件语句,当用户需要调料的时候,才调用addCondiments(),addCondiments()只返 回true,不做别的事,这就是一个钩子,子类可以覆盖这个方法,但不一定非得这么做。这样在子类中,钩子控制了咖啡因饮料是否执行某部分算法(是否加调 料)。
钩子和抽象方法的使用场景:
当子类必须提供算法中某个方法或步骤的实现时,就使用抽象方法。如果这个部分是可选的,就用钩子。如果是钩子的话,子类可以选择实现这个钩子,但并不强制这么做。
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Android框架层中对钩子的实际应用举例:
//AdapterView.java中
public abstract class AdapterView<T extends Adapter> extends ViewGroup {
/**
* @return The view corresponding to the currently selected item, or null
* if nothing is selected
*/
public abstract View getSelectedView();
private void fireOnSelected() {
if (mOnItemSelectedListener == null) {
return;
}
final int selection = getSelectedItemPosition();
if (selection >= 0) {
View v = getSelectedView();
mOnItemSelectedListener.onItemSelected(this, v, selection,
getAdapter().getItemId(selection));
} else {
mOnItemSelectedListener.onNothingSelected(this);
}
}
}
这里就使用了钩子函数getSelectedView();具体实现交给子类,而可以在AdapterView的fireOnSelected等函数中作算法控制。
比如在子类AbsListView中就有了实现:
//AbsListView.java
public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
ViewTreeObserver.OnTouchModeChangeListener,
RemoteViewsAdapter.RemoteAdapterConnectionCallback {
@Override
@ViewDebug.ExportedProperty
public View getSelectedView() {
if (mItemCount > 0 && mSelectedPosition >= 0) {
return getChildAt(mSelectedPosition - mFirstPosition);
} else {
return null;
}
}
}