代理模式
代理模式
代理模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。比如你按照小卡片上的电话打过去寻求服务,一般不是由本人,可能是一个成年雄性接听电话,然而真正做事情的可能是另一个小姐姐。
- 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性
- 通过代理对象对访问进行控制
- 抽象角色
指代理角色和真实角色对外提供的公共方法,一般为一个接口
- 真实角色
需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
- 代理角色
需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理。
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。
静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。
- 类图:
- 使用静态代理的基本步骤:
- 定义代理对象和真实对象的公共接口 Subject;
- 真实对象实现公共接口中的方法 RealSubject;
- 代理对象实现公共接口中的方法,并把方法的逻辑转发给真实对象 ProxySubject。
我们通过小明买房的这个例子来讲解静态代理,小明想要在大城市租房,但是他平时很忙没有时间去看房,于是他就找到一个房产中介,把自己的租房意愿告诉房产中介,让房产中介来替自己解决租房问题,很明显房产中介就是代理人,小明就是被代理的人。
首先定义一个租房步骤的公共接口:
1
2
3
4
5
6
7
//租房步骤公共接口,即Subject角色
public interface IRoom {
void seekRoom();//找房
void watchRoom();//看房
void room();//给钱租房
void finish();//完成租房
}
4 个步骤完成租房,很简单,然后我们定义具体的想要租房的人即小明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//被代理人,想要租房的小明,即RealSubject角色
public class XiaoMing implements IRoom {
@Override
public void seekRoom() {
System.out.println("找房");
}
@Override
public void watchRoom() {
System.out.println("看房");
}
@Override
public void room() {
System.out.println("给钱租房");
}
@Override
public void finish() {
System.out.println("完成租房");
}
}
该类实现了 IRoom 接口,实现了其中的具体逻辑,但是小明并不会自己去打租房,他委托房产中介去做,所以这里定义一个房产中介:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//代理人,房产中介,即ProxySubject角色
public class RoomAgency implements IRoom {
private IRoom mRoom;//持有一个被代理人(小明)的引用
public RoomAgency(IRoom room){
this.mRoom = room;
}
@Override
public void seekRoom() {
mRoom.seekRoom();
}
@Override
public void watchRoom() {
mRoom.watchRoom();
}
@Override
public void room() {
mRoom.room();
}
@Override
public void finish() {
mRoom.finish();
}
}
在该类中会持有一个被代理人的引用,在这里指小明,可以看到房产中介所执行的方法的实质就是简单的调用被代理人中的方法,下面来看看 Client 中具体的执行关系:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//客户端,即Client角色
public class Client {
public static void main(String[] args){
//小明想租房
XiaoMing xiaoMing = new XiaoMing();
//找一个代理人,房产中介
RoomAgency roomAgency = new RoomAgency(xiaoMing);
//房产中介找房
roomAgency.watchRoom();
//房产中介看房
roomAgency.seekRoom();
//房产中介租房
roomAgency.room();
//房产中介完成租房
roomAgency.finish();
}
}
静态代理缺点
如果 RealSubject 行为变更了,Subject 不满足需求了,就可能需要新的接口了,ProxySubject 也需要跟着变动。
但是如果小明是想要买房而不是租房,这时房产中介还能满足小明的需求吗?很显然不能了,因为这个房产中介它只有替人租房的能力,没有替人买房的能力,这时就需要更换租房接口为买房接口,再定义一个专门买房的的房产中介,你会发现我每次更换接口,都需要更换代理类,这就是静态模式的缺点,只能为给定接口下的实现类做代理,如果接口不同就需要定义不同的代理类,随着系统的复杂度增加,就会很难维护这么多代理类和被代理类之间的关系,这时动态代理就应运而生,当需要频繁的更换接口,更换代理类时,采用动态代理是一个更好的选择,动态代理可以通过一个代理类来代理 N 多个被代理类,它在更换接口时,不需要重新定义代理类,因为动态代理不需要根据接口提前定义代理类,它把代理类的创建推迟到代码运行时来完成。
动态代理
代理
反射 + 注解 + 动态代理实现 onClick 和 onLongClick 的注入
- 注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventType {
Class listenerType(); // OnClickListener/OnLongClickListener
String listenerSetter(); // setOnClickListener/setOnLongClickListener
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventType(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener")
public @interface OnClick {
int[] value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventType(listenerType = View.OnLongClickListener.class, listenerSetter = "setOnLongClickListener")
public @interface OnLongClick {
int[] value();
}
- 注入工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class InjectUtils {
public static void injectEvent(Activity activity) {
Class<? extends Activity> activityClass = activity.getClass();
Method[] declaredMethods = activityClass.getDeclaredMethods();
for (Method method : declaredMethods) {
//获得方法上所有注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
//注解类型
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType.isAnnotationPresent(EventType.class)) {
EventType eventType = annotationType.getAnnotation(EventType.class);
// OnClickListener.class
Class listenerType = eventType.listenerType();
//setOnClickListener
String listenerSetter = eventType.listenerSetter();
try {
// 不需要关心到底是OnClick 还是 OnLongClick
Method valueMethod = annotationType.getDeclaredMethod("value");
int[] viewIds = (int[]) valueMethod.invoke(annotation);
method.setAccessible(true);
ListenerInvocationHandler<Activity> handler = new ListenerInvocationHandler(activity, method);
Object listenerProxy = Proxy.newProxyInstance(listenerType.getClassLoader(),
new Class[]{listenerType}, handler);
// 遍历注解的值
for (int viewId : viewIds) {
// 获得当前activity的view(赋值)
View view = activity.findViewById(viewId);
// 获取指定的方法(不需要判断是Click还是LongClick)
// 如获得:setOnClickLisnter方法,参数为OnClickListener
// 获得 setOnLongClickLisnter,则参数为OnLongClickLisnter
Method setter = view.getClass().getMethod(listenerSetter, listenerType);
// 执行方法
setter.invoke(view, listenerProxy); //执行setOnclickListener里面的回调 onclick方法
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
/**
* 还可能在自定义view注入,所以是泛型: T = Activity/View
*/
static class ListenerInvocationHandler<T> implements InvocationHandler {
private Method method;
private T target;
public ListenerInvocationHandler(T target, Method method) {
this.target = target;
this.method = method;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return this.method.invoke(target, args);
}
}
}
- 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InjectUtils.injectEvent(this);
}
@OnClick({R.id.btn1, R.id.btn2})
public void click(View view) {
switch (view.getId()) {
case R.id.btn1:
Log.i(TAG, "click: 按钮1");
break;
case R.id.btn2:
Log.i(TAG, "click: 按钮2");
break;
}
}
@OnLongClick({R.id.btn1, R.id.btn2})
public boolean longClick(View view) {
switch (view.getId()) {
case R.id.btn1:
Log.i(TAG, "longClick: 按钮1");
break;
case R.id.btn2:
Log.i(TAG, "longClick: 按钮2");
break;
}
return false;
}
}