分類
發燒車訊

重學 Java 設計模式:實戰組合模式(營銷差異化人群發券,決策樹引擎搭建場景)

作者:小傅哥
博客:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收穫!

一、前言

小朋友才做選擇題,成年人我都要

頭幾年只要群里一問我該學哪個開發語言,哪個語言最好。群里肯定聊的特別火熱,有人支持PHP、有人喊號Java、也有C++和C#。但這幾年開始好像大家並不會真的刀槍棍棒、斧鉞鈎叉般討論了,大多數時候都是開玩笑的鬧一鬧。於此同時在整體的互聯網開發中很多時候是一些開發語言公用的,共同打造整體的生態圈。而大家選擇的方式也是更偏向於不同領域下選擇適合的架構,而不是一味地追求某個語言。這可以給很多初學編程的新人一些提議,不要刻意的覺得某個語言好,某個語言不好,只是在適合的場景下選擇最需要的。而你要選擇的那個語言可以參考招聘網站的需求量和薪資水平決定。

編程開發不是炫技

總會有人喜歡在整體的項目開發中用上點新特性,把自己新學的知識實踐試試。不能說這樣就是不好,甚至可以說這是一部分很熱愛學習的人,喜歡創新,喜歡實踐。但編程除了用上新特性外,還需要考慮整體的擴展性、可讀性、可維護、易擴展等方面的考慮。就像你家裡雇傭了一夥裝修師傅,有那麼一個小工喜歡炫技搞花活,在家的淋浴下安裝了馬桶。

即使是寫CRUD也應該有設計模式

往往很多大需求都是通過增刪改查堆出來的,今天要一個需求if一下,明天加個內容else擴展一下。日積月累需求也就越來越大,擴展和維護的成本也就越來越高。往往大部分研發是不具備產品思維和整體業務需求導向的,總以為寫好代碼完成功能即可。但這樣的不考慮擴展性的實現,很難讓後續的需求都快速迭代,久而久之就會被陷入惡性循環,每天都有bug要改。

二、開發環境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三個,可以通過關注公眾號bugstack蟲洞棧,回復源碼下載獲取(打開獲取的鏈接,找到序號18)
工程 描述
itstack-demo-design-8-01 使用一坨代碼實現業務需求
itstack-demo-design-8-02 通過設計模式優化改造代碼,產生對比性從而學習

三、組合模式介紹

從上圖可以看到這有點像螺絲和螺母,通過一堆的鏈接組織出一棵結構樹。而這種通過把相似對象(也可以稱作是方法)組合成一組可被調用的結構樹對象的設計思路叫做組合模式。

這種設計方式可以讓你的服務組節點進行自由組合對外提供服務,例如你有三個原子校驗功能(A:身份證B:銀行卡C:手機號)服務並對外提供調用使用。有些調用方需要使用AB組合,有些調用方需要使用到CBA組合,還有一些可能只使用三者中的一個。那麼這個時候你就可以使用組合模式進行構建服務,對於不同類型的調用方配置不同的組織關係樹,而這個樹結構你可以配置到數據庫中也可以不斷的通過圖形界面來控制樹結構。

所以不同的設計模式用在恰當好處的場景可以讓代碼邏輯非常清晰並易於擴展,同時也可以減少團隊新增人員對項目的學習成本。

四、案例場景模擬

以上是一個非常簡化版的營銷規則決策樹,根據性別年齡來發放不同類型的優惠券,來刺激消費起到精準用戶促活的目的。

雖然一部分小夥伴可能並沒有開發過營銷場景,但你可能時時刻刻的被營銷着。比如你去經常瀏覽男性喜歡的机械鍵盤、筆記本電腦、汽車裝飾等等,那麼久給你推薦此類的優惠券刺激你消費。那麼如果你購物不多,或者錢不在自己手裡。那麼你是否打過車,有一段時間經常有小夥伴喊,為什麼同樣的距離他就10元,我就15元呢?其實這些都是被營銷的案例,一般對於不常使用軟件的小夥伴,經常會進行稍微大力度的促活,增加用戶粘性。

那麼在這裏我們就模擬一個類似的決策場景,體現出組合模式在其中起到的重要性。另外,組合模式不只是可以運用於規則決策樹,還可以做服務包裝將不同的接口進行組合配置,對外提供服務能力,減少開發成本。

五、用一坨坨代碼實現

這裏我們舉一個關於ifelse誕生的例子,介紹小姐姐與程序員‍‍之間的故事導致的事故

日期 需求 緊急程度 程序員(話外音)
星期一.早上 猿哥哥,老闆說要搞一下營銷拉拉量,給男生女生髮不同的優惠券,促活消費。 很緊急,下班就要 行吧,也不難,加下判斷就上線
星期二.下午 小哥哥,咱們上線后非常好。要讓咱們按照年輕、中年、成年,不同年齡加下判斷,準確刺激消費。 超緊急,明天就要 也不難,加就加吧
星期三.晚上 喂,小哥哥!睡了嗎!老闆說咱們這次活動很成功,可以不可以在細分下,把單身、結婚、有娃的都加上不同判斷。這樣更能刺激用戶消費。 賊緊急,最快上線。 已經意識到ifelse越來越多了
星期四.凌晨 哇!小哥哥你們太棒了,上的真快。嘻嘻!有個小請求,需要調整下年齡段,因為現在學生處對象的都比較早,有對象的更容易買某某某東西。要改下值!辛苦辛苦! 老闆,在等着呢! 一大片的值要修改,哎!這麼多ifelse
星期五.半夜 歪歪喂!巴巴,壞了,怎麼發的優惠券不對了,有客訴了,很多女生都來投訴。你快看看。老闆,他… (一頭汗),哎,值粘錯位置了! 終究還是一個人扛下了所有

1. 工程結構

itstack-demo-design-8-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── EngineController.java
  • 公司里要都是這樣的程序員絕對省下不少成本,根本不要搭建微服務,一個工程搞定所有業務!
  • 但千萬不要這麼干!酒肉穿腸過,佛祖心中留。世人若學我,如同進魔道。

2. 代碼實現

public class EngineController {

    private Logger logger = LoggerFactory.getLogger(EngineController.class);

    public String process(final String userId, final String userSex, final int userAge) {

        logger.info("ifelse實現方式判斷用戶結果。userId:{} userSex:{} userAge:{}", userId, userSex, userAge);

        if ("man".equals(userSex)) {
            if (userAge < 25) {
                return "果實A";
            }

            if (userAge >= 25) {
                return "果實B";
            }
        }

        if ("woman".equals(userSex)) {
            if (userAge < 25) {
                return "果實C";
            }

            if (userAge >= 25) {
                return "果實D";
            }
        }

        return null;

    }

}
  • 除了我們說的擴展性和每次的維護以外,這樣的代碼實現起來是最快的。而且從樣子來看也很適合新人理解。
  • 但是我勸你別寫,寫這樣代碼不是被扣績效就是被開除。

3. 測試驗證

3.1 編寫測試類

@Test
public void test_EngineController() {
    EngineController engineController = new EngineController();
    String process = engineController.process("Oli09pLkdjh", "man", 29);
    logger.info("測試結果:{}", process);
}
  • 這裏我們模擬了一個用戶ID,並傳輸性別:man、年齡:29,我們的預期結果是:果實B。實際對應業務就是給頭禿的程序員發一張枸杞優惠券

3.2 測試結果

22:10:12.891 [main] INFO  o.i.demo.design.EngineController - ifelse實現方式判斷用戶結果。userId:Oli09pLkdjh userSex:man userAge:29
22:10:12.898 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:果實B

Process finished with exit code 0
  • 從測試結果上看我們的程序運行正常並且符合預期,只不過實現上並不是我們推薦的。接下來我們會採用組合模式來優化這部分代碼。

六、組合模式重構代碼

接下來使用組合模式來進行代碼優化,也算是一次很小的重構。

接下來的重構部分代碼改動量相對來說會比較大一些,為了讓我們可以把不同類型的決策節點和最終的果實組裝成一棵可被運行的決策樹,需要做適配設計和工廠方法調用,具體會體現在定義接口以及抽象類和初始化配置決策節點(性別年齡)上。建議這部分代碼多閱讀幾次,最好實踐下。

1. 工程結構

itstack-demo-design-8-02
└── src
    ├── main
    │   └── java
    │      └── org.itstack.demo.design.domain
    │          ├── model
    │          │   ├── aggregates
    │          │   │   └── TreeRich.java
    │          │   └── vo
    │          │       ├── EngineResult.java
    │          │       ├── TreeNode.java
    │          │       ├── TreeNodeLink.java    
    │          │       └── TreeRoot.java	
    │          └── service
    │              ├── engine
    │              │   ├── impl	
    │              │   │   └── TreeEngineHandle.java	   
    │              │   ├── EngineBase.java 
    │              │   ├── EngineConfig.java       
    │              │   └── IEngine.java	
    │              └── logic
    │                  ├── impl	
    │                  │   ├── LogicFilter.java	 
    │                  │   └── LogicFilter.java	    
    │                  └── LogicFilter.java	
    └── test
         └── java
             └── org.itstack.demo.design.test
                 └── ApiTest.java

組合模式模型結構

  • 首先可以看下黑色框框的模擬指導樹結構;11112111112121122,這是一組樹結構的ID,並由節點串聯組合出一棵關係樹樹。

  • 接下來是類圖部分,左側是從LogicFilter開始定義適配的決策過濾器,BaseLogic是對接口的實現,提供最基本的通用方法。UserAgeFilterUserGenerFilter,是兩個具體的實現類用於判斷年齡性別

  • 最後則是對這顆可以被組織出來的決策樹,進行執行的引擎。同樣定義了引擎接口和基礎的配置,在配置裏面設定了需要的模式決策節點。

    • static {
           logicFilterMap = new ConcurrentHashMap<>();
           logicFilterMap.put("userAge", new UserAgeFilter());
           logicFilterMap.put("userGender", new UserGenderFilter());
      }
      
  • 接下來會對每一個類進行細緻的講解,如果感覺沒有讀懂一定是我作者的表述不夠清晰,可以添加我的微信(fustack)與我交流。

