SpringBootポリシーモードを使用してif elseを殺す方法
シーン:いくつかのデータを受信して処理する必要がある場合、それらは異なるチャネル(例えばテンセント、トップバー)から来ているため、異なるチャネルに必要な処理方法が異なるので、以下に簡単なDemoを書いてこのシーンを実現します.
解決策
例1:簡単な例
1、まずGeneralChannelRule基礎規則抽象クラスを構築し、抽象メソッドprocess()を定義し、異なるチャネルでこの抽象メソッドを実現する必要がある.
2、テンセントのルールクラスを編纂し、テンセントのルートデータに対する処理ロジックを定義する
3.ヘッダーのルールクラスを作成し、ヘッダーデータに対する具体的な処理ロジックを定義する
4、簡単な列挙クラスを作る
5、ルールを使用してデータを処理する.
解析:上記の方法では、2つの欠点があります.
新しいチャネルを追加する必要がある場合は、mainメソッドの論理を修正して調整する必要があります.これは設計モードにおけるオープンクローズ規則に反する.開放閉鎖原則の核心的な思想は、ソフトウェアエンティティが拡張可能であり、修正不可能であることである.
すなわち,拡張に対しては開放的であり,修正に対しては閉鎖的である.
チャネルを追加すると、コードを変更するとif elseが大量に発生し、あまり優雅ではありません.以上の2つの問題を解決するために,列挙クラスを用いて巧みに最適化することができる.
新しい考え方1、次に列挙クラスを調整し、GeneralChannelRuleプロパティを追加し、対応するチャネルに対応するGeneralChannelRule実装クラスを構築し、match()マッチングメソッドを追加します. 2、書き換えプログラム
解析:列挙クラスを使用することで、列挙でkeyとルールの具体的な実装をバインドします.変更:
if-elseを減らすことで、コードをより優雅にすることができます.新しいチャネルが必要な場合は、特定のルール実装クラスを作成し、GeneralChannelRule抽象クラスを継承し、元のコードに変更することなく、列挙クラスに新しい列挙を追加する必要があります.これは開発閉鎖の原則に合致している.
最後に以上は列挙によってif-elseを巧みに殺す案であり、if-elseを減らすには興味深い解決策(状態設計モードなど)がたくさんあり、興味のある友人は関連資料を調べます.
例2:SpringBootでのポリシー・モードの運用
シーン
ユーザーの注文チャージ(注文支払いと同様)については、支付宝、微信、銀聯、財布(各アプリの口座残高)など、現在の支払い方法が非常に多いことが知られています.クエリーエンティティQuery:
サービスインタフェース:
従来の方法で実現
if elseまたはswitchを用いて条件判断を行う.
まとめ:従来の実装方式は非常に重く、コードは非常に簡潔ではなく、拡張性が悪いことがわかります.新しい支払い方法にアクセスする場合は、else ifを追加し続けるしかありません.
ポリシー・モード
Talk is cheap,show me the code.ポリシーモードを使用すると、サービスのコードがどのようになるかを見てみましょう.
Emmmm、確かに簡単です.コード量が少なく、簡潔になっただけでなく、新しい支払い方法でserviceImplのコードを変更する心配もなくなりました.
1、まず、支払いタイプに対応するプロセッサを識別するために、注釈をカスタマイズする必要があります.2、次に、1つのプロセッサを抽象化し、各支払い方式のプロセッサにこの抽象プロセッサを継承させ、handleメソッドを実現する: 3、実装クラス、例えば支付宝、微信、銀聯:注意:各プロセッサに@componentを加え、Spring管理に渡す. 4、そして最も重要なのは、クラスを作成し、ApplicationContextAwareインタフェースを実現し、setApplicationContextメソッドを書き換え、カスタム注釈@PayWayHandler付きBeanをスキャンして保存し、Serviceの取得を容易にすることです.
まとめ:ここで,ServiceImplは,フロントエンドからのpayWayに応じて対応するhandlerを選択して処理することができる.私はポリシーモデルを利用して複雑なif elseコードを簡略化し、拡張性が大幅に向上し、支払い方式の追加によってビジネスコードを修正する心配がなくなりました.
解決策
例1:簡単な例
1、まずGeneralChannelRule基礎規則抽象クラスを構築し、抽象メソッドprocess()を定義し、異なるチャネルでこの抽象メソッドを実現する必要がある.
public abstract class GeneralChannelRule {
public abstract void process();
}
2、テンセントのルールクラスを編纂し、テンセントのルートデータに対する処理ロジックを定義する
public class TencentChannelRule extends GeneralChannelRule
@Override
public void process() {
// Tencent
}
}
3.ヘッダーのルールクラスを作成し、ヘッダーデータに対する具体的な処理ロジックを定義する
public class TouTiaoChannelRule extends GeneralChannelRule
@Override
public void process() {
// TouTiao
}
}
4、簡単な列挙クラスを作る
public enum ChannelRuleEnum {
/**
*
*/
TOUTIAO("TOUTIAO"),
/**
*
*/
TENCENT("TENCENT"),
;
....
}
5、ルールを使用してデータを処理する.
public static void main(String[] args) {
// , TOUTIAO,
String sign = "TOUTIAO";
GeneralChannelRule rule;
//
if (ChannelRuleEnum.TENCENT.code.equals(sign)) {
rule = new TencentChannelRule();
} else if (ChannelRuleEnum.TOUTIAO.code.equals(sign)) {
rule = new TouTiaoChannelRule();
} else {
//
}
//
rule.process();
}
解析:上記の方法では、2つの欠点があります.
新しいチャネルを追加する必要がある場合は、mainメソッドの論理を修正して調整する必要があります.これは設計モードにおけるオープンクローズ規則に反する.開放閉鎖原則の核心的な思想は、ソフトウェアエンティティが拡張可能であり、修正不可能であることである.
すなわち,拡張に対しては開放的であり,修正に対しては閉鎖的である.
チャネルを追加すると、コードを変更するとif elseが大量に発生し、あまり優雅ではありません.以上の2つの問題を解決するために,列挙クラスを用いて巧みに最適化することができる.
新しい考え方
public enum ChannelRuleEnum {
/**
*
*/
TOUTIAO("TOUTIAO",new TouTiaoChannelRule()),
/**
*
*/
TENCENT("TENCENT",new TencentChannelRule()),
;
public String name;
public GeneralChannelRule channel;
ChannelRuleEnum(String name, GeneralChannelRule channel) {
this.name = name;
this.channel = channel;
}
//
public static ChannelRuleEnum match(String name){
ChannelRuleEnum[] values = ChannelRuleEnum.values();
for (ChannelRuleEnum value : values) {
if(value.name.equals(name)){
return value;
}
}
return null;
}
public String getName() {
return name;
}
public GeneralChannelRule getChannel() {
return channel;
}
}
public static void main(String[] args) {
String sign = "TOUTIAO";
ChannelRuleEnum channelRule = ChannelRuleEnum.match(sign);
GeneralChannelRule rule = channelRule.channel;
rule.process(sign);
}
解析:列挙クラスを使用することで、列挙でkeyとルールの具体的な実装をバインドします.変更:
if-elseを減らすことで、コードをより優雅にすることができます.新しいチャネルが必要な場合は、特定のルール実装クラスを作成し、GeneralChannelRule抽象クラスを継承し、元のコードに変更することなく、列挙クラスに新しい列挙を追加する必要があります.これは開発閉鎖の原則に合致している.
最後に以上は列挙によってif-elseを巧みに殺す案であり、if-elseを減らすには興味深い解決策(状態設計モードなど)がたくさんあり、興味のある友人は関連資料を調べます.
例2:SpringBootでのポリシー・モードの運用
シーン
ユーザーの注文チャージ(注文支払いと同様)については、支付宝、微信、銀聯、財布(各アプリの口座残高)など、現在の支払い方法が非常に多いことが知られています.クエリーエンティティQuery:
/**
* @author Howinfun
* @desc
* @date 2019/10/22
*/
@Data
public class ChargeQuery {
/** (ALI/WX/UNION) */
@NotBlank(message = " ",groups = PayWayNotBlank.class)
private String payWay;
/** */
@NotNull(message = " ",groups = AmountNotNull.class)
private Double amount;
}
サービスインタフェース:
/**
* @author Howinfun
* @desc -
* @date 2019/10/30
*/
public interface ChargeRechargeService {
/**
*
* @param query
* @return
*/
Result recharge(ChargeQuery query);
/**
*
* @param rechargeCallBack
*/
Result rechargeCallBack(RechargeCallBack rechargeCallBack);
}
従来の方法で実現
if elseまたはswitchを用いて条件判断を行う.
/**
* @author Howinfun
* @desc
* @date 2019/10/30
*/
@Service
@AllArgsConstructor
@Slf4j
public class ChargeRechargeServiceImpl implements ChargeRechargeService {
private final CarUserMapper carUserMapper;
private final IncomePaymentMapper incomePaymentMapper;
private final RechargeRecordMapper rechargeRecordMapper;
private final PayWayHandlerContext payWayHandlerContext;
@Override
@Transactional(rollbackFor = Exception.class)
public Result recharge(ChargeQuery query) {
Result result = new Result();
// ......
// ......
if (PayConstant.PAY_WAY_WX.equals(query.getPayWay())){
//
// ......
}else if (PayConstant.PAY_WAY_ALI.equals(query.getPayWay())){
//
// ......
}else if (PayConstant.PAY_WAY_UNION_PAY.equals(query.getPayWay())){
//
// ......
}
return result;
}
}
まとめ:従来の実装方式は非常に重く、コードは非常に簡潔ではなく、拡張性が悪いことがわかります.新しい支払い方法にアクセスする場合は、else ifを追加し続けるしかありません.
ポリシー・モード
Talk is cheap,show me the code.ポリシーモードを使用すると、サービスのコードがどのようになるかを見てみましょう.
/**
* @author Howinfun
* @desc
* @date 2019/10/30
*/
@Service
@AllArgsConstructor
@Slf4j
public class ChargeRechargeServiceImpl implements ChargeRechargeService {
private final PayWayHandlerContext payWayHandlerContext;
@Override
@Transactional(rollbackFor = Exception.class)
public Result recharge(ChargeQuery query) {
return this.payWayHandlerContext.getHandlerInstance(query.getPayWay()).handler(query);
}
}
Emmmm、確かに簡単です.コード量が少なく、簡潔になっただけでなく、新しい支払い方法でserviceImplのコードを変更する心配もなくなりました.
1、まず、支払いタイプに対応するプロセッサを識別するために、注釈をカスタマイズする必要があります.
/**
* @author Howinfun
* @desc ,
* @date 2019/11/2
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PayWayHandler {
String value();
}
/**
* @author Howinfun
* @desc
* @date 2019/11/2
*/
public abstract class AbstractPayWayHandler {
/**
*
* @param query
* @return
*/
abstract public Result handler(ChargeQuery query);
}
/**
* @author Howinfun
* @desc
* @date 2019/11/2
*/
@Component
@PayWayHandler("ALI")
@Slf4j
@AllArgsConstructor
public class AliPayWayHandler extends AbstractPayWayHandler {
// ....
@Override
public Result handler(ChargeQuery query) {
Result result = new Result();
// ......
return result;
}
}
/**
* @author Howinfun
* @desc
* @date 2019/11/2
*/
@Component
@PayWayHandler("WX")
@Slf4j
@AllArgsConstructor
public class WxPayWayHandler extends AbstractPayWayHandler {
// ....
@Override
public Result handler(ChargeQuery query) {
Result result = new Result();
// ......
return result;
}
}
/**
* @author Howinfun
* @desc
* @date 2019/11/2
*/
@Component
@PayWayHandler("UNION")
@Slf4j
@AllArgsConstructor
public class UnionPayWayHandler extends AbstractPayWayHandler {
// ....
@Override
public Result handler(ChargeQuery query) {
Result result = new Result();
// ......
return result;
}
}
/**
* @author Howinfun
* @desc
* @date 2019/11/2
*/
@Component
public class PayWayHandlerContext implements ApplicationContextAware {
@Autowired ApplicationContext applicationContext;
/** key PayWay,value class*/
private static final Map<String,Class> handlerMap = new HashMap<>(10);
public AbstractPayWayHandler getHandlerInstance(String payType){
Class clazz = handlerMap.get(payType);
if (clazz == null){
throw new CustomDeniedException(" ");
}
return (AbstractPayWayHandler) applicationContext.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// PayTypeHandler
Map<String,Object> beans = applicationContext.getBeansWithAnnotation(PayWayHandler.class);
if (beans != null && beans.size() > 0) {
for (Object serviceBean : beans.values()) {
String payType = serviceBean.getClass().getAnnotation(PayWayHandler.class).value();
handlerMap.put(payType, serviceBean.getClass());
}
}
}
}
まとめ:ここで,ServiceImplは,フロントエンドからのpayWayに応じて対応するhandlerを選択して処理することができる.私はポリシーモデルを利用して複雑なif elseコードを簡略化し、拡張性が大幅に向上し、支払い方式の追加によってビジネスコードを修正する心配がなくなりました.