日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 單片機 > 架構(gòu)師社區(qū)
[導(dǎo)讀]最近公司貌似融到資了!開始發(fā)了瘋似的找渠道推廣,現(xiàn)在終于明白為啥前一段大肆的招人了,原來是在下一盤大棋,對員工總的來看是個好事,或許是時候該跟boss提一提漲工資的話題了。 不過,漲工資還沒下文,隨之而來的卻是一車一車的需求,每天都有新渠道接入

<section data-website="https://www.mdnice.com" data-mpa-powered-by="yiban.io">

被迫重構(gòu)代碼,這次我干掉了 if-else

最近公司貌似融到資了!開始發(fā)了瘋似的找渠道推廣,現(xiàn)在終于明白為啥前一段大肆的招人了,原來是在下一盤大棋,對員工總的來看是個好事,或許是時候該跟boss提一提漲工資的話題了。

不過,漲工資還沒下文,隨之而來的卻是一車一車的需求,每天都有新渠道接入,而且每個渠道都要提供個性化支持,開發(fā)量陡增。最近都沒什么時間更文,準點下班都成奢望了!

被迫重構(gòu)代碼,這次我干掉了 if-else

由于推廣渠道的激增,而每一個下單來源在下單時都做特殊的邏輯處理,可能每兩天就會加一個來源,已經(jīng)把之前的下單邏輯改的面目全。出于長遠的考慮,我決定對現(xiàn)有的邏輯進行重構(gòu),畢竟長痛不如短痛。

傳統(tǒng)的實現(xiàn)方式

我們看下邊的偽代碼,大致就是重構(gòu)前下單邏輯的代碼,由于來源比較少,簡單的做if-else邏輯判斷足以滿足需求。

現(xiàn)在每種訂單來源的處理邏輯都有幾百行代碼,看著已經(jīng)比較臃腫,可我愣是遲遲沒動手重構(gòu),一方面業(yè)務(wù)方總像催命鬼一樣的讓你趕工期,想快速實現(xiàn)需求,這樣寫是最快;另一方面是不敢動,面對古董級代碼,還是想求個安穩(wěn)。

但這次來源一下子增加幾十個,再用這種方式做已經(jīng)無法維護了,想象一下那種臃腫的if-else代碼,別說開發(fā)想想都頭大!

public class OrderServiceImpl implements IOrderService {
    @Override
    public String handle(OrderDTO dto) {
        String type = dto.getType();
        if ("1".equals(type)) {
            return "處理普通訂單";
        } else if ("2".equals(type)) {
            return "處理團購訂單";
        } else if ("3".equals(type)) {
            return "處理促銷訂單";
        }
        return null;
    }
}

策略模式的實現(xiàn)方式

思來想去基于當前業(yè)務(wù)場景重構(gòu),還是用策略模式比較合適,它是oop中比較著名的設(shè)計模式之一,對方法行為的抽象。

策略模式定義了一個擁有共同行為的算法族,每個算法都被封裝起來,可以互相替換,獨立于客戶端而變化。

一、策略模式的使用場景:

  • 針對同一問題的多種處理方式,僅僅是具體行為有差別時;
  • 需要安全地封裝多種同一類型的操作時;
  • 同一抽象類有多個子類,而客戶端需要使用 if-else 或者 switch-case 來選擇具體子類時。

這個是用策略模式修改后代碼:

@Component
@OrderHandlerType(16)
public class DispatchModeProcessor extends AbstractHandler{

 @Autowired
 private OrderStencilledService orderStencilledService;
 
 @Override
 public void handle(OrderBO orderBO) {
  
  /**
      * 訂單完結(jié)廣播通知(1 - 支付完成)
      */

     orderStencilledService.dispatchModeFanout(orderBO);
  
     /**
      *  SCMS 出庫單
      */

     orderStencilledService.createScmsDeliveryOrder(orderBO.getPayOrderInfoBO().getLocalOrderNo());
 }
}

每個訂單來源都有自己單獨的邏輯實現(xiàn)類,而每次需要添加訂單來源,直接新建實現(xiàn)類,修改@OrderHandlerType(16)的數(shù)值即可,再也不用去翻又臭又長的if-lese

不僅如此在分配任務(wù)時,每個人負責開發(fā)幾種訂單來源邏輯,都可以做到互不干擾,而且很大程度上減少了合并代碼的沖突。

二、具體的實現(xiàn)過程:

1、定義注解

定義一個標識訂單來源的注解@OrderHandlerType

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OrderHandlerType {
 int value() default 0;
}

2、抽象業(yè)務(wù)處理器

向上抽象出來一個具體的業(yè)務(wù)處理器

public abstract class AbstractHandler {
 abstract public void handle(OrderBO orderBO);
}

3、項目啟動掃描 handler 入口