2. 代碼實現

2.1 基礎對象

包路徑 介紹
model.aggregates TreeRich 聚合對象,包含組織樹信息
model.vo EngineResult 決策返回對象信息
model.vo TreeNode 樹節點;子恭弘=叶 恭弘節點、果實節點
model.vo TreeNodeLink 樹節點鏈接鏈路
model.vo TreeRoot 樹根信息
  • 以上這部分簡單介紹,不包含邏輯只是各項必要屬性的get/set,整個源代碼可以通過關注微信公眾號:bugstack蟲洞棧,回復源碼下載打開鏈接獲取。

2.2 樹節點邏輯過濾器接口

public interface LogicFilter {

    /**
     * 邏輯決策器
     *
     * @param matterValue          決策值
     * @param treeNodeLineInfoList 決策節點
     * @return 下一個節點Id
     */
    Long filter(String matterValue, List<TreeNodeLink> treeNodeLineInfoList);

    /**
     * 獲取決策值
     *
     * @param decisionMatter 決策物料
     * @return 決策值
     */
    String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);

}
  • 這一部分定義了適配的通用接口,邏輯決策器、獲取決策值,讓每一個提供決策能力的節點都必須實現此接口,保證統一性。

2.3 決策抽象類提供基礎服務

public abstract class BaseLogic implements LogicFilter {

    @Override
    public Long filter(String matterValue, List<TreeNodeLink> treeNodeLinkList) {
        for (TreeNodeLink nodeLine : treeNodeLinkList) {
            if (decisionLogic(matterValue, nodeLine)) return nodeLine.getNodeIdTo();
        }
        return 0L;
    }

    @Override
    public abstract String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);

    private boolean decisionLogic(String matterValue, TreeNodeLink nodeLink) {
        switch (nodeLink.getRuleLimitType()) {
            case 1:
                return matterValue.equals(nodeLink.getRuleLimitValue());
            case 2:
                return Double.parseDouble(matterValue) > Double.parseDouble(nodeLink.getRuleLimitValue());
            case 3:
                return Double.parseDouble(matterValue) < Double.parseDouble(nodeLink.getRuleLimitValue());
            case 4:
                return Double.parseDouble(matterValue) <= Double.parseDouble(nodeLink.getRuleLimitValue());
            case 5:
                return Double.parseDouble(matterValue) >= Double.parseDouble(nodeLink.getRuleLimitValue());
            default:
                return false;
        }
    }

}
  • 在抽象方法中實現了接口方法,同時定義了基本的決策方法;1、2、3、4、5等於、小於、大於、小於等於、大於等於的判斷邏輯。
  • 同時定義了抽象方法,讓每一個實現接口的類都必須按照規則提供決策值,這個決策值用於做邏輯比對。

2.4 樹節點邏輯實現類

年齡節點

public class UserAgeFilter extends BaseLogic {

    @Override
    public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
        return decisionMatter.get("age");
    }

}

性別節點

public class UserGenderFilter extends BaseLogic {

    @Override
    public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
        return decisionMatter.get("gender");
    }

}
  • 以上兩個決策邏輯的節點獲取值的方式都非常簡單,只是獲取用戶的入參即可。實際的業務開發可以從數據庫、RPC接口、緩存運算等各種方式獲取。

2.5 決策引擎接口定義

public interface IEngine {

    EngineResult process(final Long treeId, final String userId, TreeRich treeRich, final Map<String, String> decisionMatter);

}
  • 對於使用方來說也同樣需要定義統一的接口操作,這樣的好處非常方便後續拓展出不同類型的決策引擎,也就是可以建造不同的決策工廠。

2.6 決策節點配置

public class EngineConfig {

    static Map<String, LogicFilter> logicFilterMap;

    static {
        logicFilterMap = new ConcurrentHashMap<>();
        logicFilterMap.put("userAge", new UserAgeFilter());
        logicFilterMap.put("userGender", new UserGenderFilter());
    }

    public Map<String, LogicFilter> getLogicFilterMap() {
        return logicFilterMap;
    }

    public void setLogicFilterMap(Map<String, LogicFilter> logicFilterMap) {
        this.logicFilterMap = logicFilterMap;
    }

}
  • 在這裏將可提供服務的決策節點配置到map結構中,對於這樣的map結構可以抽取到數據庫中,那麼就可以非常方便的管理。

2.7 基礎決策引擎功能

public abstract class EngineBase extends EngineConfig implements IEngine {

    private Logger logger = LoggerFactory.getLogger(EngineBase.class);

    @Override
    public abstract EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter);

    protected TreeNode engineDecisionMaker(TreeRich treeRich, Long treeId, String userId, Map<String, String> decisionMatter) {
        TreeRoot treeRoot = treeRich.getTreeRoot();
        Map<Long, TreeNode> treeNodeMap = treeRich.getTreeNodeMap();
        // 規則樹根ID
        Long rootNodeId = treeRoot.getTreeRootNodeId();
        TreeNode treeNodeInfo = treeNodeMap.get(rootNodeId);
        //節點類型[NodeType];1子恭弘=叶 恭弘、2果實
        while (treeNodeInfo.getNodeType().equals(1)) {
            String ruleKey = treeNodeInfo.getRuleKey();
            LogicFilter logicFilter = logicFilterMap.get(ruleKey);
            String matterValue = logicFilter.matterValue(treeId, userId, decisionMatter);
            Long nextNode = logicFilter.filter(matterValue, treeNodeInfo.getTreeNodeLinkList());
            treeNodeInfo = treeNodeMap.get(nextNode);
            logger.info("決策樹引擎=>{} userId:{} treeId:{} treeNode:{} ruleKey:{} matterValue:{}", treeRoot.getTreeName(), userId, treeId, treeNodeInfo.getTreeNodeId(), ruleKey, matterValue);
        }
        return treeNodeInfo;
    }

}
  • 這裏主要提供決策樹流程的處理過程,有點像通過鏈路的關係(性別年齡)在二叉樹中尋找果實節點的過程。
  • 同時提供一個抽象方法,執行決策流程的方法供外部去做具體的實現。

2.8 決策引擎的實現

public class TreeEngineHandle extends EngineBase {

    @Override
    public EngineResult process(Long treeId, String userId, TreeRich treeRich, Map<String, String> decisionMatter) {
        // 決策流程
        TreeNode treeNode = engineDecisionMaker(treeRich, treeId, userId, decisionMatter);
        // 決策結果
        return new EngineResult(userId, treeId, treeNode.getTreeNodeId(), treeNode.getNodeValue());
    }

}
  • 這裏對於決策引擎的實現就非常簡單了,通過傳遞進來的必要信息;決策樹信息、決策物料值,來做具體的樹形結構決策。

3. 測試驗證

3.1 組裝樹關係

