策略模式的應用案例,以即時通信中,對不同類型的消息的構建處理業務爲例,結合簡單的緩存業務

       對於策略模式的概念,及優缺點,對於設計模式應用的應該都有所瞭解,這裏暫不贅述,本文主要從應用實戰的角度去處理。

      少囉嗦,先上圖:

       在目前的即時通信中,消息的類型多種多樣,對於不同的消息類型我們可能需要在不同的代碼邏輯分支(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處理消息的業務爲例,在具體的項目開發中,不同消息類型的業務處理是複雜的,不同策略類內部的業務也是複雜的,經過策略模式的業務分離,將不同類型的業務就封裝到了各自的業務模塊,讓整體的業務流程更加清晰,即便於代碼的閱讀,也便於後期的業務開發擴展和維護。

GitHub地址

由於作者水平有限,語言描述及代碼實現中難免有紕漏,望各位看官多提寶貴意見!

Hello , World !

感謝所有!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章