@Component
@SuppressWarnings({"unused","rawtypes"})
public class HandlerProcessor implements BeanFactoryPostProcessor {
 
 private String basePackage = "com.ecej.order.pipeline.processor";
 
    public static final Logger log = LoggerFactory.getLogger(HandlerProcessor.class);
 
 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  
  Map<Integer,Class> map = new HashMap<Integer,Class>();
  
  ClassScaner.scan(basePackage, OrderHandlerType.class).forEach(x ->{
   int type = x.getAnnotation(OrderHandlerType.class).value();
   map.put(type,x);
  });
  
  beanFactory.registerSingleton(OrderHandlerType.class.getName(), map);
  
  log.info("處理器初始化{}", JSONObject.toJSONString(beanFactory.getBean(OrderHandlerType.class.getName())));
 }
}

4、掃描需要用到的工具類

public class ClassScaner {
 private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

 private final List<TypeFilter> includeFilters = new ArrayList<TypeFilter>();

 private final List<TypeFilter> excludeFilters = new ArrayList<TypeFilter>();

 private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
 
 /**
  * 添加包含的Fiter
  * @param includeFilter
  */

 public void addIncludeFilter(TypeFilter includeFilter) {
  this.includeFilters.add(includeFilter);
 }

 /**
  * 添加排除的Fiter
  * @param includeFilter
  */

 public void addExcludeFilter(TypeFilter excludeFilter) {
  this.excludeFilters.add(excludeFilter);
 }
 
 /**
  * 掃描指定的包,獲取包下所有的Class
  * @param basePackage 包名
  * @param targetTypes 需要指定的目標類型,可以是pojo,可以是注解
  * @return Set<Class<?>>
  */

 public static Set<Class<?>> scan(String basePackage,
   Class<?>... targetTypes) {
  ClassScaner cs = new ClassScaner();
  for (Class<?> targetType : targetTypes){
   if(TypeUtils.isAssignable(Annotation.class, targetType)){
    cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
   }else{
    cs.addIncludeFilter(new AssignableTypeFilter(targetType));
   }
  }
  return cs.doScan(basePackage);
 }
 
 /**
  * 掃描指定的包,獲取包下所有的Class
  * @param basePackages 包名,多個
  * @param targetTypes 需要指定的目標類型,可以是pojo,可以是注解
  * @return Set<Class<?>>
  */

 public static Set<Class<?>> scan(String[] basePackages,
   Class<?>... targetTypes) {
  ClassScaner cs = new ClassScaner();
  for (Class<?> targetType : targetTypes){
   if(TypeUtils.isAssignable(Annotation.class, targetType)){
    cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
   }else{
    cs.addIncludeFilter(new AssignableTypeFilter(targetType));
   }
  }
  Set<Class<?>> classes = new HashSet<Class<?>>();
  for (String s : basePackages){
   classes.addAll(cs.doScan(s));
  }
  return classes;
 }
 
 /**
  * 掃描指定的包,獲取包下所有的Class
  * @param basePackages 包名
  * @return Set<Class<?>>
  */

 public Set<Class<?>> doScan(String [] basePackages) {
  Set<Class<?>> classes = new HashSet<Class<?>>();
  for (String basePackage :basePackages) {
   classes.addAll(doScan(basePackage));
  }
  return classes;
 }
 
 /**
  * 掃描指定的包,獲取包下所有的Class
  * @param basePackages 包名
  * @return Set<Class<?>>
  */

 public Set<Class<?>> doScan(String basePackage) {
  Set<Class<?>> classes = new HashSet<Class<?>>();
  try {
   String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
     + ClassUtils.convertClassNameToResourcePath(
       SystemPropertyUtils.resolvePlaceholders(basePackage))+"/**/*.class";
   Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
   for (int i = 0; i < resources.length; i++) {
    Resource resource = resources[i];
    if (resource.isReadable()) {
     MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
     if ((includeFilters.size() == 0 && excludeFilters.size() == 0)|| matches(metadataReader)) {
      try {
       classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
      } catch (ClassNotFoundException ignore) {}
     }
    }
   }
  } catch (IOException ex) {
   throw new RuntimeException("I/O failure during classpath scanning", ex);
  }
  return classes;
 }
 
 /**
  * 處理 excludeFilters和includeFilters
  * @param metadataReader
  * @return boolean
  * @throws IOException
  */

 private boolean matches(MetadataReader metadataReader) throws IOException {
  for (TypeFilter tf : this.excludeFilters) {
   if (tf.match(metadataReader, this.metadataReaderFactory)) {
    return false;
   }
  }
  for (TypeFilter tf : this.includeFilters) {
   if (tf.match(metadataReader, this.metadataReaderFactory)) {
    return true;
   }
  }
  return false;
 }
}

5、根據(jù)類型實例化抽象類