@Before
public void init() {
    // 節點:1
    TreeNode treeNode_01 = new TreeNode();
    treeNode_01.setTreeId(10001L);
    treeNode_01.setTreeNodeId(1L);
    treeNode_01.setNodeType(1);
    treeNode_01.setNodeValue(null);
    treeNode_01.setRuleKey("userGender");
    treeNode_01.setRuleDesc("用戶性別[男/女]");
    // 鏈接:1->11
    TreeNodeLink treeNodeLink_11 = new TreeNodeLink();
    treeNodeLink_11.setNodeIdFrom(1L);
    treeNodeLink_11.setNodeIdTo(11L);
    treeNodeLink_11.setRuleLimitType(1);
    treeNodeLink_11.setRuleLimitValue("man");
    // 鏈接:1->12
    TreeNodeLink treeNodeLink_12 = new TreeNodeLink();
    treeNodeLink_12.setNodeIdTo(1L);
    treeNodeLink_12.setNodeIdTo(12L);
    treeNodeLink_12.setRuleLimitType(1);
    treeNodeLink_12.setRuleLimitValue("woman");
    List<TreeNodeLink> treeNodeLinkList_1 = new ArrayList<>();
    treeNodeLinkList_1.add(treeNodeLink_11);
    treeNodeLinkList_1.add(treeNodeLink_12);
    treeNode_01.setTreeNodeLinkList(treeNodeLinkList_1);
    // 節點:11
    TreeNode treeNode_11 = new TreeNode();
    treeNode_11.setTreeId(10001L);
    treeNode_11.setTreeNodeId(11L);
    treeNode_11.setNodeType(1);
    treeNode_11.setNodeValue(null);
    treeNode_11.setRuleKey("userAge");
    treeNode_11.setRuleDesc("用戶年齡");
    // 鏈接:11->111
    TreeNodeLink treeNodeLink_111 = new TreeNodeLink();
    treeNodeLink_111.setNodeIdFrom(11L);
    treeNodeLink_111.setNodeIdTo(111L);
    treeNodeLink_111.setRuleLimitType(3);
    treeNodeLink_111.setRuleLimitValue("25");
    // 鏈接:11->112
    TreeNodeLink treeNodeLink_112 = new TreeNodeLink();
    treeNodeLink_112.setNodeIdFrom(11L);
    treeNodeLink_112.setNodeIdTo(112L);
    treeNodeLink_112.setRuleLimitType(5);
    treeNodeLink_112.setRuleLimitValue("25");
    List<TreeNodeLink> treeNodeLinkList_11 = new ArrayList<>();
    treeNodeLinkList_11.add(treeNodeLink_111);
    treeNodeLinkList_11.add(treeNodeLink_112);
    treeNode_11.setTreeNodeLinkList(treeNodeLinkList_11);
    // 節點:12
    TreeNode treeNode_12 = new TreeNode();
    treeNode_12.setTreeId(10001L);
    treeNode_12.setTreeNodeId(12L);
    treeNode_12.setNodeType(1);
    treeNode_12.setNodeValue(null);
    treeNode_12.setRuleKey("userAge");
    treeNode_12.setRuleDesc("用戶年齡");
    // 鏈接:12->121
    TreeNodeLink treeNodeLink_121 = new TreeNodeLink();
    treeNodeLink_121.setNodeIdFrom(12L);
    treeNodeLink_121.setNodeIdTo(121L);
    treeNodeLink_121.setRuleLimitType(3);
    treeNodeLink_121.setRuleLimitValue("25");
    // 鏈接:12->122
    TreeNodeLink treeNodeLink_122 = new TreeNodeLink();
    treeNodeLink_122.setNodeIdFrom(12L);
    treeNodeLink_122.setNodeIdTo(122L);
    treeNodeLink_122.setRuleLimitType(5);
    treeNodeLink_122.setRuleLimitValue("25");
    List<TreeNodeLink> treeNodeLinkList_12 = new ArrayList<>();
    treeNodeLinkList_12.add(treeNodeLink_121);
    treeNodeLinkList_12.add(treeNodeLink_122);
    treeNode_12.setTreeNodeLinkList(treeNodeLinkList_12);
    // 節點:111
    TreeNode treeNode_111 = new TreeNode();
    treeNode_111.setTreeId(10001L);
    treeNode_111.setTreeNodeId(111L);
    treeNode_111.setNodeType(2);
    treeNode_111.setNodeValue("果實A");
    // 節點:112
    TreeNode treeNode_112 = new TreeNode();
    treeNode_112.setTreeId(10001L);
    treeNode_112.setTreeNodeId(112L);
    treeNode_112.setNodeType(2);
    treeNode_112.setNodeValue("果實B");
    // 節點:121
    TreeNode treeNode_121 = new TreeNode();
    treeNode_121.setTreeId(10001L);
    treeNode_121.setTreeNodeId(121L);
    treeNode_121.setNodeType(2);
    treeNode_121.setNodeValue("果實C");
    // 節點:122
    TreeNode treeNode_122 = new TreeNode();
    treeNode_122.setTreeId(10001L);
    treeNode_122.setTreeNodeId(122L);
    treeNode_122.setNodeType(2);
    treeNode_122.setNodeValue("果實D");
    // 樹根
    TreeRoot treeRoot = new TreeRoot();
    treeRoot.setTreeId(10001L);
    treeRoot.setTreeRootNodeId(1L);
    treeRoot.setTreeName("規則決策樹");
    Map<Long, TreeNode> treeNodeMap = new HashMap<>();
    treeNodeMap.put(1L, treeNode_01);
    treeNodeMap.put(11L, treeNode_11);
    treeNodeMap.put(12L, treeNode_12);
    treeNodeMap.put(111L, treeNode_111);
    treeNodeMap.put(112L, treeNode_112);
    treeNodeMap.put(121L, treeNode_121);
    treeNodeMap.put(122L, treeNode_122);
    treeRich = new TreeRich(treeRoot, treeNodeMap);
}

  • 重要,這一部分是組合模式非常重要的使用,在我們已經建造好的決策樹關係下,可以創建出樹的各個節點,以及對節點間使用鏈路進行串聯。
  • 及時後續你需要做任何業務的擴展都可以在裏面添加相應的節點,並做動態化的配置。
  • 關於這部分手動組合的方式可以提取到數據庫中,那麼也就可以擴展到圖形界面的進行配置操作。

3.2 編寫測試類

@Test
public void test_tree() {
    logger.info("決策樹組合結構信息:\r\n" + JSON.toJSONString(treeRich));
    
    IEngine treeEngineHandle = new TreeEngineHandle();
    Map<String, String> decisionMatter = new HashMap<>();
    decisionMatter.put("gender", "man");
    decisionMatter.put("age", "29");
    
    EngineResult result = treeEngineHandle.process(10001L, "Oli09pLkdjh", treeRich, decisionMatter);
    
    logger.info("測試結果:{}", JSON.toJSONString(result));
}
  • 在這裏提供了調用的通過組織模式創建出來的流程決策樹,調用的時候傳入了決策樹的ID,那麼如果是業務開發中就可以方便的解耦決策樹與業務的綁定關係,按需傳入決策樹ID即可。
  • 此外入參我們還提供了需要處理;(man)、年齡(29歲),的參數信息。

3.3 測試結果

23:35:05.711 [main] INFO  o.i.d.d.d.service.engine.EngineBase - 決策樹引擎=>規則決策樹 userId:Oli09pLkdjh treeId:10001 treeNode:11 ruleKey:userGender matterValue:man
23:35:05.712 [main] INFO  o.i.d.d.d.service.engine.EngineBase - 決策樹引擎=>規則決策樹 userId:Oli09pLkdjh treeId:10001 treeNode:112 ruleKey:userAge matterValue:29
23:35:05.715 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"nodeId":112,"nodeValue":"果實B","success":true,"treeId":10001,"userId":"Oli09pLkdjh"}

Process finished with exit code 0
  • 從測試結果上看這與我們使用ifelse是一樣的,但是目前這與的組合模式設計下,就非常方便後續的拓展和修改。
  • 整體的組織關係框架以及調用決策流程已經搭建完成,如果閱讀到此沒有完全理解,可以下載代碼觀察結構並運行調試。

七、總結

  • 從以上的決策樹場景來看,組合模式的主要解決的是一系列簡單邏輯節點或者擴展的複雜邏輯節點在不同結構的組織下,對於外部的調用是仍然可以非常簡單的。
  • 這部分設計模式保證了開閉原則,無需更改模型結構你就可以提供新的邏輯節點的使用並配合組織出新的關係樹。但如果是一些功能差異化非常大的接口進行包裝就會變得比較困難,但也不是不能很好的處理,只不過需要做一些適配和特定化的開發。
  • 很多時候因為你的極致追求和稍有倔強的工匠精神,即使在面對同樣的業務需求,你能完成出最好的代碼結構和最易於擴展的技術架構。不要被遠不能給你指導提升能力的影響到放棄自己的追求!

八、推薦閱讀

  • 1. 重學 Java 設計模式:實戰工廠方法模式(多種類型商品發獎場景)
  • 2. 重學 Java 設計模式:實戰抽象工廠模式(替換Redis雙集群升級場景)
  • 3. 重學 Java 設計模式:實戰建造者模式(裝修物料組合套餐選配場景)
  • 4. 重學 Java 設計模式:實戰原型模式(多套試每人題目和答案亂序場景)
  • 5. 重學 Java 設計模式:實戰橋接模式(多支付渠道「微信、支付寶」與多支付模式「刷臉、指紋」場景)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※推薦台中搬家公司優質服務,可到府估價

分類
發燒車訊

.NET開發者省份分佈排名

 

什麼叫.NET開發者省份分佈排名呢? 顧名思義,這幾個詞大家都認識,.NET開發者都集中在城市,涵蓋一線城市到五線城市。排名的方法非常簡單粗暴,就是根據本公眾號(dotnet跨平台)的省份訂閱讀者數量排名的微信大數據分析。

本號從2015年初的三位數訂閱到現在五位數的訂閱,目前總數6.2w,增長一直平緩從未有過暴增,這显示了傳播和反饋的自主選擇,目前每天還在增長。同時我注意到一個現象:由於公眾號內容都是.NET Core相關的,對.NET 不感興趣的人,壓根就讀不下去。

從訂閱年齡看,高達99%的人落在18歲到60歲的區間且分佈正態,這正是我國勞動人口的年齡, 25歲以下只有20%,所以訂閱並不是以大學生為主,這也反映了現在高校中.NET 的教學比較少或者還是以.NET Framework的老舊內容;60歲以上極少,而所謂的“大專家”群體落在這個區間。

從地域分佈看,訂閱讀者分佈在300多個地級市,幾乎完整覆蓋全國。我的微信好友還不到5000個,遠遠達不到這個廣度,因此傳播是自發形成的。

排名中也提供了海外訂閱的比例。我們從中可以看到海外華人佔比3.22%,按人口比例還是很突出的,有大量的.NET開發到北美打拚,那邊的.NET環境要比國內好很多

這些數據都是藉助於微信的大數據,其實後台是根據註冊IP判斷地址的,會有少量遷移但不影響結果。這裏的6萬樣本相對於程序員群體來說,聚焦於.NET開發領域這個數據根據統計學原理,差異的顯著性已經足夠,具體我不展開了。

下面我們來看下主要省份排名:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

網頁設計最專業,超強功能平台可客製化

分類
發燒車訊

熬夜之作:一文帶你了解Cat分佈式監控

Cat 是什麼?

CAT(Central Application Tracking)是基於 Java 開發的實時應用監控平台,包括實時應用監控,業務監控。

CAT 作為服務端項目基礎組件,提供了 Java, C/C++, Node.js, Python, Go 等多語言客戶端,已經在美團點評的基礎架構中間件框架(MVC 框架,RPC 框架,數據庫框架,緩存框架等,消息隊列,配置系統等)深度集成,為美團點評各業務線提供系統豐富的性能指標、健康狀況、實時告警等。

CAT 很大的優勢是它是一個實時系統,CAT 大部分系統是分鐘級統計,但是從數據生成到服務端處理結束是秒級別,秒級定義是 48 分鐘 40 秒,基本上看到 48 分鐘 38 秒數據,整體報表的統計粒度是分鐘級;第二個優勢,監控數據是全量統計,客戶端預計算;鏈路數據是採樣計算。

