对于策略模式的概念,及优缺点,对于设计模式应用的应该都有所了解,这里暂不赘述,本文主要从应用实战的角度去处理。
少啰嗦,先上图:
在目前的即时通信中,消息的类型多种多样,对于不同的消息类型我们可能需要在不同的代码逻辑分支(if else判断)上去处理,并且随着消息类型的增加随,业务逻辑的扩展,会造成代码的臃肿,难以阅读,难以维护,这个时候我们可以想到的一种应用模式就是“策略模式”。
下面进入具体的应用代码示例:
package com.windfallsheng.strategypatterncase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.windfallsheng.strategypatterncase.action.MsgHelper;
import com.windfallsheng.strategypatterncase.entity.MsgEntity;
import com.windfallsheng.strategypatterncase.util.TimeUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* @author lzsheng
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private final String TAG = "MainActivity";
private int msgId;
private int msgTextIndex;
private int msgImgIndex;
private int msgShareIndex;
private Random r;
private RecyclerView mRecyclerView;
private MsgListAdapter mMsgListAdapter;
private LinearLayoutManager mLinearLayoutManager;
private List<MsgEntity> mMsgList;
private TextView text, share, img;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.recyclerview);
text = findViewById(R.id.text);
share = findViewById(R.id.share);
img = findViewById(R.id.img);
text.setOnClickListener(this);
share.setOnClickListener(this);
img.setOnClickListener(this);
mLinearLayoutManager = new LinearLayoutManager(MainActivity.this);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mMsgListAdapter = new MsgListAdapter(MainActivity.this);
mMsgList = new ArrayList<>();
mMsgListAdapter.setMsgList(mMsgList);
mRecyclerView.setAdapter(mMsgListAdapter);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.text: {
String textJson = "{" +
"\"content\": \"这是第" + ++msgTextIndex + "条文本内容的消息\"" +
"}";
MsgEntity msgEntity = buildMsgEntity(1, textJson);
MsgEntity msg = MsgHelper.obtain().buildMsgObj(msgEntity);
Log.d(TAG, "text:msg=" + msg.toString());
addMsgAndRefresh(msg);
}
break;
case R.id.share: {
String shareJson = "{" +
"\"title\": \"这是第" + ++msgShareIndex + "条分享内容的消息\"," +
"\"desc\": \"这是我分享的内容,Just a little code farmer." +
"Just a little code farmer.Just a little code farmer.\"," +
"\"imgUrl\": \"我是\"" +
"}";
MsgEntity msgEntity = buildMsgEntity(2, shareJson);
MsgEntity msg = MsgHelper.obtain().buildMsgObj(msgEntity);
Log.d(TAG, "share:msg=" + msg.toString());
addMsgAndRefresh(msg);
}
break;
case R.id.img: {
String imgJson = "{" +
"\"desc\": \"这是第" + ++msgImgIndex + "条图片内容消息\"," +
"\"resUrl\": \"https://../../*.jpg\"," +
"\"type\": 1" +
"}";
MsgEntity msgEntity = buildMsgEntity(3, imgJson);
MsgEntity msg = MsgHelper.obtain().buildMsgObj(msgEntity);
Log.d(TAG, "img:msg=" + msg.toString());
addMsgAndRefresh(msg);
}
break;
default:
break;
}
}
private void addMsgAndRefresh(MsgEntity msg) {
mMsgList.add(msg);
if (mMsgListAdapter != null) {
mMsgListAdapter.notifyDataSetChanged();
}
if (mRecyclerView != null && mLinearLayoutManager != null) {
int itemCount = mLinearLayoutManager.getItemCount();
Log.d(TAG, "addMsgAndRefresh:itemCount=" + itemCount);
mRecyclerView.scrollToPosition(itemCount - 1);
}
}
/**
* 模拟网络传输过来的不同类型的消息实例;
*
* @param json
* @return
*/
private MsgEntity buildMsgEntity(int msgType, String json) {
if (r == null) {
r = new Random();
}
int msgDirection = r.nextInt(2);
Log.d(TAG, "msgDirection=" + msgDirection);
String msgFrom;
String msgto;
if (msgDirection == 1) {
// 发送的消息;
msgFrom = "me";
msgto = "friend";
} else {
// 接收的消息;
msgFrom = "friend";
msgto = "me";
}
return new MsgEntity(++msgId, msgType, json, null,
msgFrom, msgto, msgDirection, 0, TimeUtils.getStrDate());
}
}
buildMsgEntity();方法用来模拟网络传输过来的不同类型的消息实例;
根据网络传输过来的不同类型的消息,只需要按照如下的方式处理,就可以构建出来前端需要的消息实例;
MsgEntity msg = MsgHelper.obtain().buildMsgObj(msgEntity);
下面来看 MsgHelper 类是如何处理的:
package com.windfallsheng.strategypatterncase.action;
import android.util.Log;
import com.windfallsheng.strategypatterncase.builder.BaseMsgBuider;
import com.windfallsheng.strategypatterncase.builder.IMsgBuilder;
import com.windfallsheng.strategypatterncase.builder.MsgShareBuilder;
import com.windfallsheng.strategypatterncase.builder.MsgTextBuilder;
import com.windfallsheng.strategypatterncase.builder.MsgImgBuilder;
import com.windfallsheng.strategypatterncase.entity.MsgEntity;
/**
* @author lzsheng
*/
public class MsgHelper {
private final String TAG = "MsgHelper";
/**
* 当前实例的属性是否可重置,且实例可被重用,fase为不可用,true为可用;
*/
public boolean flag;
public MsgHelper() {
}
/**
* 获取一个{@link MsgHelper}实例
* <p>
* 如果内存中的实例可被重用,则会返回内存中的实例对象,否则创建新实例;
*
* @return 返回一个可用的实例对象;
*/
public static MsgHelper obtain() {
MsgHelper msgHelper = (MsgHelper) CacheService.getInstance().getMsgCacheObj(MsgHelper.class);
// MsgHelper msgHelper = new MsgHelper();
return msgHelper;
}
public MsgHelper recycle() {
this.flag = false;
return this;
}
/**
* 根据不同的消息类型,创建不同的消息构建者实例,
* <p>
* 如果这里的业务增多,也可以结合工厂方法模式处理不同对象的生成业务;
*
* @param msgType
* @return
*/
private IMsgBuilder initMsgBuilder(int msgType) {
IMsgBuilder msgBuilder = null;
switch (msgType) {
case 1:
msgBuilder = (IMsgBuilder) CacheService.getInstance().getMsgCacheObj(MsgTextBuilder.class);
// msgBuilder = new MsgTextBuilder();
break;
case 2:
msgBuilder = (IMsgBuilder) CacheService.getInstance().getMsgCacheObj(MsgShareBuilder.class);
// msgBuilder = new MsgShareBuilder();
break;
case 3:
msgBuilder = (IMsgBuilder) CacheService.getInstance().getMsgCacheObj(MsgImgBuilder.class);
// msgBuilder = new MsgVoiceBuilder();
break;
default:
break;
}
Log.d(TAG, "method:initMsgBuilder#return msgBuilder=" + msgBuilder);
return msgBuilder;
}
/**
* 创建消息构建者实例,并构建消息对象返回;
*
* @param msgEntity
* @return
*/
public MsgEntity buildMsgObj(MsgEntity msgEntity) {
final IMsgBuilder msgBuilder = initMsgBuilder(msgEntity.getMsgType());
if (msgBuilder == null) {
return null;
}
final MsgEntity msgInfo = msgBuilder.buildMsgObj(msgEntity);
synchronized (this) {
// 当前实例对象任务已完成,将当前相关实例的的flag属性置为true,取可重用状态;
((BaseMsgBuider) msgBuilder).flag = true;
flag = true;
Log.d(TAG, "method:buildMsgObj#msgBuilder=" + msgBuilder);
Log.d(TAG, "method:buildMsgObj#this=" + this);
return msgInfo;
}
}
// @Override
// public String toString() {
// return "MsgHelper{" +
// "flag=" + flag +
// '}';
// }
}
可以看到,initMsgBuilder(); 方法根据不同的消息类型创建了不同的消息处理策略类,而它们都实现了统一的接口;
创建消息构建的接口,不同一消息类型处理类要实现这个统一的接口:
package com.windfallsheng.strategypatterncase.builder;
import com.windfallsheng.strategypatterncase.entity.MsgEntity;
/**
* @author lzsheng
* <p>
* 构建不同类型消息实例的接口;
*/
public interface IMsgBuilder {
public MsgEntity buildMsgObj(MsgEntity msgEntity);
}
我们定义了3种类型的消息处理策略类:
首先为不同消息的一些相同的处理业务定义一个抽象类;
package com.windfallsheng.strategypatterncase.builder;
/**
* @author lzsheng
* <p>
* 增加一个简单的基类,用来处理共同的业务;
*/
public abstract class BaseMsgBuider implements IMsgBuilder {
/**
* 当前实例的属性是否可重置,且实例可被重用,fase为不可用,true为可用;
*/
public boolean flag;
public BaseMsgBuider recycle() {
this.flag = false;
return this;
}
}
文本类型消息处理策略类:
package com.windfallsheng.strategypatterncase.builder;
import com.google.gson.Gson;
import com.windfallsheng.strategypatterncase.entity.MsgEntity;
import com.windfallsheng.strategypatterncase.entity.MsgTextBody;
/**
* @author lzsheng
* <p>
* 在这里对不同的消息类型的处理业务只是简单的演示,实际情况要复杂;
*/
public class MsgTextBuilder extends BaseMsgBuider {
@Override
public MsgEntity buildMsgObj(MsgEntity msgEntity) {
MsgTextBody msgTextBody = new Gson().fromJson(msgEntity.getMsgJson(), MsgTextBody.class);
msgEntity.setMsgBody(msgTextBody);
msgEntity.setMsgJson(null);
// do something else.
return msgEntity;
}
}
分享类型消息处理策略类:
package com.windfallsheng.strategypatterncase.builder;
import com.google.gson.Gson;
import com.windfallsheng.strategypatterncase.entity.MsgEntity;
import com.windfallsheng.strategypatterncase.entity.MsgShareBody;
/**
* @author lzsheng
* <p>
* 在这里对不同的消息类型的处理业务只是简单的演示,实际情况要复杂;
*/
public class MsgShareBuilder extends BaseMsgBuider {
@Override
public MsgEntity buildMsgObj(MsgEntity msgEntity) {
MsgShareBody msgShareBody = new Gson().fromJson(msgEntity.getMsgJson(), MsgShareBody.class);
msgEntity.setMsgBody(msgShareBody);
msgEntity.setMsgJson(null);
// do something else.
return msgEntity;
}
}
图片类型消息处理策略类:
package com.windfallsheng.strategypatterncase.builder;
import com.google.gson.Gson;
import com.windfallsheng.strategypatterncase.entity.MsgEntity;
import com.windfallsheng.strategypatterncase.entity.MsgImgBody;
/**
* @author lzsheng
* <p>
* 在这里对不同的消息类型的处理业务只是简单的演示,实际情况要复杂;
*/
public class MsgImgBuilder extends BaseMsgBuider {
@Override
public MsgEntity buildMsgObj(MsgEntity msgEntity) {
MsgImgBody msgImgBody = new Gson().fromJson(msgEntity.getMsgJson(), MsgImgBody.class);
msgEntity.setMsgBody(msgImgBody);
msgEntity.setMsgJson(null);
// do something else.
return msgEntity;
}
}
这里自己作了一个简单的对应用对象实例的缓存类,以便让频繁使用对象实例得以复用;
package com.windfallsheng.strategypatterncase.action;
import android.util.Log;
import com.windfallsheng.strategypatterncase.builder.BaseMsgBuider;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author lzsheng
* <p>
* 这里的缓存策略可能根据业务需求处理;
*/
public class CacheService {
private final String TAG = "CacheService";
private ConcurrentHashMap CACHE = new ConcurrentHashMap();
private static class SingletonHolder {
static CacheService INSTANCE = new CacheService();
}
public static CacheService getInstance() {
return SingletonHolder.INSTANCE;
}
public void cacheObj(Object o) {
String clazzName = o.getClass().getName();
Log.d(TAG, "method:cacheObj#clazzName=" + clazzName);
CACHE.put(clazzName, o);
}
public Object getObj(Class<?> clazz) {
String name = clazz.getName();
Object obj = CACHE.get(name);
Log.d(TAG, "method:getObj#obj=" + obj);
return obj;
}
public Object getMsgCacheObj(Class<?> clazz) {
String clazzName = clazz.getName();
Log.d(TAG, "method:getMsgCacheObj#clazzName=" + clazzName);
Object obj = CACHE.get(clazzName);
Log.d(TAG, "method:getMsgCacheObj#CACHE#obj=" + obj);
if (obj != null) {
// 获取到实例后,就是要使用当前实例,所以将重置对象所有属性;
// 同时flag=false时,当前实例也就不能同时被其它调用,只能等当前实例的工作完成,才能重用当前对象;
if (obj instanceof BaseMsgBuider) {
((BaseMsgBuider) obj).recycle();
} else if (obj instanceof MsgHelper) {
((MsgHelper) obj).recycle();
}
} else {
try {
obj = clazz.newInstance();
cacheObj(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
Log.d(TAG, "method:getMsgCacheObj#newInstance#obj=" + obj);
}
return obj;
}
}
以上只是简单的演示,以具体的IM处理消息的业务为例,在具体的项目开发中,不同消息类型的业务处理是复杂的,不同策略类内部的业务也是复杂的,经过策略模式的业务分离,将不同类型的业务就封装到了各自的业务模块,让整体的业务流程更加清晰,即便于代码的阅读,也便于后期的业务开发扩展和维护。
由于作者水平有限,语言描述及代码实现中难免有纰漏,望各位看官多提宝贵意见!
Hello , World !
感谢所有!