@Component
public class HandlerContext {

 @Autowired
 private ApplicationContext beanFactory;

 public  AbstractHandler getInstance(Integer type){
  
  Map<Integer,Class> map = (Map<Integer, Class>) beanFactory.getBean(OrderHandlerType.class.getName());
  
  return (AbstractHandler)beanFactory.getBean(map.get(type));
 }
 
}

6、調(diào)用入口

我這里是在接受到MQ消息時,處理多個訂單來源業(yè)務(wù),不同訂單來源路由到不同的業(yè)務(wù)處理類中。


@Component
@RabbitListener(queues = "OrderPipelineQueue")
public class PipelineSubscribe{
 
 private final Logger LOGGER = LoggerFactory.getLogger(PipelineSubscribe.class);
 
 @Autowired
 private HandlerContext HandlerContext;
 
 @Autowired
 private OrderValidateService orderValidateService;
 
    @RabbitHandler
    public void subscribeMessage(MessageBean bean){
     
     OrderBO orderBO = JSONObject.parseObject(bean.getOrderBO(), OrderBO.class);
     
     if(null != orderBO &&CollectionUtils.isNotEmpty(bean.getType()))
     {
      for(int value:bean.getType())
      {
          AbstractHandler handler = HandlerContext.getInstance(value);
          handler.handle(orderBO);
      }
  }
 }
}

接收實體 MessageBean 類代碼

public class MessageBean implements Serializable {
    private static final long serialVersionUID = 5454831432308782668L;
    private String cachKey;
    private List<Integer> type;
    private String orderBO;

    public MessageBean(List<Integer> type, String orderBO) {
        this.type = type;
        this.orderBO = orderBO;
    }
}

以上設(shè)計模式方式看著略顯復(fù)雜,很些小伙伴提出質(zhì)疑:“你為了個if-else,弄的如此的麻煩,又是自定義注解,又弄這么多類不麻煩嗎?”  還有一些小伙伴糾結(jié)于性能問題,策略模式的性能可能確實不如if-else

但我覺得吧增加一點復(fù)雜度、犧牲一丟丟性能,換代碼的整潔和可維護性還是值得的。不過,一個人一個想法,怎么選還是看具體業(yè)務(wù)場景吧!

被迫重構(gòu)代碼,這次我干掉了 if-else

策略模式的優(yōu)缺點

優(yōu)點

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

缺點

  • 策略模式,當策略算法太多時,會造成很多的策略類
  • 客戶端不知道有哪些策略類,不能決定使用哪個策略類,這點可以通過封裝common公共包解決,也可以考慮使 IOC容器依賴注入的方式來解決。

以下是訂單來源策略類的一部分,不得不說策略類確實比較多。被迫重構(gòu)代碼,這次我干掉了 if-else

總結(jié)

凡事都有他的兩面性,if-else多層嵌套和也都有其各自的優(yōu)缺點:

  • if-else的優(yōu)點就是簡單,想快速迭代功能,邏輯嵌套少且不會持續(xù)增加,if-else更好些,缺點也是顯而易見,代碼臃腫繁瑣不便于維護。

  • 策略模式 將各個場景的邏輯剝離出來維護,同一抽象類有多個子類,需要使用if-else 或者 switch-case 來選擇具體子類時,建議選策略模式,他的缺點就是會產(chǎn)生比較多的策略類文件。

兩種實現(xiàn)方式各有利弊,如何選擇還是要依據(jù)具體業(yè)務(wù)場景,還是那句話設(shè)計模式不是為了用而用,一定要用在最合適的位置。

閑聊

平常和粉絲私下聊天,好多人對于學(xué)設(shè)計模式的感受:設(shè)計模式背了一大堆,可平常開發(fā)還不是成天寫if-else業(yè)務(wù)邏輯,根本就用不到。

學(xué)設(shè)計模式也不是用不到,只是有時候沒有合適它的場景而已,像我們今天說的這種業(yè)務(wù)場景,用設(shè)計模式就可以完美的解決嘛。

學(xué)了N多技術(shù)可工作用不到是一種很常見的事情,一個穩(wěn)定的項目使用一種技術(shù)會有諸多考量的,新技術(shù)會不會提升系統(tǒng)復(fù)雜度?它有哪些性能瓶頸?這些都必須考慮到,畢竟項目穩(wěn)定才是最重要,誰也不敢輕易冒險嘗試。

而我們學(xué)習技術(shù)可不僅為了眼下項目中是否會用到,是要做一個技術(shù)積累,做長遠打算,人往高處走,沒點能力可不行。

特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:

被迫重構(gòu)代碼,這次我干掉了 if-else

長按訂閱更多精彩▼

被迫重構(gòu)代碼,這次我干掉了 if-else

如有收獲,點個在看,誠摯感謝

section>

免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