策略模式的使用場景:
- 針對同一問題的多種處理方式,僅僅是具體行爲有差別時;
- 需要安全地封裝多種同一類型的操作時;
- 同一抽象類有多個子類,而客戶端需要使用 if-else 或者 switch-case 來選擇具體子類時。
我們假設電商場景,購物時根據會員等級不同付款時優惠的價格也不同
最早我們沒有使用任何策略模式,最原始的代碼如下:
@Service
public class CalculateStrategyService {
private static final Logger logger = LoggerFactory.getLogger(CalculateStrategyService.class);
/**
* 根據會員等級計算費用
* @param money
* @param type
* @return
*/
public double getCalMoneyResult(long money, int type) {
if (money < 1000) {
logger.info("費用低於1000,不參與會員折扣");
return money;
}
if (type == 1) {
// 白銀會員優惠50
return money - 50;
} else if (type == 2) {
// 黃金會員8折
return money * 0.8;
} else if (type == 3) {
// 白金會員8折後再優惠20
return (money * 0.8) - 20;
} else if (type == 4) {
// 鑽石會員5折
return money * 0.5;
} else {
return money;
}
}
}
這種寫法直觀,清晰隨便一個人都能看懂,但是隨着業務原來越多需求越來越複雜,可能在各個會員等級裏面還有其它操作,這個時候就會發現代碼特別的冗長後期維護起來特別痛苦要不停的往下翻找到對應的位置,顯然這不適合後期維護。這個時候策略模式就出來了,把每個會員等級單獨抽出來。
首先新建一個計算費用的接口類
public interface MemberStrategy {
/**
* 計算費用
* @param money
* @return
*/
double calMoney(long money);
}
然後分別新建對應的會員等級接口實現類
@Service
public class OrdinaryMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(OrdinaryMemberStrategy.class);
/**
* 普通會員不打折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("普通會員不打折");
return money;
}
}
@Service
public class SilverMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(SilverMemberStrategy.class);
/**
* 白銀會員優惠50元
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("白銀會員優惠50");
return money - 50;
}
}
@Service
public class GoldMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(GoldMemberStrategy.class);
/**
* 黃金會員8折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("黃金會員8折");
return money * 0.8;
}
}
@Service
public class PlatinumMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(PlatinumMemberStrategy.class);
/**
* 白金會員打8折,並且優惠20
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("白金會員打8折,並且優惠20");
return (money - 20) * 0.8;
}
}
@Service
public class DiamondMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(DiamondMemberStrategy.class);
/**
* 白鑽石會員打5折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("鑽石會員打5折");
return money * 0.5;
}
}
然後計算費用接口修改成如下
@Service
public class CalculateStrategyService {
private static final Logger logger = LoggerFactory.getLogger(CalculateStrategyService.class);
@Autowired
OrdinaryMemberStrategy ordinaryStrategyService;
@Autowired
SilverMemberStrategy silverStrategyService;
@Autowired
GoldMemberStrategy goldStrategyService;
@Autowired
PlatinumMemberStrategy platinumStrategyService;
@Autowired
DiamondMemberStrategy diamondMemberStrategy;
/**
* 根據會員等級計算費用
* @param money
* @param type
* @return
*/
public double getCalMoneyResult(long money, int type) {
if (money < 1000) {
logger.info("費用低於1000,不參與會員折扣");
return money;
}
MemberStrategy memberStrategy;
if (type == 1) {
memberStrategy = silverStrategyService;
} else if (type == 2) {
memberStrategy = goldStrategyService;
} else if (type == 3) {
memberStrategy = platinumStrategyService;
} else if (type == 4) {
memberStrategy = diamondMemberStrategy;
} else {
memberStrategy = ordinaryStrategyService;
}
return memberStrategy.calMoney(money);
}
}
這種方式就化解了之前代碼全寫在if else邏輯判斷裏面了,改爲對應的會員等級邏輯處理各自有自己的類,後期要修改那個會員等級的邏輯直接去對應的類方法裏面修改就好了。
但是這樣還是有if else 判斷並沒有達到完美的要求,這個時候就需要引進工廠模式了。
修改會員策略接口增加一個獲取會員等級的接口
public interface MemberStrategy {
/**
* 計算費用
* @param money
* @return
*/
double calMoney(long money);
/**
* 獲取會員等級
* @return
*/
int getType();
}
對應的各個會員等級接口實現類也修改下,把會員等級的type寫進getType方法裏面
@Service
public class OrdinaryMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(OrdinaryMemberStrategy.class);
/**
* 普通會員不打折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("普通會員不打折");
return money;
}
@Override
public int getType() {
return 0;
}
}
@Service
public class SilverMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(SilverMemberStrategy.class);
/**
* 白銀會員優惠50元
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("白銀會員優惠50");
return money - 50;
}
@Override
public int getType() {
return 1;
}
}
@Service
public class GoldMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(GoldMemberStrategy.class);
/**
* 黃金會員8折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("黃金會員8折");
return money * 0.8;
}
@Override
public int getType() {
return 2;
}
}
@Service
public class PlatinumMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(PlatinumMemberStrategy.class);
/**
* 白金會員打8折,並且優惠20
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("白金會員打8折,並且優惠20");
return (money - 20) * 0.8;
}
@Override
public int getType() {
return 3;
}
}
@Service
public class DiamondMemberStrategy implements MemberStrategy {
private static final Logger logger = LoggerFactory.getLogger(DiamondMemberStrategy.class);
/**
* 白鑽石會員打5折
* @param money
* @return
*/
@Override
public double calMoney(long money) {
logger.info("鑽石會員打5折");
return money * 0.5;
}
@Override
public int getType() {
return 4;
}
}
我們在每個會員等級接口實現類裏面設置了等級對應的type,接下來我們新增一個會員等級工廠類
public class MemberStrategyFactory {
private Map<Integer, MemberStrategy> map;
public MemberStrategyFactory() {
List<MemberStrategy> list = new ArrayList<>();
list.add(new OrdinaryMemberStrategy());
list.add(new SilverMemberStrategy());
list.add(new GoldMemberStrategy());
list.add(new PlatinumMemberStrategy());
list.add(new DiamondMemberStrategy());
map = list.stream().collect(Collectors.toMap(MemberStrategy::getType, memberStrategy -> memberStrategy));
}
public static class Holder {
public static MemberStrategyFactory instance = new MemberStrategyFactory();
}
public static MemberStrategyFactory getInstance() {
return Holder.instance;
}
public MemberStrategy get(Integer type) {
return map.get(type);
}
}
工廠類裏面添加所有的會員等級的策略接口實現,最後我們的計算方法接口修改爲
@Service
public class CalculateStrategyService {
private static final Logger logger = LoggerFactory.getLogger(CalculateStrategyService.class);
/**
* 根據會員等級計算費用
* @param money
* @param type
* @return
*/
public double getCalMoneyResult(long money, int type) {
if (money < 1000) {
logger.info("費用低於1000,不參與會員折扣");
return money;
}
MemberStrategy memberStrategy = MemberStrategyFactory.getInstance().get(type);
if (memberStrategy == null) {
throw new IllegalStateException("會員等級類型不符合");
}
return memberStrategy.calMoney(money);
}
}
使用了工廠策略後就減少了if else了同時也將各個類型的邏輯處理分開了