設計模式之策略模式(如何優雅的去除if-else邏輯代碼)

                          設計模式之策略模式(如何優雅的去除if-else邏輯代碼)

    項目要求:

    新增一個數據接收的接口,接收來自不同渠道的數據進行個性化的處理。目前已知的渠道有360、百度,以後還會不斷擴展渠道,要求必須在同一個接口處理所有數據接收請求。

    大多數的實現是這樣的:

    

/**
	 * 點擊數據匹配接口
	 */
	@GetMapping(value = "/matchData", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	public JsonResult matchData(@RequestParam("imei") String imei, @RequestParam("event_type") String event_type, @RequestParam("channelid") String channelid) {
		try {
			Map<String,Object> map=new HashMap<String,Object>(2);
			map.put("imei", imei);
			map.put("event_type", event_type);
			if("01".equals(channelid)) {
				do360MatchData(map);
			}else if("02".equals(channelid)) {
				doBaiduMatchData(map);
			}else {
				doSomethingElse(map);
			}
			return JsonResult.getSuccess("點擊數據匹配");
		} catch (Exception e) {
            logger.error("點擊數據匹配失敗:" + e.getMessage(), e);
			return JsonResult.getInnerFail("9999", "點擊數據匹配失敗");
		}

    以上代碼實現有如下的缺點:

  • 不易於擴展,增加一個新的渠道需要改變原有的代碼,不符合開放封閉原則
  • 使用多重條件選擇語句,不符合面向對象設計思想
  • 代碼柔和在一起,不利於閱讀和維護,不符合單一職責原則。 

   針對以上代碼的缺點我們有啥好的改進方式嗎?

   有!我們可以利用策略模式來消除臃腫複雜的if-else。

   

   

策略模式定義

定義了一些平行的算法組,分別封裝起來,算法之間可以相互替換,此模式使算法的變化獨立於調用者之外

算法結構

  • 抽象策略角色(Strategy):這是一個抽象類或者接口,將算法的行爲進行封裝,所有的策略類都要實現該接口
  • 具體策略角色(ConcreteStrategy):封裝了具體的算法和行爲
  • 環境角色(Context):持有一個抽象策略的引用,並提供統一調用的入口

 

 代碼實現:

1:首先定義抽象的數據策略接口

package com.zte.service.primary;

import java.util.Map;

public interface IMatchServcie {
	
	
	
	void match(Map<String,Object> map);
	
	String getChannelId();

}

其中 match方法是執行具體匹配數據邏輯的接口,而getChannelId方法則有妙用,請往下看

2:定義具體策略角色360數據匹配角色

package com.zte.service.primary;

import java.util.Map;

import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSONObject;
import com.zte.model.TAdvertisementMatching;
import com.zte.model.TAdvertisementThrowin;
import com.zte.utils.DateUtil;

@Service("match360Service")
public class Match360ServiceImpl implements IMatchServcie {
	@Autowired
	private Logger logger;
	@Autowired
	private RestTemplate restTemplate;

	@Autowired
	private AdvertisementThrowinService advertisementThrowinService;
	
	public static final String CHANNEL360="22017001";

	@Override
	public void match(Map<String, Object> map) {
		try {
			doSomthing();
		} catch (Exception e) {
			logger.error("點擊數據匹配失敗:" + e.getMessage(), e);
		}

	}

	@Override
	public String getChannelId() {

		return CHANNEL360;
	}

}

 該類實現了抽象策略接口,實現了默認的方法,其中match方法實現自己的業務邏輯,getChannelId方法返回一個具體的常量,還記得我們的需求嗎?根據不同的渠道實現不同的數據匹配邏輯。我們如何實現,就是通過這個常量來匹配到具體的策略角色去處理。

3:環境角色類創建

package com.zte.service.primary;

import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;


@Service
public class MatchStrategyPattern {
	@Resource
    private List<IMatchServcie> matchServcieList;

	public void match(String channelId,Map<String,Object> map) {
		 Optional<IMatchServcie> optional = matchServcieList.stream()
                 .filter(service -> service.getChannelId().equals(channelId))
                 .findFirst();
		 optional.ifPresent(op->{optional.get().match(map);});
	}
	
	

}

從上述代碼我們可以看到,我們利用spring的自動注入,注入了一個類型位抽象策略接口的List,程序會自動將類型爲IMatchService的具體策略角色類注入到List中。而match方法則是暴露給外部的統一匹配方法。該方法通過外部傳入的渠道id字段通過接口方法getChaennlId返回的渠道id進行匹配,如果匹配中了就選用該角色進行邏輯處理。達到動態匹配的效果。

 

4:這個時候如果我們要加入一個新的數據匹配角色,我們只需要新建一個新類實現抽象策略方法,然後的然後程序就可以自動的跑起來,無需修改任何現有的文件。程序中也沒有了那個令人煩厭的if-else。

package com.zte.service.primary;

import java.util.Map;

import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSONObject;
import com.zte.model.TAdvertisementMatching;
import com.zte.model.TAdvertisementThrowin;
import com.zte.utils.DateUtil;

@Service("matchBaiduService")
public class MatchBaiduServiceImpl implements IMatchServcie {
	@Autowired
	private Logger logger;
	@Autowired
	private RestTemplate restTemplate;

	@Autowired
	private AdvertisementThrowinService advertisementThrowinService;
	
	public static final String CHANNELBAIDU="22017002";

	@Override
	public void match(Map<String, Object> map) {
		try {
			doSomthing();
		} catch (Exception e) {
			logger.error("點擊數據匹配失敗:" + e.getMessage(), e);
		}

	}

	@Override
	public String getChannelId() {

		return CHANNELBAIDU;
	}

}

策略模式的優缺點

優點

  • 易於擴展,增加一個新的策略只需要添加一個具體的策略類即可,基本不需要改變原有的代碼,符合開放封閉原則
  • 避免使用多重條件選擇語句,充分體現面向對象設計思想
  • 策略類之間可以自由切換,由於策略類都實現同一個接口,所以使它們之間可以自由切換
  • 每個策略類使用一個策略類,符合單一職責原則
  • 客戶端與策略算法解耦,兩者都依賴於抽象策略接口,符合依賴反轉原則
  • 客戶端不需要知道都有哪些策略類,符合最小知識原則

缺點

  • 策略模式,當策略算法太多時,會造成很多的策略類
  • 客戶端不知道有哪些策略類,不能決定使用哪個策略類,這點可以通過本文中的方式解決,也可以考慮使用IOC容器和依賴注入的方式來解決
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章