Github: https://github.com/dianping/cat

Cat 功能亮點

  • 實時處理:信息的價值會隨時間銳減,尤其是事故處理過程中
  • 全量數據:全量採集指標數據,便於深度分析故障案例
  • 高可用:故障的還原與問題定位,需要高可用監控來支撐
  • 故障容忍:故障不影響業務正常運轉、對業務透明
  • 高吞吐:海量監控數據的收集,需要高吞吐能力做保證
  • 可擴展:支持分佈式、跨 IDC 部署,橫向擴展的監控系統

為什麼要用 Cat?

場景一:用戶反饋 App 無法下單,用戶反饋無法支付,用戶反饋商品無法搜索等問題

場景一的問題在於當系統出現問題后,第一反饋的總是用戶。我們需要做的是什麼,是在出問題后研發第一時間知曉,而不是讓用戶來告訴我們出問題了。

Cat 可以出故障后提供秒級別的異常告警機制,不用再等用戶來反饋問題了。

場景二:出故障后如何快速定位問題

一般傳統的方式當出現問題后,我們就會去服務器上看看服務是否還存活。如果存活就會看看日誌是否有異常信息。

在 Cat 後台的首頁,會展示各個系統的運行情況,如果有異常,則會大片飄紅,非常明顯。最直接的方式還是直接查看 Problem 報表,這裡會為我們展示直接的異常信息,快速定位問題。

場景三:用戶反饋訂單列表要 10 幾秒才展示,用戶反饋下單一直在轉圈圈

場景三屬於優化相關,對於研發來說,優化是一個長期的過程,沒有最好只有更好。優化除了需要有對應的方案,最重要的是要對症下藥。

所謂的對症下藥也就是在優化之前,你得先知道哪裡比較慢。RPC 調用慢?數據庫查詢慢?緩存更新慢?

Cat 可以提供詳細的性能數據,95 線,99 線等。更細粒度的就是可以看到某個請求或者某個業務方法的所有耗時邏輯,前提是你做了埋點操作。

Cat 報表

Cat 目前有五種報表,每種都有特定的應用場景,下面我們來具體聊聊這些報表的作用。

Transaction 報表

適用於監控一段代碼運行情況,比如:運行次數、QPS、錯誤次數、失敗率、響應時間統計(平均影響時間、Tp 分位值)等等場景。

埋點方式:

public void shopService() {
    Transaction transaction = Cat.newTransaction("ShopService", "Service");
    try {
        service();
        transaction.setStatus(Transaction.SUCCESS);
    } catch (Exception e) {
        transaction.setStatus(e); // catch 到異常,設置狀態,代表此請求失敗
        Cat.logError(e); // 將異常上報到cat上
        // 也可以選擇向上拋出: throw e;
    } finally {
        transaction.complete();
    }
}

可以在基礎框架中對 Rpc, 數據庫等框架進行埋點,這樣就可以通過 Cat 來監控這些組件了。

業務中需要埋點也可以使用 Cat 的 Transaction,比如下單,支付等核心功能,通常我們對 URL 進行埋點就可以了,也就包含了具體的業務流程。

Event 報表

適用於監控一段代碼運行次數,比如記錄程序中一個事件記錄了多少次,錯誤了多少次。Event 報表的整體結構與 Transaction 報表幾乎一樣,只缺少響應時間的統計。

埋點方式:

 Cat.logEvent("Func", "Func1");

Problem 報表

Problem 記錄整個項目在運行過程中出現的問題,包括一些異常、錯誤、訪問較長的行為。

如果有人反饋你的接口報 500 錯誤了,你進 Cat 后就直接可以去 Problem 報表了,錯誤信息就在 Problem 中。

Problem 報表不需要手動埋點,我們只需要在項目中集成日誌的 LogAppender 就可以將所有 error 異常記錄,下面的段落中會講解如何整合 LogAppender。

Heartbeat 報表

Heartbeat 報表是 CAT 客戶端,以一分鐘為周期,定期向服務端彙報當前運行時候的一些狀態。

系統指標有系統的負載信息,內存使用情況,磁盤使用情況等。

JVM 指標有 GC 相關信息,線程相關信息。

Business 報表

Business 報表對應着業務指標,比如訂單指標。與 Transaction、Event、Problem 不同,Business 更偏向於宏觀上的指標,另外三者偏向於微觀代碼的執行情況。

這個報表我也沒怎麼用過,用的多的還是前面幾個。

Cat 在 Kitty Cloud 中的應用

Kitty Cloud 的基礎組件是 Kitty,Kitty 裏面對需要的一些框架都進行了一層包裝,比如擴展,增加 Cat 埋點之類的功能。

Cat 的集成

Kitty 中對 Cat 封裝了一層,在使用的時候直接依賴 kitty-spring-cloud-starter-cat 即可整合 Cat 到項目中。

 <dependency>
       <groupId>com.cxytiandi</groupId>
       <artifactId>kitty-spring-cloud-starter-cat</artifactId>
       <version>Kitty Version</version>
 </dependency>

然後在 application 配置文件中配置 Cat 的服務端地址信息,多個英文逗號分隔:

cat.servers=47.105.66.210

在項目的 resources 目錄下創建 META-INF 目錄,然後在 META-INF 中創建 app.properties 文件配置 app.name。此名稱是在 Cat 後台显示的應用名

app.name=kitty-cloud-comment-provider

最後需要配置一下 Cat 的 LogAppender,這樣應用在記錄 error 級別的日誌時,Cat 可以及時進行異常告警操作。

在 logback.xml 增加下面的配置:

 <appender name="CatAppender" class="com.dianping.cat.logback.CatLogbackAppender"></appender>
 <root level="INFO">
     <appender-ref ref="CatAppender" />
 </root>

更詳細的內容請移步 Cat 的 Github 主頁進行查看。

MVC 框架埋點

基於 Spring Boot 做 Web 應用開發,我們最常用到的一個 Starter 包就是 spring-boot-starter-web。

如果你使用了 Kitty 來構建微服務的框架,那麼就不再需要直接依賴 spring-boot-starter-web。而是需要依賴 Kitty 中的 kitty-spring-cloud-starter-web。

kitty-spring-cloud-starter-web 在 spring-boot-starter-web 的基礎上進行了封裝,會對請求的 Url 進行 Cat 埋點,會對一些通用信息進行接收透傳,會對 RestTemplate 的調用進行 Cat 埋點。

在項目中依賴 kitty-spring-cloud-starter-web:

<dependency>
      <groupId>com.cxytiandi</groupId>
      <artifactId>kitty-spring-cloud-starter-web</artifactId>
      <version>Kitty Version</version>
</dependency>

啟動項目,然後訪問你的 REST API。可以在 Cat 的控制台看到 URL 的監控信息。

點擊 URL 進去可以看到具體的 URL 信息。

再進一步可以看到整個 URL 的信息,比如數據庫的查詢,緩存的操作,Http 的調用等。後端同學在優化性能的時候就直接從 URL 下手可以將整個請求的鏈路耗時的情況都分析清楚。

Mybatis 埋點

Kitty 中 Mybatis 是用的 Mybatis Plus, 主要是對數據庫相關操作的 SQL 進行了 Cat 埋點,可以很方便的查看 SQL 的耗時情況。

依賴 kitty-spring-cloud-starter-mybatis:

 <dependency>
     <groupId>com.cxytiandi</groupId>
     <artifactId>kitty-spring-cloud-starter-mybatis</artifactId>
     <version>Kitty Version</version>
 </dependency>

其他的使用方式還是跟 Mybatis Plus 一樣,具體參考 Mybatis Plus 文檔:https://mp.baomidou.com

只要涉及到數據庫的操作,都會在 Cat 中進行數據的展示。

點擊 SQL 進去還可以看到是哪個 Mapper 的操作。

再進一步就可以看到具體的 SQL 語句和消耗的時間。

有了這些數據,後端研發同學就可以對相關的 SQL 進行優化了。

Redis 埋點

如果需要使用 Spring Data Redis 的話,直接集成 kitty-spring-cloud-starter-redis 就可以,kitty-spring-cloud-starter-redis 中對 Redis 的命令進行了埋點,可以在 Cat 上直觀的查看對應的命令和消耗的時間。

添加對應的 Maven 依賴:

<dependency>
     <groupId>com.cxytiandi</groupId>
     <artifactId>kitty-spring-cloud-starter-redis</artifactId>
     <version>Kitty Version</version>
 </dependency>

直接使用 StringRedisTemplate:

@Autowired
private StringRedisTemplate stringRedisTemplate;
 
stringRedisTemplate.opsForValue().set("name", "yinjihuan");

Cat 中可以看到 Redis 信息。

點擊 Redis 進去可以看到有哪些命令。

再進去可以看到命令的詳細信息,比如操作的 key 和消耗的時間。

MongoDB 埋點

Kitty 中對 Spring Data Mongodb 做了封裝,只對 MongoTemplate 做了埋點。使用時需要依賴 kitty-spring-cloud-starter-mongodb。

<dependency>
    <groupId>com.cxytiandi</groupId>
    <artifactId>kitty-spring-cloud-starter-mongodb</artifactId>
    <version>Kitty Version</version>
</dependency>

在發生 Mongo 的操作后,Cat 上就可以看到相關的數據了。

點進去就可以看到是 MongoTemplate 的哪個方法發生了調用。

再進一步就可以看到具體的 Mongo 參數和消耗的時間。

還有 Dubbo, Feign,Jetcache,ElasticSearch 等框架的埋點就不細講了,感興趣的可以移步 Github 查看代碼。

Cat 使用小技巧

埋點工具類

如果要對業務方法進行監控,我們一般會用 Transaction 功能,將業務邏輯包含在 Transaction 裏面,就能監控這個業務的耗時信息。

