對於策略模式的概念,及優缺點,對於設計模式應用的應該都有所瞭解,這裏暫不贅述,本文主要從應用實戰的角度去處理。
少囉嗦,先上圖:
在目前的即時通信中,消息的類型多種多樣,對於不同的消息類型我們可能需要在不同的代碼邏輯分支(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 !
感謝所有!