深さ解析:コードの中のifelseをどのように置き換えるか、私の彼女はすべて見終わった!
普段、コードを書くときに、状況に応じて異なるビジネスロジックを処理する必要があります.最も多く使われているのはifとelseです.しかし、状況が多すぎると、多くの「if else」が現れます.これは、多くの残されたシステムで、1つの関数に数千行のコードが現れる可能性があります.もちろん、抽出方法やクラスで実現することができ、それぞれの状況を1つの方法や対応するクラスに渡して処理することができますが、コードがきれいに見えるだけでなく、「if else」が大量にあり、後に新しい論理がある場合、さらに多くの「if else」を追加し、根本的に問題を解決していません.
例えば、メール送信業務の実現は、夢網、玄武、アリ雲などの複数のメールプラットフォーム(メールチャネルと呼ぶ)に接続されるのが一般的です.例えば、アリクラウドメールの管理が厳しく、マーケティング文字付きのメールは送信させない場合、マーケティング類のメールは他のメールルートを使って送信する必要がある.2.あるメールプラットフォームのサービスが一時的に利用できない可能性があり、別のメールルートに切り替える必要がある.3.一部のメールプラットフォームは优遇があって、临时にこのメールのルートに切り替えてメールを送信する必要があります;4.
コード実装
上記のビジネスシーンは簡単に言えば、異なるメールチャネルに対して対応するメールプラットフォームインタフェースを呼び出してメール送信を実現する.メールチャネルは一般的にファイルに配置されたり、データベースに配置されたりする.
コードは次のように実装されます(次のすべてのコードは、キーロジック部分のサンプルコードにすぎないことに注意してください).
腐ったコードの例
メール送信クラスがあります.SmsSendServicesです.sendメソッドでメールを送信します.SmsSendServices.javaです.
ある日メールチャネルCが追加されたら、「else if...」を追加します.
他のメールチャネルを追加したら?また「else if...」?明らかにこのやり方は望ましくないし、SOLID原則の「開閉原則」--拡張に対して開放的で、変更に対して閉鎖的ではない.このように私たちは毎回元のコード(変更に対して閉鎖的ではない)を修正し、「if else」を絶えず追加する必要がある.
次に、コードを最適化します.
最適化コード1
SMSチャネルのインタフェースを定義しますSmsChannelService、すべてのSMSチャネルAPIがインタフェースを実現します.SMSチャネルインタフェースSmsChannelServices.java
SMSチャネルA SmsChannelServiceImplaA.java
メールチャネルB SmsChannelServiceImplB.java
すべてのメールチャネルを工場クラスで初期化
元のSmsSendServiceで異なるメールチャネルのインタフェースを呼び出します.元のSmsSendServiceクラスは以下のように最適化されています.
これにより、SmsSendServiceクラスは非常に簡潔になり、「if else」をクリアします.SmsSendServiceクラスを再度変更する必要がなく、メールチャネルCを追加する場合は、SmsSendServiceクラスを変更する必要はありません.SmsChannelServiceImplCを追加してSmsChannelServiceインタフェースを実装し、工場クラスSmsChannelFactoryにSmsChannelServiceImplCを初期化するコードを1行追加するだけです.
SMSチャネルCの実装を増やすSmsChannelServiceImplC.java
工場クラスの変更SmsChannelFactory.java
「if else」はやってしまったが、元のクラスSmsChannelFactoryを修正しなければならず、「開閉原則」を満たしていない.もっと良い方法はないだろうか.
springの依存注入を用いてコードをさらに最適化した.
最適化コード2
SmsChannelServiceインタフェースはgetChannelType()メソッドを追加することが重要です.
サブクラスはこのメソッドの実装を追加し、@Service注記を加えてspringコンテナを管理します.SmsChannelServiceImplaA.java
SmsChannelServiceImplB.java
SmsChannelFactoryクラスの変更:このステップも重要です.SmsChannelFactory.java
SmsSendServiceに@Service注記を追加します.SmsChannelFactorySmsSendService.javaに@Autowiredで注入します.
この場合、チャネルCを追加する必要がある場合は、本当にSmsChannelServiceImplCを追加するだけで、既存のコードを変更することなく、「開閉原則」に完全に従うことができます.
SmsChannelServiceImplC.java
まとめは上記の最適化によって「if else」をよく取り除き、「臭い」「長い」「トイレットペーパー」のようなコードは二度と現れず、「開閉原則」に完全に従っています.springは良いもので、使い方次第です.
何か分からないことがあったら、下にコメントを残して、いいねをクリックしてください.
例えば、メール送信業務の実現は、夢網、玄武、アリ雲などの複数のメールプラットフォーム(メールチャネルと呼ぶ)に接続されるのが一般的です.例えば、アリクラウドメールの管理が厳しく、マーケティング文字付きのメールは送信させない場合、マーケティング類のメールは他のメールルートを使って送信する必要がある.2.あるメールプラットフォームのサービスが一時的に利用できない可能性があり、別のメールルートに切り替える必要がある.3.一部のメールプラットフォームは优遇があって、临时にこのメールのルートに切り替えてメールを送信する必要があります;4.
コード実装
上記のビジネスシーンは簡単に言えば、異なるメールチャネルに対して対応するメールプラットフォームインタフェースを呼び出してメール送信を実現する.メールチャネルは一般的にファイルに配置されたり、データベースに配置されたりする.
コードは次のように実装されます(次のすべてのコードは、キーロジック部分のサンプルコードにすぎないことに注意してください).
腐ったコードの例
メール送信クラスがあります.SmsSendServicesです.sendメソッドでメールを送信します.SmsSendServices.javaです.
public class SmsSendService{
/**
* @Param phoneNo
* @Param content
*/
public void send(String phoneNo,String content){
//
String channelType=config.getChannelType();
// A, A api
if(Objects.equals(channelType,"CHANNEL_A")){
System.out.println(" A ");
}
// B, B api
else if(Objects.equals(channelType,"CHANNEL_B")){
System.out.println(" B ");
}
}
}
ある日メールチャネルCが追加されたら、「else if...」を追加します.
//... ...
//
String channelType=config.getChannelType();
// A, A api
if(Objects.equals(channelType,"CHANNEL_A")){
System.out.println(" A ");
}
// B, B api
else if(Objects.equals(channelType,"CHANNEL_B")){
System.out.println(" B ");
}
//ADD: C, C api
else if(Objects.equals(channelType,"CHANNEL_C")){
System.out.println(" C ");
}
//... ...
他のメールチャネルを追加したら?また「else if...」?明らかにこのやり方は望ましくないし、SOLID原則の「開閉原則」--拡張に対して開放的で、変更に対して閉鎖的ではない.このように私たちは毎回元のコード(変更に対して閉鎖的ではない)を修正し、「if else」を絶えず追加する必要がある.
次に、コードを最適化します.
最適化コード1
SMSチャネルのインタフェースを定義しますSmsChannelService、すべてのSMSチャネルAPIがインタフェースを実現します.SMSチャネルインタフェースSmsChannelServices.java
public interface SmsChannelService{
//
void send(String phoneNo,String content);
}
SMSチャネルA SmsChannelServiceImplaA.java
public class SmsChannelServiceImplA implements SmsChannelService {
public void send(String phoneNo, String content) {
System.out.println(" A ");
}
}
メールチャネルB SmsChannelServiceImplB.java
public class SmsChannelServiceImplB implements SmsChannelService {
public void send(String phoneNo, String content) {
System.out.println(" B ");
}
}
すべてのメールチャネルを工場クラスで初期化
public class SmsChannelFactory {
private Map serviceMap;
// , Service Map
public SmsChannelFactory(){
// key , value :
serviceMap=new HashMap(2);
serviceMap.put("CHANNEL_A",new SmsChannelServiceImplA());
serviceMap.put("CHANNEL_B",new SmsChannelServiceImplB());
}
// Service
public SmsChannelService buildService(String channelType){
return serviceMap.get(channelType);
}
}
元のSmsSendServiceで異なるメールチャネルのインタフェースを呼び出します.元のSmsSendServiceクラスは以下のように最適化されています.
public class SmsSendService {
private SmsChannelFactory smsChannelFactory;
public SmsSendService(){
smsChannelFactory=new SmsChannelFactory();
}
public void send(String phoneNo,String content){
//
String channelType=config.getChannelType();
//
SmsChannelService channelService=smsChannelFactory.buildService(channelType);
//
channelService.send(phoneNo,content);
}
}
これにより、SmsSendServiceクラスは非常に簡潔になり、「if else」をクリアします.SmsSendServiceクラスを再度変更する必要がなく、メールチャネルCを追加する場合は、SmsSendServiceクラスを変更する必要はありません.SmsChannelServiceImplCを追加してSmsChannelServiceインタフェースを実装し、工場クラスSmsChannelFactoryにSmsChannelServiceImplCを初期化するコードを1行追加するだけです.
SMSチャネルCの実装を増やすSmsChannelServiceImplC.java
public class SmsChannelServiceImplC implements SmsChannelService {
public void send(String phoneNo, String content) {
System.out.println(" C ");
}
}
工場クラスの変更SmsChannelFactory.java
public class SmsChannelFactory {
private Map serviceMap;
// serviceMap , Service Map
public SmsChannelFactory(){
// key , value :
serviceMap=new HashMap(3);
serviceMap.put("CHANNEL_A",new SmsChannelServiceImplA());
serviceMap.put("CHANNEL_B",new SmsChannelServiceImplB());
//ADD SmsChannelServiceImplC
serviceMap.put("CHANNEL_C",new SmsChannelServiceImplC());
}
// Service
public SmsChannelService buildService(String channelType){
return serviceMap.get(channelType);
}
}
「if else」はやってしまったが、元のクラスSmsChannelFactoryを修正しなければならず、「開閉原則」を満たしていない.もっと良い方法はないだろうか.
springの依存注入を用いてコードをさらに最適化した.
最適化コード2
SmsChannelServiceインタフェースはgetChannelType()メソッドを追加することが重要です.
public interface SmsChannelService {
//
void send(String phoneNo,String content);
// : getChannelType() ,
String getChannelType();
}
サブクラスはこのメソッドの実装を追加し、@Service注記を加えてspringコンテナを管理します.SmsChannelServiceImplaA.java
@Service
public class SmsChannelServiceImplA implements SmsChannelService {
public void send(String phoneNo, String content) {
System.out.println(" A ");
}
// : getChannelType()
public String getChannelType() {
return "CHANNEL_A";
}
}
SmsChannelServiceImplB.java
@Service
public class SmsChannelServiceImplB implements SmsChannelService {
public void send(String phoneNo, String content) {
System.out.println(" B ");
}
// : getChannelType()
public String getChannelType() {
return "CHANNEL_B";
}
}
SmsChannelFactoryクラスの変更:このステップも重要です.SmsChannelFactory.java
@Service
public class SmsChannelFactory {
private Map serviceMap;
/* : spring SmsChannelService serviceList */
@Autowired
private List serviceList;
/* @PostConstruct , SmsChannelFactory , serviceMap */
@PostConstruct
private void init(){
if(CollectionUtils.isEmpty(serviceList)){
return ;
}
serviceMap=new HashMap(serviceList.size());
// serviceList serviceMap
for (SmsChannelService channelService : serviceList) {
String channelType=channelService.getChannelType();
// , getChannelType() 。
if(serviceMap.get(channelType)!=null){
throw new RuntimeException(" ");
}
/* key , value :
“ 1” “CHANNEL_A"、"CHANNEL_B" ,
, “CHANNEL_C" */
serviceMap.put(channelType,channelService);
}
}
// Service
public SmsChannelService buildService(String channelType){
return serviceMap.get(channelType);
}
}
SmsSendServiceに@Service注記を追加します.SmsChannelFactorySmsSendService.javaに@Autowiredで注入します.
@Service
public class SmsSendService {
@Autowired
private SmsChannelFactory smsChannelFactory;
public void send(String phoneNo,String content){
//
String channelType=config.getChannelType();
//
SmsChannelService channelService=smsChannelFactory.buildService(channelType);
//
channelService.send(phoneNo,content);
}
}
この場合、チャネルCを追加する必要がある場合は、本当にSmsChannelServiceImplCを追加するだけで、既存のコードを変更することなく、「開閉原則」に完全に従うことができます.
SmsChannelServiceImplC.java
@Service
public class SmsChannelServiceImplC implements SmsChannelService {
public void send(String phoneNo, String content) {
System.out.println(" C ");
}
public String getChannelType() {
return "CHANNEL_C";
}
}
まとめは上記の最適化によって「if else」をよく取り除き、「臭い」「長い」「トイレットペーパー」のようなコードは二度と現れず、「開閉原則」に完全に従っています.springは良いもので、使い方次第です.
何か分からないことがあったら、下にコメントを残して、いいねをクリックしてください.