埋點的方式也是通過 Cat.newTransaction 來進行,具體可以參考上面 Transaction 介紹時給出的埋點示列。

像這種埋點的方式最好是有一個統一的工具類去做,將埋點的細節封裝起來。

public class CatTransactionManager {
    public static <T> T newTransaction(Supplier<T> function, String type, String name, Map<String, Object> data) {
        Transaction transaction = Cat.newTransaction(type, name);
        if (data != null && !data.isEmpty()) {
            data.forEach(transaction::addData);
        }
        try {
            T result = function.get();
            transaction.setStatus(Message.SUCCESS);
            return result;
        } catch (Exception e) {
            Cat.logError(e);
            if (e.getMessage() != null) {
                Cat.logEvent(type + "_" + name + "_Error", e.getMessage());
            }
            transaction.setStatus(e);
            throw e;
        } finally {
            transaction.complete();
        }
    }
}

工具類使用:

public SearchResponse search(SearchRequest searchRequest, RequestOptions options) {
    Map<String, Object> catData = new HashMap<>(1);
    catData.put(ElasticSearchConstant.SEARCH_REQUEST, searchRequest.toString());
    return CatTransactionManager.newTransaction(() -> {
        try {
            return restHighLevelClient.search(searchRequest, options);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }, ElasticSearchConstant.ES_CAT_TYPE, ElasticSearchConstant.SEARCH, catData);
}

通過使用工具類,不再需要每個監控的地方都是設置 Transaction 是否 complete,是否成功這些信息了。

註解埋點

為了讓 Transaction 使用更方便,我們可以自定義註解來做這個事情。比如需要監控下單,支付等核心業務方法,那麼就可以使用自定義的 Transaction 註解加在方法上,然後通過 AOP 去統一做監控。

定義註解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface CatTransaction {
    /**
     * 類型, 默認為Method
     * @return
     */
    String type() default "";
    /**
     * 名稱, 默認為類名.方法名
     * @return
     */
    String name() default "";
    /**
     * 是否保存參數信息到Cat
     * @return
     */
    boolean isSaveParamToCat() default true;
}

定義切面:

@Aspect
public class CatTransactionAspect {
    @Around("@annotation(catTransaction)")
    public Object aroundAdvice(ProceedingJoinPoint joinpoint, CatTransaction catTransaction) throws Throwable {
        String type = catTransaction.type();
        if (StringUtils.isEmpty(type)){
            type = CatConstantsExt.METHOD;
        }
        String name = catTransaction.name();
        if (StringUtils.isEmpty(name)){
            name = joinpoint.getSignature().getDeclaringType().getSimpleName() + "." + joinpoint.getSignature().getName();
        }
        Map<String, Object> data = new HashMap<>(1);
        if (catTransaction.isSaveParamToCat()) {
            Object[] args = joinpoint.getArgs();
            if (args != null) {
                data.put("params", JsonUtils.toJson(args));
            }
        }
        return CatTransactionManager.newTransaction(() -> {
            try {
                return joinpoint.proceed();
            } catch (Throwable throwable) {
               throw new RuntimeException(throwable);
            }
        }, type, name, data);
    }

}

註解使用:

@CatTransaction
@Override
public Page<ArticleIndexBO> searchArticleIndex(ArticleIndexSearchParam param) {
}

你可能關心的幾個問題

Cat 能做鏈路跟蹤嗎?

Cat 主要是一個實時監控系統,並不是一個標準的全鏈路系統,主要是 Cat 的 logview 在異步線程等等一些場景下,不太合適,Cat 本身模型並不適合這個。Cat 的 Github 上有說明:在美團點評內部,有 mtrace 專門做全鏈路分析。

但是如果在 Mvc,遠程調用等這些框架中做好了數據的無縫傳輸,Cat 也可以充當一個鏈路跟蹤的系統,基本的場景足夠了。

Cat 也可以構建遠程消息樹,可以看到請求經過了哪些服務,每個服務的耗時等信息。只不過服務之間的依賴關係圖在 Cat 中沒有。

下圖請求從網關進行請求轉發到 articles 上面,然後 articles 裏面調用了 users 的接口。

Cat 跟 Skywalking 哪個好用?

Skywalking 也是一款非常優秀的 APM 框架,我還沒用過,不過看過一些文檔,功能點挺全的 ,界面也挺好看。最大的優勢是不用像 Cat 一樣需要埋點,使用字節碼增強的方式來對應用進行監控。

之所以列出這個小標題是因為如果大家還沒有用的話肯定會糾結要選擇落地哪個去做監控。我個人認為這兩個都可以,可以自己先弄個簡單的版本體驗體驗,結合你想要的功能點來評估落地哪個。

用 Cat 的話最好有一套基礎框架,在基礎框架中埋好點,這樣才能在 Cat 中詳細的显示各種信息來幫助我們快速定位問題和優化性能。

感興趣的 Star 下唄:https://github.com/yinjihuan/kitty-cloud

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?

分類
發燒車訊

Cypress系列(14)- 環境變量詳解

如果想從頭學起Cypress,可以看下面的系列文章哦

https://www.cnblogs.com/poloyy/category/1768839.html

 

前言

  • 環境變量,其實就是根據環境的變化,變量會有不同的值
  • 比如最常見的:開發環境、測試環境、生產環境的 URL 肯定不一樣,我們可以根據不同的環境選擇不同的環境變量
  • 這就是為什麼我們要學習環境變量的原因

 

環境變量在以下情況會很有用

  • 不同開發人員,對應的值也可能不同
  • 不同環境下的值是不同的,入:dev、test、prod
  • 某些值會頻繁變化,而且高度動態
  • 環境變量很容易會更改,尤其是在持續集成(CI)中運行時

 

栗子

不要在測試中進行硬編碼(寫死,常量),需要改的時候需要動代碼,比如:

cy.request('https://api.acme.corp') // 這將在其他環境中無法使

 

使用環境變量后

cy.request(Cypress.env('EXTERNAL_API')) // 指向動態環境變量

 

當不同環境運行時,如果需要訪問不同的 URL 我們只需要改環境變量即可了,而不用動到代碼

 

baseUrl

  • 前面我們說到可以通過環境變量設置測試套件訪問的 URL,這是其中一種方式
  • 而 Cypress 早就替我們想好了如何解決這問題,可以通過配置 baseUrl 來取代環境變量的方式
  • 當你配置了 baseUrl ,測試套件中的 cy.visit() 、 cy.request() 都會自動以 baseUrl 的值作為前綴
  • 並且,當你需要訪問某些網址或者發起接口請求時,在代碼中就可以不用再指定請求的 host 或者 url 了

 

如何配置 baseUrl

  • 細心的小夥伴已經知道,前面我講 Cypress 全局配置項的時候已經提到過 baseUrl 了
  • 只需要在 cypress.json 文件進行配置就可以啦,如下

 

代碼中調用

cy.visit("")

// 錯誤寫法  cy.visit()

記住調用 visit 或 request 時,再怎麼樣也要傳個空字符串 “” ,不能啥都不填哦

 

通過環境變量來覆蓋 baseUrl

即使配置了 baseUrl ,我們也可以通過環境變量來覆蓋它

CYPRESS_baseUrl=https://staging.app.com cypress run

 

設置環境變量

一共有五種方式

  1. 在 cypress.json 文件中設置
  2. 創建一個 cypress.env.json 文件
  3. 導出為 CYPRESS_* 
  4. 在 CLI 中傳遞為 –env (命令行運行中添加)
  5. 在插件中設置一個環境變量

 

—————————–>>>>>>>>>>>>>>>>>>> 點擊右側目錄即可跳轉

 

最常見的做法

  • 使用一種策略進行本地開發,但在 CI(持續集成)中運行時使用另一種策略
  • 在測試運行時,可以使用 Cypress.env() 訪問環境變量的值

 

cypress.json 中設置

在 cypress.json 的 env 鍵下設置的任何 key:value 都是環境變量

 

cypress.json 代碼

 

測試文件代碼

// 打印所有環境變量
Cypress.env()

// 打印某個環境變量的值
Cypress.env("foor")

 

測試結果

 

優缺點

優點 缺點
適用於需要源碼託管(git)並在所有計算機保持相同的值 只適用於在所有計算機上應該有相同的值

 

創建 cypress.env.json 文件

該文件的描述

  • 可以創建自己的 cypress.env.json 文件,Cypress 將會自動檢查它
  • 並且裏面的值會覆蓋 cypress.json 中重名的環境變量
  • 它創建在 cypress.json 同級目錄下

 

用這個文件有啥用

如果將cypress.env.json 添加到.gitgnore文件中,那麼文件中的值對於每個開發人員的計算機都是不同的

 

cypress.env.json 文件代碼

 

測試文件代碼

 

測試結果

在 cypress.json 中也有一個 key 的環境變量,所以在 cypress.env.json 的 key 的值覆蓋了它的值

 

優缺點

優點 缺點
專用文件,只存放環境變量 需要單獨多處理一個新的文件
可以從其他構建過程中生成此文件 可能會過度干預 1 或 2 個環境變量
不同計算機的環境變量可能不同  

 

CYPRESS_*

重點!

  • 計算機中任何以 CYPRESS_ 或 cypress_ 開頭的環境變量都會自動被 Cypress 識別出來
  • 會直接覆蓋 cypress.json 和 cypress.env.json 文件中重名的環境變量
  • Cypress在添加環境變量時,會自動去掉 CYPRESS_ 前綴

 

在系統添加環境變量

 

總結

  • 我測試過發現並沒有生效,也不知道為啥,需要後面再研究研究
  • 其實並不推薦這種寫法,當環境變量無效時,再改起來就很麻煩了

 

–env

重點!

  • 可以通過命令行將環境變量作為命令行參數傳進來
  • 它的優先級最高,會覆蓋其他地方設置的重名環境變量 
  • 可以為 cypress open 或 cypress run 添加 –env 參數

 

cmd 命令

在 Cypress 安裝目錄下,cmd敲

yarn cypress:open --env host=poloyy.com,key=命令行參數環境變量

yarn cypress:run --env host=poloyy.com,key=命令行參數環境變量

 

測試文件代碼

 

測試結果

 

優缺點

優點 缺點
不需要對文件或配置項進行任何更改 使用 –env 並不友好
簡單明了的設置環境變量  
優先級最高,覆蓋其他形式設置的環境變量  

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

分類
發燒車訊

澳洲發現無齒恐龍 與迅猛龍是近親

摘錄自2020年5月19日自由時報報導

澳洲發現新種無齒恐龍,跟暴龍、迅猛龍是近親!澳洲古生物學家日前發布了新品種恐龍,此種新恐龍雖然與暴龍和迅猛龍同屬於一個亞目,但卻有著長脖子、沒有牙齒、飲食習性也不相同等特點。學界尚未給予正式名稱,研究人員暫時將其稱作「伊拉夫羅龍(Era the Elaphrosaur)」。

根據《BBC》報導,伊拉夫羅龍在維多利亞州奧特韋角出土,作為新品種的獸腳亞目,這種新恐龍具有許多古代捕食者的熟悉體型,牠採用雙足站立,加上兩隻些微笨拙退化的前爪(小胳膊),甚至可能披覆著一層羽毛,但從鼻子到尾巴的身長只有約2公尺左右。

伊拉夫羅龍與其他獸腳亞目的最大區別是,其脖子較大多數獸腳類要長得多,且似乎只有小時候才有牙齒。隨著年齡的增長,伊拉夫羅龍的牙齒會逐漸退化,並留下角質的喙。該物種可能小時候吃肉、長大後又轉向吃素。不過研究人員暫時還無法確定,因為目前暫缺頭骨部分的化石(只有頸骨部分)。

生活環境
國際新聞
澳洲
恐龍

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

皮實耐用家轎王終於迎來新款 7.99萬起!

新車的動力系統為1。4L 90馬力+5擋手動、1。5L 110馬力+5擋手動/6擋自動、1。4T 131馬力+7擋雙離合。老款的1。6L發動機將被新款的1。5L所取代。1。5L發動機最大馬力和1。6L相同,只是最大扭矩由160牛·米變為150牛·米,在保持動力基本不變的前提下可以降低油耗,這也是消費者喜歡的。

捷達自從2013年上市以來,一直沒有經過什麼大的改變。這一點讓消費者不太滿意。不過這一情況如今得到了改善,目前新捷達已經上市,外觀改變較大,配置提升,1.5L發動機取代1.6L發動機。下面就一起看看改款后的捷達到底性價比如何。

一汽-大眾新捷達在12月7日晚上上市,新車售價為7.99-13.49萬,共推出9個車型供消費者選擇。全新捷達上市,老款捷達處於停產在售狀態,同時1.6L車型被1.5L車型取代。

既然是新款車型,最起碼外觀還是要做出一點改變的吧!要不然就太對不起觀眾了。捷達也不例外,雖然還是遵從着大眾的家族式設計,但是不管是外觀還是內飾,還是有所改變的。

首先是前臉的變化,這也是最明的變化。新車的前進氣格柵採用了網格狀造型,同時增加了鍍鉻元素,前大燈造型和保險杠也有較大的改變,整體來看新車看起來比老款要年輕時尚許多。

前臉看完了來看側面,側面的變化非常小,新車的車窗下方會有鍍鉻裝飾條,只是高配車型的輪轂造型有一些變化。

尾部造型變化也比較大,新款車型的尾部看起來“機靈”了許多,造型也向大哥寶來靠攏。

新款車型的顏色增加了胡桃棕、鈦光灰兩種顏色。同時還有深黑、甜蜜金、反射銀、糖果白、板岩灰五種顏色。新車一共有七種車身配色供消費者選擇。

同時新車的車尺寸為4501*1704*1469mm,軸距為2604mm,老款車型的車身尺寸4487*1706*1470mm,軸距為2603mm。尺寸略有增大。

內飾有所升級,整體質感比老款車型有所提升。整車的配置也有不小的升級,全系車型除了時尚版,都標配了車身穩定系統,要知道,這可是一款捷達,大眾能給它裝上ESp算是有點意外了,老款車型只有頂配才有。同時新車也會有座椅加熱、胎壓監測、真皮坐椅等,有了這些配置,捷達終於有點上檔次了。

新車的動力系統為1.4L 90馬力+5擋手動、1.5L 110馬力+5擋手動/6擋自動、1.4T 131馬力+7擋雙離合。老款的1.6L發動機將被新款的1.5L所取代。1.5L發動機最大馬力和1.6L相同,只是最大扭矩由160牛·米變為150牛·米,在保持動力基本不變的前提下可以降低油耗,這也是消費者喜歡的。

競爭對手:

可以看出新捷達的配置有所提升,外觀也變得更加年輕了,競爭力也會隨之增強。但是捷達所處的這個級別競爭真的是特別激烈。他面臨的競爭對手也不是吃素的,不少車型性價要比捷達高。

吉利汽車-帝豪

帝豪的指導價為6.98-24.98萬,作為最暢銷的自主品牌緊湊型轎車,同價位的帝豪要比捷達的配置高了幾個檔次。不管是做工還是用料都不輸捷達,差的只是“車標”吧!

奇瑞汽車-艾瑞澤5

艾5的指導價為5.89-9.79萬,作為自主品牌性價比最高的車型之一,艾瑞澤5的實力一點也不差,同等配置,選擇艾5,省下的幾萬塊錢可以買多少升汽油了,可以多跑多少公里了…

上汽通用雪佛蘭-科沃茲

科沃茲的指導價為7.99-10.99萬,就算你避過了自主品牌的鋒芒,同級別合資車這一關你也不過好過吧!科沃茲的配置比你好,車身也比你大,外觀也很時尚。

除了這些還有鋒范、標緻301、桑塔納等,所以捷達如果還想維持較高的銷量,就必須和老款一樣,大力降價促銷,畢竟薄利多銷也是一個很好的選擇么!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

保值率有啥用?看了才知道這些十幾萬的國產SUV這麼值錢

無論是從消費者口中還是市場銷量,無一不證明傳祺GS4擁有過人的產品實力。首先是原創度極高的外觀設計,年輕運動的造型風格拉攏了多少追求個性的年輕人,在整個自主SUV市場可是非常搶眼。與此同時,傳祺GS4的空間表現、動力總成,均在同級中有不錯的產品賣點,加上實惠的購車、養車成本。

二手車市場中流傳着這麼一句話:新車落地打八折。不管你是豪車還是家用車,只要是用過一段時間,先在新車的售價基礎上砍掉20%再談。因此,很多精打細算的消費者也會在準備購車前,未雨綢繆,盡量多方打聽某款車型的保值率,以便未來出手時能預測虧損區間。但是,保值率是什麼,你真的了解嗎?



長安CS75

新車價:9.28-15.88萬

保值率:57.70%

長安作為一家自主品牌的老大哥,旗下的CS系列均有口皆碑。其中,最受好評的還是長安CS75,沉穩大氣的外觀形象,雖然看起來中庸點,但卻非常符合國人骨子里對於傳統SUV的想象,寬敞又舒適的大空間也為長安CS75贏得一片掌聲。

除此之外,同級別非常有誠意的配置,成熟可靠的強勁動力,綜合起來真的不比合資SUV差。一句話下來,但凡SUV應該具備的特質,長安CS75都一個不漏地呈現給消費者,這也難怪長安CS75在二手車市場依然那麼搶手,保值率居高不下自然是常態。更何況,近期長安CS75 1.5T車型的上市,無疑進一步降低了購車門檻和成本。



傳祺GS4

新車價:9.98-15.38萬元

保值率:58.90%

上市不足兩年的傳祺GS4,一舉成為當今最受關注度的自主SUV之一。無論是從消費者口中還是市場銷量,無一不證明傳祺GS4擁有過人的產品實力。首先是原創度極高的外觀設計,年輕運動的造型風格拉攏了多少追求個性的年輕人,在整個自主SUV市場可是非常搶眼。

與此同時,傳祺GS4的空間表現、動力總成,均在同級中有不錯的產品賣點,加上實惠的購車、養車成本。這樣既有顏值又有實力,性價比還不低的傳祺GS4,又豈有賣不好的道理。另外,步入今年以來,傳祺GS4的市場表現愈加穩定,月銷兩三萬輛穩居SUV市場亞軍,以上有利因素都造就了傳祺GS4較高的保值率。值得一提的是,隨着傳祺GS4 6AT變速箱車型的上市,相信消費者對於其購買熱情只會有增無減。



哈弗H6

新車價:8.88-16.28萬

保值率:56.40%

提到自主SUV的保值率,是無法如何是避不開神車哈弗H6的。事實上,關於哈弗H6保值率高的原因也不難理解,只要了解它一個月賣出去的天量,再算一算哈弗H6在神州大地的保有量規模,你會發現其它的國產車簡直是渣渣的存在。

當然,哈弗H6的熱銷不是憑空而來,其一是抓對了早期自主SUV市場的空白期,可以算是第一個吃大螃蟹的自主SUV,為哈弗H6今時今日的頭把交椅奠定了良好的群眾基礎。其二是哈弗H6基本上完全針對國人的審美點出發打造,無論外觀還是空間,都讓人非常容易接受。此外,哈弗H6多到數不清的車型(紅藍標,運動升級版…),一定程度上幫助了哈弗H6滿足不同需求的消費者,再加上終端方面的讓利促銷。哈哈哈,如此才會延續哈弗H6越賣越好的節奏,哪怕是在二手車市場也是一車難求。

總結

以上推薦的幾款國產SUV,只是在保值率較高中比較有代表性的幾位。事實上,隨着國產SUV整體品質的提升,以及市場保有量的不斷擴大,許多有顏值有實力的國產SUV也越來越值錢,告別了以往二市車市場無人問津的尷尬。相比之下,國產轎車仍需加把勁啊。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

※回頭車貨運收費標準

分類
發燒車訊

傳奇新生 換裝1.5L發動機的新捷達有什麼變化

電影結束后,一汽-大眾汽車有限公司董事、總經理張丕傑上前發表感言致辭,喚醒“國民情捷”,引發情感共鳴宣布《平凡成就偉大》這部舞台劇正式演出,男主角為了尋找父親以前賣了的老捷達,從而找上尋車的道路。外觀更時尚年輕化外觀方面,我們可以看到像邁騰、朗逸那樣大眾最新的橫貫式前臉設計語言,從正前臉看去,X造型的設計元素使車頭帶來很強的視覺衝擊,彰顯了新捷達的力量和運動感。

問起老一輩人最熟悉的車型,他們肯定會異口同聲的說老三樣:捷達、富康、桑塔納,也許連他們自己都難以想象,捷達已經和國人的緣分已經走過了25個年頭,捷達承載的不只是一代人甚至是兩代人的美好回憶,卓越的品質使其締造了一個又一個傳奇。

12月7日晚,備受關注的新捷達在廣州亞運城綜合體育館正式上市,提供了1.4L、1.5L、1.4T三種動力共9款車型,售價區間在7.99-13.49萬元之間。

從場外觀看,可見捷達的發布會充滿着時尚的元素,藍色主題的燈光,音樂節般的場景布局,是否意味着這次新捷達更時尚年輕化的設計路線呢?

走進發布會的場館內,彷彿走進了一個汽車的博物館,每一輛車子都是時間的見證者,25年來所推出的所有捷達車型湧現眼前。

一部簡述捷達的國民情節大電影拉開了發布會的帷幕,描述捷達在中國汽車市場的25年傳奇經歷,多項技術的創新、時代的發展、團隊的奮鬥都歷歷在目。

電影結束后,一汽-大眾汽車有限公司董事、總經理張丕傑上前發表感言致辭,喚醒“國民情捷”,引發情感共鳴宣布《平凡成就偉大》這部舞台劇正式演出,男主角為了尋找父親以前賣了的老捷達,從而找上尋車的道路。

外觀

更時尚年輕化

外觀方面,我們可以看到像邁騰、朗逸那樣大眾最新的橫貫式前臉設計語言,從正前臉看去,X造型的設計元素使車頭帶來很強的視覺衝擊,彰顯了新捷達的力量和運動感。

側面的簡潔流暢額腰線從前翼子板一直延伸至車尾,使整個車身顯得更加修長,搭配硬朗有力的曲面輪廓,給人很緊緻飽滿的感覺,全新造型的輪轂,更突顯了整車的運動氛圍,尾部的設計頗有種小寶來的味道。

內飾

全面升級

沿用了大眾最新的內飾設計風格,全新設計的組合儀錶盤和平底式方向盤,金屬質感的裝飾板和烤漆面板的合理搭配,營造了很好的質感,更讓車內充滿時尚運動的氣息,配置方面根據車型不同,配備有自動空調、感應雨刷、自動頭燈、座椅加熱、定速巡航、胎壓監測等非常豐富的配置。

動力

全新的動力總成

全新的EA211系列1.5L自然吸氣發動機取代了之前的1.6L發動機,最大功率110馬力,最大扭矩150牛米,擁有雙VVT正時調節系統,持續可調式機油泵等先進技術,降低油耗的同時,排放和噪音也改善了不少,與之匹配的是5擋手動或者6擋自動變速器,而1.4L和1.4T發動機得以保留。

總結:本次上的新捷達外觀上有了較大的變化,更家族化的外觀使大眾的車型更加統一了,更時尚年輕的外觀造型適應的人群也更廣了,內飾的配置、造型、用料都有很大的調節和提升,同時新增的1.5L發動機無論是動力和燃油經濟性都有所提升,可以說捷達的競爭力提升了很多。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

Java編程技術之淺析JVM內存

JVM

JVM->Java Virtual Machine:Java虛擬機,是一種用於計算設備的規範,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。

基本認知:

  • 1.JVM是用於運行Java代碼的假象計算機,主要有一套字節碼指令集,一組寄存器,一個棧,一個垃圾回收,堆 和 一個存儲方法域。
  • 2.JVM運行在操作系統之上,與硬件沒有直接的交互。

Java程序執行過程:

  • 1.編譯->源文件由編譯器編譯成字節碼[ByteCode]

Java 源文件—->編譯器—->字節碼文件

  • 2.運行->字節碼由java虛擬機解釋運行

字節碼文件—->JVM—->機器碼

Java類的加載步驟:

  • 1.加載->主要是完成3個階段的提交:

通過類的全限定名來獲取定義類的二進制字節流
將字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構
在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。

  • 2.驗證->四個階段的檢驗動作:

文件格式驗證
元數據驗證
字節碼驗證
符號引用驗證

  • 3.準備->為類變量(static)分配內存並設置類變量的初始值。
  • 4.解析->將常量池內的符號引用轉為直接的引用
  • 5.初始化->按照static塊和static變量在文件中的出現順序,合併到 ()方法中。實例變量由 ()函數賦值。

JVM線程實體:

JVM線程->程序執行過程中的一個線程實體,JVM 允許一個應用併發執行多個線程。

從此,我們應該意識到,在Java中,當提到線程就應該是指JVM線程和Java線程。其中JVM線程指的是Hotspot JVM 後台運行的系統線程,而且Hotspot JVM 中的 Java 線程與原生操作系統線程有直接的映射關係。

️[注意事項]:
[1].當線程本地存儲、緩衝區分配、同步對象、棧、程序計數器等準備好以後,就會創建一個操作系統原生線程。
[2].Java 線程結束,原生線程隨之被回收。操作系統負責調度所有線程,並把它們分配到任何可用的 CPU 上。
[3].當原生線程初始化完畢,就會調用 Java 線程的 run() 方法。當線程結束時,會釋放原生線程和 Java 線程的所有資源

特別需要知道的是,Hotspot JVM 後台運行的系統線程主要是:

  • 虛擬機線程->VM thread:等待 JVM 到達安全點操作出現。這些操作必須要在獨立的線程里執行,因為當堆修改無法進行時,線程都需要 JVM 位於安全點。這些操作的類型有:stop-theworld垃圾回收、線程棧 dump、線程暫停、線程偏向鎖(biased locking)解除。
  • 周期性任務線程->負責定時器事件(也就是中斷),用來調度周期性操作的執行
  • GC線程->支持 JVM 中不同的垃圾回收活動
  • 編譯器線程->在運行時將字節碼動態編譯成本地平台相關的機器碼
  • 信號分發線程->程接收發送到 JVM 的信號並調用適當的 JVM 方法處理

JVM內存

版權聲明:本文為博主原創文章,遵循相關版權協議,如若轉載或者分享請附上原文出處鏈接和鏈接來源。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

分類
發燒車訊

國產三巨頭15台轎車/SUV點評 總有一台適合你

38萬11月銷量10105輛H8、H9的上市問題產能問題等導致兩個好数字的產品銷量表現不佳,但H7很快就邁進萬輛俱樂部,也許能讓長城在中型SUV身上看到了希望。比起H6高達七萬多輛的銷量,H7的萬台銷量的意義一點也不小。15萬-20萬元這個價位區間一直被合資把持着,被稱為自主品牌難以逾越的天花板,而H7的定價正是位於這個區間,並且取得了初步的成功,這個成功不僅對於長城,甚至對於整個自主品牌都是極大的鼓勵。

為什麼有些車甚至不停加價都供不應求,而有些車一再優惠卻門可羅雀?為什麼同是國產車企,以下這些車型卻月月熱賣?

很多網友在評論中留言:“美編你覺得CS15這車怎樣?”“CS35現在還值得買嗎?”“CS75哪個配置好啊?”“H6那麼多款我應該買哪款?”“吉利博越很好看但我們這邊老是沒車怎麼辦?”

面對這些潮水般的相似問題,美編恨自己不能變成千手觀音回復大夥,於是整理出這篇長長的文章來作為解答。如果這裏剛好解答了你或者你的朋友類似的問題,在評論區留言或者把這篇文章傳遞給他們好不好?

長安CS75

指導價:9.28-15.88萬

11月銷量:21978輛

長安CS75面對榮威RX5以及吉利博越的熱銷,絲毫沒有影響其銷量,11月份銷量更是達到了21978輛,這也可以說明消費群體不同,長安CS75更實用外觀設計也偏沉穩。也不能忽略購置稅減半金融政策,臨近年底,也能刺激消費者購車慾望,而長安CS75也最大功臣莫過於1.5T車型,帶動着長安CS75銷量一路攀升。

長安CX70

指導價:6.89-8.49萬

11月銷量:10125

長安CX70從銷量過萬以來,一直維持在一萬輛/月左右。銷量難突破主因,應該是缺乏自動擋車型。畢竟現在成熟路況駕馭自動擋會更舒適,就算是新手駕馭也不會手忙腳亂。其次還是同尺寸配置更高的自主品牌車型越來越多,產品競爭力也越來越強,CX70真該升級換代了。

長安CS35

指導價:7.89-9.89萬

11月銷量:13902輛

2012年10月底上市至今4年過去了,CS35外觀就沒有改變過,不斷優化的小細節包括內飾的變化,好開的手動車型,易用舒適的駕駛體驗乃至各種實用的配置,讓CS35即使面對後來者哈弗H2、瑞風S3雖然銷量有所下降但依然有着不錯的成績。

長安CS15

指導價:5.79-7.79萬

11月銷量:10430輛

看完CS15你會明白,為什麼CS35沒有進行外觀改款了—兩款車車身尺寸相當,CS15價格還便宜些,雖然從型號來說跟CS35相差了20代(CS15→ CS35,開玩笑的)但其實對於真正買車的用戶來說他們差別真的是價格和配置了。CS15還有望搭載1.0T發動機,配合DCT雙離合變速箱,能爆發出最多功率82千瓦和170牛·米的最大扭矩,相比2016年頭髮布車型上那套1.5L發動機的78.5最大功率和145牛·米最大扭矩,動力強勁不少。11月份CS15銷量也突破了1萬輛。自動擋車型的上市也直接提高長安CS15銷量,畢竟擁堵的城市路況駕駛自動擋更加舒適,年輕消費群體也更愛自動擋車型。

悅翔(系列)

指導價:4.69(V3)-8.69萬(V7)

11月銷量:10263輛

長安悅翔系列可謂是國產轎車中不錯的車型,美編身邊也有幾個朋友在用,相比艾瑞澤5和新遠景來說,這個車系特點是保留了長安一貫以來皮實耐用的特點,手動擋車型即使是女司機也能很好駕馭。

逸動

指導價:8.09-24.99萬

11月銷量:10571

相比皮實耐用的悅翔來說,逸動更經典老道的外形,以及勉強夠用的配置,很大程度上限制了銷量增長,所以本月逸動銷量同比下跌了33%。或許長安應該向最近頻繁曝光的吉利學習,提高更新換代的速度以及新增多些配置,至少能讓人覺得誠意滿滿。但整體來說逸動綜合能力並不差。

11月,長安汽車旗下已經有8款車型銷量過萬,這個成績也相當不錯。希望長安汽車加緊對新車型的開發,趁着市場反應熱烈推出CS95這樣的重量級車型,其次在轎車市場多增加配置乃至像奔奔mini那樣提供越級的換擋撥片,應該可以更好刺激目前成熟的消費市場,畢竟作為老牌車企,相信長安的技術儲備還是很驚人的。

我是一條細長的分割線

難怪長城這麼积極的公布銷量數據,因為H6在11月份的銷量竟然為70292台,七萬多台是什麼概念啊,抵得上一些小廠一年的總銷量了。除了H6之外,H2、H7的銷量也算是爆表了。而且今年長城的累計銷量已經達到了92萬輛,距離2017年還有一個月,長城今年完成百萬銷量的目標應該是板上釘釘的事情。下面我們開始對重點車型點評。

哈弗H6

指導價:8.88-16.28萬

11月銷量70292輛

10月份H6的銷量為56667輛,那時候我和朋友們都在討論,這應該就是最高了,就算以後藉著購置稅結束的大潮,它也不會增長太多了。萬萬沒想到啊,一個月之後就被打臉了,今年1-11月哈弗H6已經累銷售50萬零188輛,同比增長51.26%,這也就意味着,就算12月份H6的銷量為0(開玩笑這當然是不可能的),也不能阻止H6成為中國SUV的銷量冠軍,作為中國乃至世界上銷量最好的SUV,H6的銷量神話將會被人傳頌。

如果把H6比作一個人的話,那麼H6絕對是個“人精”,無論外在內在都在不停變化適應各種人群,眾口難調在H6身上真的是個反面例子,因為到目前為止,算上H6藍標、紅標運動版以及H6 Couple、H6升級版,就是在產在銷的車型,已經達到了63款!加上停售車型,市面上能夠買到的哈弗H6已經有85款,實在誇張!

所以H6銷量有這樣的銷量是完全有基礎的,例如H6 coupe它和H6完全不是同一台車,但是它的銷量也會被統計在H6的銷量範圍之內。據估計H6 coupe月銷量佔H6銷量的20%-30%。

哈弗H7

指導價:14.98-19.38萬

11月銷量10105輛

H8、H9的上市問題產能問題等導致兩個好数字的產品銷量表現不佳,但H7很快就邁進萬輛俱樂部,也許能讓長城在中型SUV身上看到了希望。比起H6高達七萬多輛的銷量,H7的萬台銷量的意義一點也不小。

15萬-20萬元這個價位區間一直被合資把持着,被稱為自主品牌難以逾越的天花板,而H7的定價正是位於這個區間,並且取得了初步的成功,這個成功不僅對於長城,甚至對於整個自主品牌都是極大的鼓勵。

H7+H7L,7座+5座,同時還有豐富的配置,超高的顏值,精緻的內飾,實惠的價格,所以H7也取得了很好的銷量,H7L的車身尺寸4900*1925*1785mm,軸距為2850mm,這樣H7L看起來更加大氣,當GS8宣稱要做同級別標杆的時候,H7的銷量已經上萬了。所以就現在而言,H7算是國內首款比較成功的中型SUV。

哈弗H2

指導價:8.68-12.88萬

11月銷量26039輛

H2目前也是哈弗最成功的車型之一,雖然兩萬多的銷量沒有辦法和自己大哥H6的銷量比,但是和“外人”比起來,26039輛銷量也是非常成功的。H2是美編比較喜歡的一款車型,雖然H6看着感覺更實惠,但是比起H6,我更願意選擇H2。同時伴隨着H2s的上市,H2的銷量還會走高。

哈弗H5

指導價:9.48-16.38萬

11月銷量2082輛

H5的銷量比較穩定,這車其實說配置也勉強夠用,幾乎萬年不變的外觀真的沒什麼特別。這樣的銷量一是和它越野的定位有關係,另外就是H5全系是2.0L和2.0T發動機,不會受到購置稅優惠的影響。其實H5絕對是哈弗裏面性價比最高的車型之一,非承載式車身+分時四驅,有一定的越野能力,但是價格卻比較實惠。

哈弗H8

指導價:18.88-25.68萬

11月銷量676輛

哈弗H8是一款銷量不高的車型。不過H8雖然銷量不高,畢竟這是哈弗走向高端的第一步,間接的拉升了哈弗的品牌形象,同時為H7的成功積累了不少經驗。

至於長城的轎車,也沒啥好說的,除了能在路上看到幾年前的C30之外,C50幾乎很難在市面上看得到,變成了一台限量版車型。因為長城SUV的重心傾斜,轎車已經被邊緣化了…..

我是一條細長的分割線

和國內“偏科生”哈弗不一樣的是,吉利屬於那種體美勞全面發展、各項素質綜合能力都比較好的學生,吉利在11月的銷量首次突破10萬輛大關,轎車和SUV均衡發展,其中有6款車型月銷量上萬。

吉利11月份的總銷量為102422輛,去年同期為51367,同比增長99.4%,銷量幾乎翻倍。旗下主力車型個個生龍活虎的,銷量增長很快。

帝豪

指導價:6.98-24.98萬

11月份銷量28842輛

帝豪11月銷量28842輛,10月份的銷量25015輛,9月份的銷量20198輛,每月3000-5000輛增幅很嚇人,自主轎車能拿出將近3萬輛的銷量,實在驚天地泣鬼神。我們都知道自主轎車在和合資轎車的對抗中,幾乎全軍覆沒,像朗逸、軒逸、福睿斯、英朗等這些合資轎車,月銷三萬台以上都不是什麼難事。

但是自主轎車月銷量能上萬的車型目前也只有帝豪、逸動、艾瑞澤5、遠景、比亞迪F3等,不過這些車子的月銷量僅僅在在萬台以上,要知道三五十萬的奧迪A6L、寶馬5系也能月銷上萬啊!轎車前10的銷量幾乎沒有自主轎車。

因轎車競爭太激烈,合資轎車價格下壓的太狠,幾乎是密不透風的車型定位,自主品牌的轎車生存壓力太大、所以自主品牌在轎車領域的投入也就較少,導致轎車領域自主品牌呼聲越來越低。不過帝豪的熱賣讓自主轎車的旗幟重新立起,在合資車型上不至於全軍覆沒。畢竟有着幾年歷史的帝豪口碑很不錯,所以得到了大家的熱捧。

博越

指導價:9.88-15.78萬

11月份銷量18402輛

博越11月銷量18402輛,10月銷量16779輛,9月銷量14053輛,8月銷量10130輛,可以看出來博越隨着產能的逐步釋放,銷量也在穩步上升。作為現階段自主品牌中綜合實力最強的SUV之一,博越的熱銷是板上釘釘的事情。

不過還是因為吉利產能的限制,導致吉利旗下的博越也是不能按時交車,等車是家常便飯,甚至還出現了變相加價的行為,如果吉利可以儘快把產能問題解決了,那麼博越的銷量還會更高。

遠景SUV

指導價:7.49-10.19萬

11月份銷量10942輛

其實遠景SUV從公布售價的時候就註定了銷量不會太差,因為7.49萬元的起售價。還標配ESp、上坡輔助、陡坡緩降、真皮方向盤、日間行車燈等,同時車內空間足夠大,動力系統也比較可靠,底盤調教也很過關,所以月銷上萬不是夢。

帝豪GL

指導價:7.88-11.38萬

11月份銷量10010輛

作為吉利3.0時代的重要作品之一,帝豪GL不負眾望,這款被大家普遍看好的車型找到了它應該處於的位置,達到了上萬的水準。GL憑藉較大的車身,更高的顏值,更寬裕的乘坐空間,取得了不錯的成績。

另外還有遠景轎車、帝豪GS和博瑞,都有着不錯的表現。而且現在吉利的產能也特別吃緊,訂單甚至都排到了兩三個月開外了,所以只要吉利能把產能問題解決了,銷量還會進一步走高,畢竟國內的汽車市場容量真的是太大了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準