参考资料:
https://blog.csdn.net/gdutxiaoxu/article/details/81394050
https://blog.csdn.net/lmj623565791/article/details/79278864
https://blog.csdn.net/liujiahan629629/article/details/19428485
http://www.jianshu.com/p/b28fbc388d30
目录
一、两种代理模式
代理模式分为静态代理与动态代理
- 静态代理
在编译期生成代理对象
代码示例:
//Subject类,定义了RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以
使用Proxy。
public interface Subject {
public void request();
}
//实体类RealSubject,定义Proxy所代表的真实实体。
public class RealSubject implements Subject {
public void request() {
System.out.println("真实的请求");
}
}
//代理类Proxy,保存一个引用使得代理可以访问实体。
public class Proxy implements Subject {
RealSubject realSubject;
public void request() {
if(realSubject == null){
realSubject = new RealSubject();
}
realSubject.request();
}
}
客户端调用:
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
}
- 动态代理
在运行时生成代理对象,可以拦截方法,可以中途修改方法的参数,决定方法要不要执行等(return null不执行)。
动态代理技术方案:
1)通过JDK提供的API实现的
2)通过CGLIB库,生成的代理类与被代理类就是父子关系。
代码示例:
final Singer singer = new Singer();
Humen proxy = (Humen) Proxy.newProxyInstance(singer.getClass().getClassLoader()
, singer.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println(method.getName());
System.out.println(Arrays.asList(args));
//可能对args做一些手脚
//指定singer为被代理对象
return method.invoke(singer, args);
}
});
proxy.sing(40);
proxy.dance(30);
二、代理模式应用场景
1)数据库事务。数据库当中的事务,代码头是开启事务,代码后面是回滚和释放释放。可以把业务代码放在被代理类中执行,代码头与代码后放在代理类中执行。
2)统计方法的执行时间。
三、代理模式实战演练
1)在编辑资料的时候,检测数据是否被修改过。
构造一个Map的代理类,对put方法进行代理。
```java
/**
* Created by xx on 2016/11/8 0008.
* func:
* 代理带监听的Map,在退出时,判断是否修改了数据。
*/
public class ProxyObservableMap<K,V> {
private Map<K,V> map;
private boolean changed = false;
<pre><code>//在业务逻辑层调用,如果保存了数据,就消除数据已经改变的标记。
public void clearCache(){
changed = false;
}
public void put(K key,V value){
if(map == null){
map = new HashMap<>();
}
if(map.containsKey(key)){
if(!map.get(key).equals(value)){
changed = true;
}
}
if(changed) return;
map.put(key,value);
}
public boolean isChanged() {
return changed;
}
</code></pre>
}
<pre><code class="line-numbers"> 一般的做法应该是先把原始的数据存入一个集合,在保存的时候再次把数据存入一个集合,然后对比两个集合得到答案。作为优秀的程序员,循环遍历一定是最后的办法。
#### 2)公共参数配置
定义的retrofit网络接口,都是通过map来传递参数的,但是所有的接口都有一个公共的参数。为了解决每次传参的时候,公共参数不重复传入。
代理类:主要是代理了MAP类的put方法(代理不一定要完全代理!!!)
```java
/**
* Created by xx on 2017/12/8.
* MAP代理map,封装共同信息。
*/
public class CZMap {
private Map<String,Object> realMap;
public CZMap() {
realMap = new HashMap<String, Object>();
realMap.put(Constants.SystemConfigKey.TERMINAL_ID, SystemPropertyConfigUtil.getProperty(Constants.SystemConfigKey.TERMINAL_ID));
realMap.put(Constants.SystemConfigKey.MANUFACTURER_ID, SystemPropertyConfigUtil.getProperty(Constants.SystemConfigKey.MANUFACTURER_ID));
if(OperatorManager.getInstance().isHasLogin()){
realMap.put("operatorName", OperatorManager.getInstance().getOperatorInfoBean().getOperatorName());
realMap.put("authToken", SPUtil.get(SPUtilKeys.AUTH_TOKEN,""));
}
}
public void put(String key,Object value){
realMap.put(key,value);
}
public Map getRealMap(){
return realMap;
}
调用代理MAP类:
```java
@Override
public void savePic(String base64, String fileName, String returnId, Callback<SavePicResultBean> callBack) {
CZMap czMap = new CZMap();
czMap.put("base64", base64);
czMap.put("fileName", fileName);
czMap.put("returnId", returnId);
MainNetClient.getInstance().savePic(czMap.getRealMap(), new TimeCountCallBackHandler(callBack));
}
<pre><code class="line-numbers"> #### 3)去重操作
曾经做了1个签到系统,由于之前的bug和服务也未做签到的去重操作,导致解析出来的日期实体集合有重复的元素。我就想要写一个代理类List,保证像Set一样保证无重复的元素。那有人会问:为什么不直接采用HashSet呢?
HashSet保证元素的唯一性,是根据元素的hashCode和equals方法来综合判断的,但是我将存储的这个元素名叫CalendarDay,是一个第3方的jar包中的类,因此无法复写它的这2个方法。当然直接去修改这个类,也违反了设计模式中的“开闭原则”。下面就是我这个代理类逐步设计的思路:
```java
/**
* Created by 陈章 on 2016/11/9 0009.
* func:
* List代理类:可以模仿map去重的功能。
*/
public abstract class ProxyUniqList<T> {
protected List<T> realObjectList;
protected List<Adapter> adapterList;
public ProxyUniqList() {
if(realObjectList == null){
realObjectList = new ArrayList<>();
}
if(adapterList == null){
adapterList = new ArrayList<>();
}
}
public void add(T value){
if(adapterList.size() > 0){
if(adapterList.get(adapterList.size() - 1).equals(getAdapter(value))){
return;
}
}
realObjectList.add(value);
adapterList.add(getAdapter(value));
}
public abstract Adapter<T> getAdapter(T value);
public T get(int position){
return realObjectList.get(position);
}
public void clear(){
realObjectList.clear();
}
public int size(){
return realObjectList.size();
}
//适配器,判断两个元素是否“相等”。
public static abstract class Adapter<T>{
protected T instance;
public Adapter(T instance) {
this.instance = instance;
}
protected abstract long getUniqId();
public T getInstance() {
return instance;
}
public boolean equals(Adapter t){
return this.getUniqId()==t.getUniqId();
}
}
}
可以看到上面这个类,考虑到不光要唯一存储CalendarDay这个类,可能在其它地方或别的项目中还会用到,所以加了泛型。
另外一个很纠结的问题就是ProxyUniqList这个类,它存储的元素T,怎么判别它们是否“相等”。
结合“适配器”设计模式(专门针对已有的无法修改的类),我在ProxyUniqList的内部定义了一个适配器类,通过getUniqId()抽象方法返回一个id判断元素是否唯一,之所以getUniqId()要抽象,因为不同的元素构造uniqid的方式不唯一。
再者可以看到ProxyUniqList方法add,在这个方法中,每存储一个元素,我都会用一层Adapter封装这个元素,并将adapter存入到一个集合中。在add方法最前面,我会判断对应索引的adapter,从而判断2个元素是否相等。但是此时adapter还是抽象的,无法实例化,于是利用“模板方法”设计模式在ProxyUniqList中抽象出一个方法getAdapter(),这样就避免了add方法全部抽象,将抽象局部化到getAdapter方法。
基类写好了,针对CalendarDay这个类的集合的代理类:
public class CalendarDayProxyUniqList extends ProxyUniqList<CalendarDay>{
@Override
public Adapter<CalendarDay> getAdapter(CalendarDay value) {
return new CalendarDayAdapter(value);
}
class CalendarDayAdapter extends ProxyUniqList.Adapter<CalendarDay> {
public CalendarDayAdapter(CalendarDay instance) {
super(instance);
}
@Override
protected long getUniqId() {
return instance.getCalendar().getTimeInMillis();
}
}
}
这样,将原来代码里的ArrayList替换成CalendarDayProxyUniqList,用法与arraylist用法一样,就实现了类似Set的保证元素唯一的功能。
0 条评论