豪華新標杆 這台又新又帥的合資SUV讓奔馳寶馬震驚了

看起來漂亮些嗎。我告訴他,是性能。性能是一台豪車最內在的地方,而豪車之間差距最大的則在於動力系統和科技水平,比如說XT5的2。0T發動機動力強大198KW/400牛米,這樣的強大動力推動XT5動力綽綽有餘,而同級別的奧迪Q5僅為169kw,奔馳GLC更是僅有135kw。

目前的中國豪車市場上,奧迪Q5和寶馬X3車型偏老,遲遲未換代的尷尬一直存在,老舊的設計以及技術平台使得它們的性能表現差強人意,而GLC雖然新上市,但是加價過高,性價比比較低。

但是XT5的出現打破了這種局面,XT5一經上市便引爆市場,銷量超過了德系老將寶馬X3,XT5的到來為這個市場吹進了一股新風,而XT5出現之後,其他的產品似乎變得黯淡無光,而在小編看來XT5也確實是這個價位最值得推薦的SUV車型。為什麼呢?

龐大尺寸 有容乃大

外觀設計是豪華SUV的消費者十分注重的一個方面,一個氣派的造型設計很大程度上決定了它的成功與否,而XT5的外觀設計十分有特點,氣派不失時尚。4812*1903*1685也全面碾壓BBA車型。4813mm的長度是什麼概念呢?百萬級SUV奔馳GLE長度也不過是4813mm而已。花一台GLC的錢買一台GLE級別的SUV,你說划不划算呢?

而大尺寸帶來的最直接的好處就是大空間,XT5的空間表現十分驚人。相比之下奧迪Q5/奔馳GLC表現就相形見絀了。得益於2857mm的超長軸距,XT5的內部乘坐空間十分寬敞,每個乘客擁有的都不止是一席之地。

而且XT5的裝載空間也十分巨大,XT5在常規狀態下行李箱容積便已經達到了584升,而放倒後排座椅之後容積更是高達1634升,相當於一部廂式小貨車的空間了,不過還不止如此。XT5有着多種車內空間的組合方式,支持4/6比例放倒的後排座椅可以靈活兼顧坐人和拉貨的需求,空間靈活性做得十分優秀。

反觀對手奧迪Q5和奔馳GLC的裝載空間,奧迪Q5的行李箱容積為540升,軸距也僅為2807mm,而奔馳GLC甚至都沒有公開它的尾箱容積數據。

澎湃動力 智能四驅

有人問我豪車和普通車型差別到底在哪?看起來漂亮些嗎?我告訴他,是性能。

性能是一台豪車最內在的地方,而豪車之間差距最大的則在於動力系統和科技水平,比如說XT5的2.0T發動機動力強大198KW/400牛米,這樣的強大動力推動XT5動力綽綽有餘,而同級別的奧迪Q5僅為169kw,奔馳GLC更是僅有135kw。在動力上的差距就尤其明顯了。

再比如XT5的智能雙離合適時四驅系統使得車輛的動力能夠在前後軸之間實現0~100%的扭矩分配,在運動和經濟以及通過性上取得完美均衡。為什麼前後軸的0~100%扭矩分配這麼重要呢?車輛在不同狀態下前後軸需要的動力是不一樣的,比如在山路就需要把更多動力分配給後輪,提高操控靈活性;在雪地就需要更多的實現前後50:50的動力分配來保證穩定性,而在高速則可以變成前驅車達到節油目的,XT5的四驅系統就能夠實現0~100%的扭矩分配。

而奔馳GLC的4matci四驅系統和奧迪Q5的quattro四驅系統屬於全時四驅,不管在什麼狀態下都是四驅,而我們日常使用實際上是用不到四輪驅動的,而四驅會帶來高油耗,因此奔馳/奧迪的全時四驅車型能夠省油嗎?

如果你說你只看四驅系統實力不在乎油耗,奧迪能打敗XT5嗎?不能!奧迪Q5的quattro四驅系統最多只能將85%的動力傳遞給後輪,前輪最多也只能接受70%的動力,相比XT5來說,弱爆了~

總結:

為什麼XT5的性價比/性能會如此突出呢?眾所周知BBA在中國的價格一直虛高,花了40萬隻能買到30萬的品質,但是許多人對BBA盲目追捧,即使是X3這樣的老產品也能有春天,而這些價格虛高的產品也一定程度上凸顯了XT5的性價比,因而一直保持高裝備水平的XT5的也就更加顯得划算了。

XT5的出現是必然,豪華SUV市場就應該是百花齊放的,而配置高動力強勁內外裝備也足夠奢華的XT5獲得成功是必然,XT5的出現為豪華SUV市場樹立了一個新的標杆,使用最具性價比的價格打造一檯面面俱到的優秀SUV,到目前也只有凱迪拉克XT5做到了。動力性和空間上的優勢是德系豪車無法相比的,想要追上XT5,德系還需要再換一次代才行!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司"嚨底家"!

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

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

※回頭車貨運收費標準

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

你真的了解EF嗎?關於EntityFramework的優化

接上一篇文章。現在寫程序,做項目不是說功能做完就完事了,在平常的開發過程中對於性能的考慮也是極其重要的。

關於ef的那些事,今天就來說說吧。首先必須得知道.net ef在程序中的五種狀態變化過程與原理。

主要來說說查詢部分的性能優化,在所有查詢中,客戶端查詢出來的數據一般來說是不需要進行跟蹤的。也就是說查詢只是給用戶看,不做其他任何操作。對於基於B/S模式的項目網站開發,應該是無狀態的,也就是ef中的遊離態(Unchanged)(個人理解)。如果是C/S可能還需要進行其他連接的狀態。通俗的說,在開發過程不用去檢測某一個屬性或者類是否被修改或者刪除。

例如:

AsNoTracking:它的作用就是在查詢的過程中不被緩存,也就是不被保留,查出來就完事了,這樣的狀態就變成了Detached遊離態

//AsNoTracking:它的性能比ToList快大約4.8倍,不被緩存的數據。
var item=db.Book.AsNoTracking().First(m=>m.Id==1);
Console.WriteLine(db.Entry(item).State); //輸出Detached(遊離態)

去掉AsNoTracking

var item=db.Book.First(m=>m.Id==1);
Console.WriteLine(db.Entry(item).State); //輸出UnChanged(持久態)

也就是加上AsNoTracking做出來的查詢操作,就和數據庫斷絕了關係(個人理解)這樣對性能也是一個極好的優化。

然後來說說在項目中對ef整體框架優化的使用。

C#是一門面向對象的語言無外乎就是封裝、繼承、多態。。。類可以繼承類,同樣接口也可以繼承接口。面向對象的思想就是每張表都應該有一個父類,只寫一遍,其他類繼承就好了不用重複去寫。

建立一個空白的解決方案,添加一個名為BaseEntity的類,你可以把它看做是所有類的基礎類(父類)。

重點來說說這個BaseEntity為什麼要作為基礎類,它的用意何在。

先看看裏面的寫了些什麼吧!

以前用int或者long作為主鍵自增長的數據類型,這樣後期數據非常龐大的時候可能會無法預估難免可能會出現重複的數據,這個時候對於整個系統來說就無法保障了。

Guid給我做出了一個計算,它是32位的数字加字母的組合,而且是特別長的不會重複的自增數,可以這樣去理解。

DateTime就是每條數據的創建時間,IsRemove就是作為數據邏輯刪除的標識,也就是說,每個類(表)都會存在這三個字段屬性。你可以通過DateTime對沒張表進行排序的操作。

using System;
namespace Book.Models
{
    public class BaseEntity
    {
        public Guid Id { get; set; }=Guid.NewGuid(); //計算32位,字母加数字,特別長的,不重複的,自增數
        public DateTime DateTime { get; set; }=DateTime.Now;    //每條數據的創建時間
        public bool IsRemove { get; set; }  //偽刪除的標識
    }
}

創建BookType類

using System.ComponentModel.DataAnnotations;

namespace Book.Models
{
    public class BookType:BaseEntity
    {
        [StringLength(20)]
        public string Name { get; set; }
    }
}

創建Book類

using System.ComponentModel.DataAnnotations;

namespace Book.Models
{
    public class Book:BaseEntity
    {
        [StringLength(50),Required]
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

這兩個類都會繼承BaseEntity

在App.Config裏面寫上你自己的數據庫連接字符串

這樣的目的就是去生成數據庫,只做生成數據庫的操作。這裏只是在Models層寫了一次,在後期加上表示層還有在寫一遍!

接下來就是去通過指令遷移到數據庫,操作有三步:

  1. 啟動遷移:enable-migrations
  2. 添加遷移:add-migration ‘參數’
  3. 更新數據庫:update-database(如果你需要添加或者修改某個字段屬性,只需要進行第二步和第三步的操作即可!)

記住默認項目要選擇你的Models,如果是多個項目(我這裡是只有一個)就必須要把Models設為啟動項目,不然遷移指令可能不會起到作用

這樣就表示你的數據庫遷移的工作已經完成了,你可以在數據庫進行查看

 接下來的工作就是準備分層的工作,什麼是分層?怎麼分?好處是什麼?

我的理解:聽某一個大佬說,代碼是一層一層寫的不是一行一行寫的。個人覺得拿捏了。原諒我沒去百度。。。

一直覺得對於面向對象的思想,一直覺得自己才疏學淺,學的只是皮毛,真是非常的慚愧。

剛好借這個機會來通過分層一起來學習學習。三層,在之前的學習中對於三層也只是找到表示層(UI)、業務邏輯層(BLL)、數據訪問層(DAL)。

個人覺得,這樣確實是解耦了,但是,並沒有將業務邏輯層的“業務邏輯”這一詞的功能發揮到極致,就只是簡單的從UI層get一下BLL層,BLL層簡單的get一下DAL層。。。(個人覺得並沒有解什麼耦。。。來自菜鳥的思考)

那我們可不可以封一個接口層,通過面向對象的繼承來去調整一下呢?

首先得明確接口是可以繼承接口的!

創建一個數據接口層IDAL,結構如下:引用Models層

 為什麼寫這個接口呢?原因很簡單,就是將增刪改查等操作進行一個封裝,並且這不是普通的增刪改查,看代碼:

你可以把它看做是增刪改查的基礎接口,這個接口是一個泛型(比如你的商品表,用戶表等等…),它必須得基礎BaseEntity,並且這個T必須繼承與BaseEntity,也就是必須是BaseEntity的派生類。

關於IQueryable的好處註釋以寫!

這四個方法就是增刪改查,就算你有幾百張表,應該也只要寫一次增刪改查的操作就ok(也是來自菜鳥理解)

using System;
using System.Linq;
using Book.Models;

namespace IDAL
{
    //接口封裝增刪改查的方法它,是個泛型,並且要給一個約束,這個T必須是BaseEntity的派生類也就是子類,別的不行
    public interface IBaseService<T> where T:BaseEntity
    {
        void Add(T t);
        void Edit(T t);
        void Remove(Guid id);
        T GetOne(Guid id); //查詢某一個對象
        //IQueryable得到的是一個集合,它不會立刻給你去生成Sql語句,直到你需要拿到指定的某個結果后,再去給你生成Sql語句,比如根據條件,分頁,排序等操作獲得的集合數據
        IQueryable<T> GetAll();
    }
}

既然有了接口,那麼肯定是要實現這個接口才能調用裏面的增刪改查的四個方法吧!既然如此,那麼就再寫一個DAL類庫,去實現這個接口層。

結構如下:引用EF以及Models和IDAL層,並且寫一個BaseService類去實現IBaseService接口裡面的方法

BaseService代碼如下:

using IDAL;
using System;
using System.Linq;
using Book.Models;

namespace DAL
{
    public class BaseService<T> : IBaseService<T> where T:BaseEntity    //指明這個T是誰,就是繼承BaseEntity的類
    {
        public void Add(T t)
        {
            throw new NotImplementedException();
        }

        public void Edit(T t)
        {
            throw new NotImplementedException();
        }

        public IQueryable<T> GetAll()
        {
            throw new NotImplementedException();
        }

        public T GetOne(Guid id)
        {
            throw new NotImplementedException();
        }

        public void Remove(Guid id)
        {
            throw new NotImplementedException();
        }
    }
}

前面的 IBaseService<T>就是你實現的是哪個接口,後面的Where就是告訴這個接口要實現的T(泛型已經指定了是繼承BaseEntity的派生類)是誰。。。

可能這裏的Where作用還需要去理解一下,個人理解就是約束、條件之類的作用。

怎麼寫方法?代碼如下:

using IDAL;
using System;
using System.Data.Entity;
using System.Linq;
using Book.Models;

namespace DAL
{
    public class BaseService<T> : IBaseService<T> where T:BaseEntity,new()    //指明這個T是誰,就是繼承BaseEntity的類
    {
        private BookContext _db=new BookContext();
        public void Add(T t)
        {
            //db.Books.Add(t);
            _db.Set<T>().Add(t); //Set<>就是返回一個DbSet實例,為什麼是T ,作用就是動態的訪問,無論是Book還是BookTypes,你給我什麼我就用T接就好了
            _db.SaveChanges();
        }

        public void Edit(T t)
        {
            _db.Entry(t).State = EntityState.Modified;   //直接通過主題去作修改
            _db.SaveChanges();
        }

        public IQueryable<T> GetAll()
        {
            return _db.Set<T>().Where(m => !m.IsRemove).AsNoTracking(); //脫離持久態,變為遊離態,並且過濾掉了已被刪除的數據
        }

        public T GetOne(Guid id)
        {
            return GetAll().First(m => m.Id == id); //直接通過GetAll在通過id查到你想要的數據
        }

        public void Remove(Guid id)
        {
            var t=new T()
            {
                Id = id
            };  //這裡是偽刪除,T不能直接new,也是給個約束條件 new()即可,代表它有構造函數
            _db.Entry(t).State = EntityState.Unchanged; //也是根據狀態去刪除 
            t.IsRemove = true;
            _db.SaveChanges();
        }
    }
}

這些增刪改查方法寫完后,就要去繼承了。原理實際上和BaseEntity差不多

既然增刪改查的方法都寫好了,那麼怎麼去才能調用到它呢?這個時候為什麼還需要去寫IBook和IBookType呢?。首先我們在之前是寫了IBaseService這個接口並且寫了對應的增刪改查方法,但是具體的實現是在DAL層所有,這兩個接口只需要繼承對應的IBaseService就好了,具體實現也是在DAL層,至於DAL層怎麼去寫,請看下面的內容。

這個時候泛型 T就其作用了,我們可以去寫對應的接口繼承IBaseService就好了。。

 代碼如下:

IBookService:

namespace IDAL
{
    public interface IBook:IBaseService<Book.Models.Book>
    {
        
    }
}

IBookTypeService:

using Book.Models;

namespace IDAL
{
    public interface IBookTypeService:IBaseService<BookType>
    {
        
    }
}

DAL層結構:

為什麼還要繼承BaseService?首先BaseService具體實現了繼承IBaseService的方法,所以,我們只需要繼承一下就可以調用到BaseService的增刪改查的方法了!

BookService代碼:

using Book.Models;
using IDAL;

namespace DAL
{
    public class BookService:BaseService<Book.Models.Book>, IBookService
    {
        
    }
}

BookTypeService代碼:

using Book.Models;
using IDAL;

namespace DAL
{
    public class BookTypeService:BaseService<BookType>,IBookTypeService
    {
        
    }
}

以上操作完成后,我們只需要實例化DAL層的Service方法就可以調用在UI層增刪改查了。

寫到這裏只是最最基礎的底層,如果還要完善需要進一步的改善改善。。。

如果有錯誤的地方請提出來,本人也是學生,大家都是抱着學習的心態去記錄和鞏固自己的知識點。。。。。

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

※回頭車貨運收費標準

容器技術之Docker私有鏡像倉庫harbor

  前文我們聊到了docker的私有鏡像倉庫docker-distribution的搭建和簡單的使用,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/13058338.html;從前文的搭建和使用過程來看,docker-distribution搭建的倉庫非常簡陋,它甚至連一個用戶認證都沒有,更別提多用戶;今天我們來介紹另外一款docker倉庫工具harbor;harbor這款工具相對docker-distribution來講功能上豐富了許多;它支持多租戶,可擴展的API和web ui ,支持跨多個harbor實例的鏡像複製,支持身份集成和基於角色的訪問控制等等特徵;接下來我們來安裝看看harbor吧;

  首先我們要去官網下載安裝器,目前最新版本是2.0;下載地址https://github.com/goharbor/harbor/releases/tag/v2.0.0;harbor的安裝器有在線和離線兩個版本,在線包通常較小,適用於網絡環境較好地環境中使用,離線包是所有的安裝文件和腳本等等打包在一起的;

  1、上傳已經下載好的安裝器到服務器

  2、解壓安裝器,並進入到解壓后的目錄中

[root@docker_node01 ~]# tar xf harbor-offline-installer-v2.0.0.tgz -C /usr/local/
[root@docker_node01 ~]# ls /usr/local/
bin  etc  games  harbor  include  lib  lib64  libexec  sbin  share  src
[root@docker_node01 ~]# cd /usr/local/harbor/
[root@docker_node01 harbor]# ls
common.sh  harbor.v2.0.0.tar.gz  harbor.yml.tmpl  install.sh  LICENSE  prepare
[root@docker_node01 harbor]# 

  3、編輯harbor.yml.tmpl文件,更改必要的配置

  提示:以上我只修改了hostname的值,後面的我都是用默認值;有關這個配置文件的說明,可參考官方文檔說明去配置;這裏需要注意一點使用https需要自己手動的去申請證書,沒有證書文件harbor是不能夠正常安裝的;

  4、把harbor.yml.tmpl重命名為harbor.yml

[root@docker_node01 harbor]# ls
common.sh  harbor.v2.0.0.tar.gz  harbor.yml.tmpl  install.sh  LICENSE  prepare
[root@docker_node01 harbor]# mv harbor.yml.tmpl harbor.yml
[root@docker_node01 harbor]#

  5、運行install.sh

  提示:如果運行install.sh腳本出現以上錯誤,我們需要先安裝好docker-compose;

  6、安裝docker-compose

[root@docker_node01 harbor]# yum install docker-compose -y
Loaded plugins: fastestmirror
base                                                                                                                                                | 3.6 kB  00:00:00     
docker-ce-stable                                                                                                                                    | 3.5 kB  00:00:00     
epel                                                                                                                                                | 4.7 kB  00:00:00     
extras                                                                                                                                              | 2.9 kB  00:00:00     
updates                                                                                                                                             | 2.9 kB  00:00:00     
(1/3): updates/7/x86_64/primary_db                                                                                                                  | 2.1 MB  00:00:00     
(2/3): epel/x86_64/updateinfo                                                                                                                       | 1.0 MB  00:00:01     
(3/3): epel/x86_64/primary_db                                                                                                                       | 6.8 MB  00:00:03     
Loading mirror speeds from cached hostfile
 * base: mirrors.aliyun.com
 * extras: mirrors.aliyun.com
 * updates: mirror.bit.edu.cn
Resolving Dependencies
--> Running transaction check
---> Package docker-compose.noarch 0:1.18.0-4.el7 will be installed
--> Processing Dependency: python36-cached_property >= 1.2.0 for package: docker-compose-1.18.0-4.el7.noarch
--> Processing Dependency: python36-docker >= 2.6.1 for package: docker-compose-1.18.0-4.el7.noarch
……省略部分內容
Installed:
  docker-compose.noarch 0:1.18.0-4.el7                                                                                                                                     

Dependency Installed:
  python36-PyYAML.x86_64 0:3.13-1.el7                 python36-cached_property.noarch 0:1.5.1-2.el7             python36-chardet.noarch 0:3.0.4-1.el7                      
  python36-docker.noarch 0:2.6.1-3.el7                python36-docker-pycreds.noarch 0:0.2.1-2.el7              python36-dockerpty.noarch 0:0.4.1-18.el7                   
  python36-docopt.noarch 0:0.6.2-8.el7                python36-idna.noarch 0:2.7-2.el7                          python36-jsonschema.noarch 0:2.5.1-4.el7                   
  python36-pysocks.noarch 0:1.6.8-7.el7               python36-requests.noarch 0:2.14.2-2.el7                   python36-six.noarch 0:1.14.0-2.el7                         
  python36-texttable.noarch 0:1.6.2-1.el7             python36-urllib3.noarch 0:1.25.6-1.el7                    python36-websocket-client.noarch 0:0.47.0-2.el7            

Complete!
[root@docker_node01 harbor]# 

  提示:docker-compose是docker容器的單機編排工具;

  7、再運行install.sh腳本

[root@docker_node01 harbor]# ./install.sh 

[Step 0]: checking if docker is installed ...

Note: docker version: 19.03.8

[Step 1]: checking docker-compose is installed ...

Note: docker-compose version: 1.18.0

[Step 2]: loading Harbor images ...
dbaf2c918102: Loading layer [==================================================>]   34.5MB/34.5MB
1f3458bb7308: Loading layer [==================================================>]  8.435MB/8.435MB
74e91bd5ca15: Loading layer [==================================================>]  6.317MB/6.317MB
82da861dccd3: Loading layer [==================================================>]  14.61MB/14.61MB
8d62f2bfdf94: Loading layer [==================================================>]  28.25MB/28.25MB
40510e398799: Loading layer [==================================================>]  22.02kB/22.02kB
6941a908d292: Loading layer [==================================================>]  49.17MB/49.17MB
Loaded image: goharbor/notary-signer-photon:v2.0.0
bd70463b9e5a: Loading layer [==================================================>]  8.441MB/8.441MB
d3927e3c53ea: Loading layer [==================================================>]  3.584kB/3.584kB
a3b2acbb8f7d: Loading layer [==================================================>]  3.072kB/3.072kB
de14f7f144ce: Loading layer [==================================================>]   9.71MB/9.71MB
94c03f31b276: Loading layer [==================================================>]  10.53MB/10.53MB
Loaded image: goharbor/clair-adapter-photon:v2.0.0
935e17d700d1: Loading layer [==================================================>]   8.44MB/8.44MB
eef8d67e9248: Loading layer [==================================================>]   42.3MB/42.3MB
a181769f3c52: Loading layer [==================================================>]  3.072kB/3.072kB
4b801e4d76d7: Loading layer [==================================================>]  3.584kB/3.584kB
7f7c81a33722: Loading layer [==================================================>]  43.12MB/43.12MB
Loaded image: goharbor/chartmuseum-photon:v2.0.0
4076b322e7f5: Loading layer [==================================================>]  49.89MB/49.89MB
da16bbe3a170: Loading layer [==================================================>]  3.584kB/3.584kB
f8967a1d9155: Loading layer [==================================================>]  3.072kB/3.072kB
6b7eaf984fde: Loading layer [==================================================>]   2.56kB/2.56kB
4406aea83cb2: Loading layer [==================================================>]  3.072kB/3.072kB
78566a971bf2: Loading layer [==================================================>]  3.584kB/3.584kB
e4e05e2ffdad: Loading layer [==================================================>]  12.29kB/12.29kB
f3bcf1de026d: Loading layer [==================================================>]  5.632kB/5.632kB
Loaded image: goharbor/harbor-log:v2.0.0
101133a0a2e6: Loading layer [==================================================>]  8.441MB/8.441MB
40eb3ab360dd: Loading layer [==================================================>]  3.584kB/3.584kB
172ace267ace: Loading layer [==================================================>]  20.94MB/20.94MB
cb361129c579: Loading layer [==================================================>]  3.072kB/3.072kB
f0221c34f9dc: Loading layer [==================================================>]  8.721MB/8.721MB
1880cedc9407: Loading layer [==================================================>]  30.48MB/30.48MB
Loaded image: goharbor/harbor-registryctl:v2.0.0
15f399ca8b42: Loading layer [==================================================>]  8.441MB/8.441MB
182251d62618: Loading layer [==================================================>]  3.584kB/3.584kB
c72ce5e8bba9: Loading layer [==================================================>]  3.072kB/3.072kB
6cb620513867: Loading layer [==================================================>]  20.94MB/20.94MB
8f68617c13e6: Loading layer [==================================================>]  21.76MB/21.76MB
Loaded image: goharbor/registry-photon:v2.0.0
464d98f962d2: Loading layer [==================================================>]  115.2MB/115.2MB
6f577ce93b49: Loading layer [==================================================>]  12.15MB/12.15MB
468b747374fb: Loading layer [==================================================>]  3.072kB/3.072kB
c7d4e40274a2: Loading layer [==================================================>]  49.15kB/49.15kB
349c2528bf8f: Loading layer [==================================================>]  3.584kB/3.584kB
50765adb1994: Loading layer [==================================================>]  13.03MB/13.03MB
Loaded image: goharbor/clair-photon:v2.0.0
f3ae9281f64f: Loading layer [==================================================>]  16.04MB/16.04MB
79de921bba64: Loading layer [==================================================>]  28.25MB/28.25MB
a4826ccd0680: Loading layer [==================================================>]  22.02kB/22.02kB
527c0492bb8a: Loading layer [==================================================>]   50.6MB/50.6MB
Loaded image: goharbor/notary-server-photon:v2.0.0
da380ff7675f: Loading layer [==================================================>]  39.44MB/39.44MB
3e72063a3c12: Loading layer [==================================================>]  3.072kB/3.072kB
87063a362784: Loading layer [==================================================>]   59.9kB/59.9kB
12042912d563: Loading layer [==================================================>]  61.95kB/61.95kB
Loaded image: goharbor/redis-photon:v2.0.0
497d39fd8ed4: Loading layer [==================================================>]  10.28MB/10.28MB
Loaded image: goharbor/nginx-photon:v2.0.0
db89bcd4a7aa: Loading layer [==================================================>]  12.22MB/12.22MB
a3c69d8e6487: Loading layer [==================================================>]  3.072kB/3.072kB
22888c961e12: Loading layer [==================================================>]   2.56kB/2.56kB
15c04c0d67b3: Loading layer [==================================================>]   46.5MB/46.5MB
5e59e5738914: Loading layer [==================================================>]  5.632kB/5.632kB
2fb21742e876: Loading layer [==================================================>]   51.2kB/51.2kB
ebe005c22385: Loading layer [==================================================>]  47.32MB/47.32MB
e91a77a1cc5d: Loading layer [==================================================>]   2.56kB/2.56kB
Loaded image: goharbor/harbor-core:v2.0.0
c9ad3414e408: Loading layer [==================================================>]  63.57MB/63.57MB
0aea7ae12d77: Loading layer [==================================================>]  60.58MB/60.58MB
c3be2cda3349: Loading layer [==================================================>]  5.632kB/5.632kB
970c1e4372ae: Loading layer [==================================================>]  2.048kB/2.048kB
51e00ddbcdac: Loading layer [==================================================>]   2.56kB/2.56kB
27d44e884cd0: Loading layer [==================================================>]   2.56kB/2.56kB
3086c2ee0489: Loading layer [==================================================>]   2.56kB/2.56kB
efd18d9ef79c: Loading layer [==================================================>]  10.24kB/10.24kB
Loaded image: goharbor/harbor-db:v2.0.0
ad0a4ed99dd0: Loading layer [==================================================>]  12.22MB/12.22MB
50121125e459: Loading layer [==================================================>]  3.072kB/3.072kB
6d05b39a8c44: Loading layer [==================================================>]   2.56kB/2.56kB
5380ddc5210f: Loading layer [==================================================>]  35.68MB/35.68MB
e8053e60aee7: Loading layer [==================================================>]   36.5MB/36.5MB
Loaded image: goharbor/harbor-jobservice:v2.0.0
9fefe33a31db: Loading layer [==================================================>]  9.741MB/9.741MB
a52a9b417697: Loading layer [==================================================>]  3.584kB/3.584kB
9b6c54642038: Loading layer [==================================================>]  3.072kB/3.072kB
6a32c528face: Loading layer [==================================================>]  20.34MB/20.34MB
526552ecb5a3: Loading layer [==================================================>]  9.317MB/9.317MB
bc3e72205f25: Loading layer [==================================================>]  30.48MB/30.48MB
Loaded image: goharbor/trivy-adapter-photon:v2.0.0
51193d3ba093: Loading layer [==================================================>]  77.29MB/77.29MB
398b7c3413c0: Loading layer [==================================================>]  48.31MB/48.31MB
cb902b44bae6: Loading layer [==================================================>]   2.56kB/2.56kB
11d3bf655c22: Loading layer [==================================================>]  1.536kB/1.536kB
3d373d988076: Loading layer [==================================================>]  18.43kB/18.43kB
755d5115a4fd: Loading layer [==================================================>]  3.751MB/3.751MB
5d456b2e2b47: Loading layer [==================================================>]  249.3kB/249.3kB
Loaded image: goharbor/prepare:v2.0.0
2128feaae029: Loading layer [==================================================>]  10.28MB/10.28MB
c1e2c6faf4a4: Loading layer [==================================================>]  8.487MB/8.487MB
8728e424e45b: Loading layer [==================================================>]  178.7kB/178.7kB
243de4b81324: Loading layer [==================================================>]  157.2kB/157.2kB
1909dd7d54dc: Loading layer [==================================================>]  33.28kB/33.28kB
e91e103cac7d: Loading layer [==================================================>]  17.41kB/17.41kB
ef43ac036ce0: Loading layer [==================================================>]  15.36kB/15.36kB
3205feaa4e7b: Loading layer [==================================================>]  3.584kB/3.584kB
Loaded image: goharbor/harbor-portal:v2.0.0


[Step 3]: preparing environment ...

[Step 4]: preparing harbor configs ...
prepare base dir is set to /usr/local/harbor
WARNING:root:WARNING: HTTP protocol is insecure. Harbor will deprecate http protocol in the future. Please make sure to upgrade to https
Clearing the configuration file: /config/log/logrotate.conf
Clearing the configuration file: /config/log/rsyslog_docker.conf
Clearing the configuration file: /config/nginx/nginx.conf
Clearing the configuration file: /config/core/env
Clearing the configuration file: /config/core/app.conf
Clearing the configuration file: /config/registry/passwd
Clearing the configuration file: /config/registry/config.yml
Clearing the configuration file: /config/registry/root.crt
Clearing the configuration file: /config/registryctl/env
Clearing the configuration file: /config/registryctl/config.yml
Clearing the configuration file: /config/db/env
Clearing the configuration file: /config/jobservice/env
Clearing the configuration file: /config/jobservice/config.yml
Generated configuration file: /config/log/logrotate.conf
Generated configuration file: /config/log/rsyslog_docker.conf
Generated configuration file: /config/nginx/nginx.conf
Generated configuration file: /config/core/env
Generated configuration file: /config/core/app.conf
Generated configuration file: /config/registry/config.yml
Generated configuration file: /config/registryctl/env
Generated configuration file: /config/registryctl/config.yml
Generated configuration file: /config/db/env
Generated configuration file: /config/jobservice/env
Creating harbor-log ... done
loaded secret from file: /data/secret/keys/secretkey
Generated configuration file: /compose_location/docker-compose.yml
Clean up the input dir

Creating harbor-db ... done
Creating harbor-core ... done
[Step 5]: starting Harbor ...
Creating nginx ... done
Creating registry ... 
Creating harbor-db ... 
Creating redis ... 
Creating harbor-portal ... 
Creating registryctl ... 
Creating harbor-core ... 
Creating harbor-jobservice ... 
Creating nginx ... 
 ----Harbor has been installed and started successfully.----
[root@docker_node01 harbor]# 

  提示:從上面的信息可以看到harbor導入了很多鏡像,然後基於各個鏡像間的關係提供配置文件,然後按照一定的依賴關係順序啟動為容器;我們用docker images 可以來看看它導入了那些鏡像

[root@docker_node01 harbor]# docker images
REPOSITORY                      TAG                 IMAGE ID            CREATED             SIZE
goharbor/chartmuseum-photon     v2.0.0              4db8d6aa63e9        3 weeks ago         127MB
goharbor/redis-photon           v2.0.0              c89ea2e53cc0        3 weeks ago         72.2MB
goharbor/trivy-adapter-photon   v2.0.0              6122c52b7e48        3 weeks ago         103MB
goharbor/clair-adapter-photon   v2.0.0              dd2210cb7f53        3 weeks ago         62MB
goharbor/clair-photon           v2.0.0              f7c7fcc52278        3 weeks ago         171MB
goharbor/notary-server-photon   v2.0.0              983ac10ed8be        3 weeks ago         143MB
goharbor/notary-signer-photon   v2.0.0              bee1b6d75e0d        3 weeks ago         140MB
goharbor/harbor-registryctl     v2.0.0              c53c32d58d04        3 weeks ago         102MB
goharbor/registry-photon        v2.0.0              afdc1b7ada36        3 weeks ago         84.5MB
goharbor/nginx-photon           v2.0.0              17892f03e56c        3 weeks ago         43.6MB
goharbor/harbor-log             v2.0.0              5f8ff08e795c        3 weeks ago         82MB
goharbor/harbor-jobservice      v2.0.0              c68a2495bf55        3 weeks ago         116MB
goharbor/harbor-core            v2.0.0              3aa3af64baf8        3 weeks ago         138MB
goharbor/harbor-portal          v2.0.0              e0b1d3c894c4        3 weeks ago         52.4MB
goharbor/harbor-db              v2.0.0              5c76f0296cec        3 weeks ago         154MB
goharbor/prepare                v2.0.0              7266d49995ed        3 weeks ago         158MB
[root@docker_node01 harbor]# docker ps -a
CONTAINER ID        IMAGE                                COMMAND                  CREATED             STATUS                   PORTS                       NAMES
909486114bab        goharbor/nginx-photon:v2.0.0         "nginx -g 'daemon of…"   2 minutes ago       Up 2 minutes (healthy)   0.0.0.0:80->8080/tcp        nginx
201af4781190        goharbor/harbor-jobservice:v2.0.0    "/harbor/entrypoint.…"   2 minutes ago       Up 2 minutes (healthy)                               harbor-jobservice
d926598a1b4b        goharbor/harbor-core:v2.0.0          "/harbor/entrypoint.…"   2 minutes ago       Up 2 minutes (healthy)                               harbor-core
b655e8bb9da3        goharbor/harbor-portal:v2.0.0        "nginx -g 'daemon of…"   2 minutes ago       Up 2 minutes (healthy)   8080/tcp                    harbor-portal
596d050acf8b        goharbor/registry-photon:v2.0.0      "/home/harbor/entryp…"   2 minutes ago       Up 2 minutes (healthy)   5000/tcp                    registry
88a6b3335d25        goharbor/harbor-registryctl:v2.0.0   "/home/harbor/start.…"   2 minutes ago       Up 2 minutes (healthy)                               registryctl
cf8db1840524        goharbor/harbor-db:v2.0.0            "/docker-entrypoint.…"   2 minutes ago       Up 2 minutes (healthy)   5432/tcp                    harbor-db
5d522f8f3c38        goharbor/redis-photon:v2.0.0         "redis-server /etc/r…"   2 minutes ago       Up 2 minutes (healthy)   6379/tcp                    redis
020fbf3571a2        goharbor/harbor-log:v2.0.0           "/bin/sh -c /usr/loc…"   2 minutes ago       Up 2 minutes (healthy)   127.0.0.1:1514->10514/tcp   harbor-log
[root@docker_node01 harbor]# 

  提示:可以看到本地倉庫中多了很多鏡像,同時也啟動了很多容器;其中名為nginx的容器把80端口暴露到數組機上了;到此harbor就安裝好了;接下來我們訪問宿主機的80端口看看是否能夠訪問到harbor

  提示:以上就是harbor的web 頁面,默認用戶名是admin密碼是Harbor12345

  登錄harbor web頁面

  提示:我們就可以基於這個web頁面來做管理了;接下來我們先創建一個用戶和項目,然後在通過docker push上傳鏡像到harbor上

  創建用戶

  提示:填寫好以上信息,點擊確定用戶就創建好了;

  創建項目

   提示:如果創建的項目是私有的,把訪問級別後面的公開對勾取消即可

  從別的docker主機上上傳鏡像到harbor

  提示:使用非https的倉庫必須要在daemon.json文件中配置insecure-registries來聲明不安全的鏡像倉庫地址;

  提示:這裏提示我們未授權;接下來我們去web管理頁面授權qiuhom是test項目的成員;

  提示:現在我們把qiuhom這個用戶設置為test這個項目的管理員,現在我們在以qiuhom的身份推鏡像到test項目中,看看是否能夠成功把進行推送到harbor上?

[root@docker_node02 ~]# docker push node01.docker-registry.io/test/nginx:1.14-alpine
The push refers to repository [node01.docker-registry.io/test/nginx]
076c58d2644f: Pushed 
b2cbae4b8c15: Pushed 
5ac9a5170bf2: Pushed 
a464c54f93a9: Pushed 
1.14-alpine: digest: sha256:a3a0c4126587884f8d3090efca87f5af075d7e7ac8308cffc09a5a082d5f4760 size: 1153
[root@docker_node02 ~]# 

  提示:這次推送鏡像沒有報錯,我們去web頁面中看看鏡像是否推送到test項目中去了?

  驗證:在harborweb界面看看是否有我們推上去的鏡像?

  用其他docker主機下載harbor上的鏡像

  提示:可以看到現在我們搭建的harbor是可以正常下載和上傳鏡像的;管理鏡像我們可以通過web頁面管理即可,我這裏就不去演示了;接下來我們再來說說在命令行用docker-compose啟動harbor和停止harbor吧

  停止harbor

  提示:用docker-compose停止harbor需要先進入到harbor目錄下,然後執行docker-compose stop 這條命令會去尋找docker-compose.yml文件,根據文件中定義的服務來停止容器;這個有點類似docker build命令,找Dockerfile文件,而docker-compose 是找docker-compose.yml;這裏還需要注意一點的是這個文件名必須是docker-compose.yml;

  啟動harbor

  提示:啟動huabor同停止harbor一樣都必須在docker-compose.yml文件所在目錄下執行docker-compose start 或docker-compose up -d ;

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

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

重學 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開發領域這個數據根據統計學原理,差異的顯著性已經足夠,具體我不展開了。

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

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

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

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

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

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

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

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

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

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

【其他文章推薦】

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

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

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

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

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

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

新冠病毒是就業殺手!美太陽能產業五年的就業成長歸零

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

路透報導,新冠病毒的大流行,導致美國太陽能產業五年來的就業成長歸零。

產業領袖周一(18日)表示,在疫情期間,美國砍掉了6萬5000多個太陽能產業工作,這個以遏止氣候變遷為目標領域五年來的就業成長倒轉回原點。美國貿易公會組織「太陽能產業協會」表示,因商店關門和居家避疫令而無法安裝太陽能設施,導致工作急劇流失。

美國整個經濟體4月流失逾2050萬個工作,是自經濟大蕭條以來受薪工作最急劇的下滑。這波裁員後預期人力剩下18萬8000多名員工,類似此產業在2014年的規模,與之前預測的30萬2000名員工相距甚遠。

能源議題
再生能源
能源轉型
國際新聞
美國
武漢肺炎
太陽能
低碳產業
就業
疫情看氣候與能源

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

【其他文章推薦】

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

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

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

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

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

疫情沒遊客使盜獵激增 柬埔寨國鳥也遇難 3隻極危大䴉遭加保扶毒死

環境資訊中心綜合外電;黃鈺婷 翻譯;林大利 審校;稿源:Mongabay

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

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

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

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

皮實耐用家轎王終於迎來新款 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/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

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

購置稅減半優惠政策真的即將結束?結果可能會是這樣

所以,汽車工業協會已經向有關部門提出建議,希望繼續推行1。6L及以下乘用車購置稅優惠政策,而且,對於是否繼續延續這個優惠政策,有關部門正在進行商討。我們從某兩家車企的高層了解到,他們認為接下來小排量汽車的購置稅按7。

馬上到元旦了,近期有購車打算的小夥伴們都是一個比一個忙的,看車、試駕、砍價、簽單一氣呵成,好像現在不買車以後就買不到了一樣。

昨天表哥還跟我打電話說車子已經定好了,就等提車了,我當時就說你這個做事這麼墨跡的人怎麼這麼快就做出決定了。

他回答:“不買不行啊,到了月底購置稅減半政策就結束了,銷售也一個勁的催我,害怕多花幾千塊錢就趕緊訂車了。”

再聯想到11月份汽車銷量又創新高,所以可以看出來,這一政策對打算購車之人的影響還是非常大的。

2015年9月29日,國務院召開會議,為促進小排量汽車發展,決定對購買1.6L及以下排量的乘用車實行車輛購置稅減半的優惠政策,即10%稅率降為按5%稅率徵收。簡單的理解就是相當於在之前的購置稅基礎之上打五折。實行日期為2015年10月1日-2016年12月31日。

眼看着這個五折優惠政策即將到期,所以大家都是想在這個政策結束之前趕緊下單買車。

當時實行這個優惠政策的目的也是為了擴大內需,促進消費。這個政策確實極大的促進了汽車銷量的增長,達到了預期效果,今年前十個月國內汽車銷量同比增長約為13%,過去五年的年均增長率大約為6.4%,增速明顯。

但是這個政策更像是催熟劑,使得需求提前被釋放出來了。如果優惠政策戛然而止,那麼對車市的打擊很可能是十分巨大的。所以,汽車工業協會已經向有關部門提出建議,希望繼續推行1.6L及以下乘用車購置稅優惠政策,而且,對於是否繼續延續這個優惠政策,有關部門正在進行商討。

我們從某兩家車企的高層了解到,他們認為接下來小排量汽車的購置稅按7.5%的稅率徵收的可能性更大。簡單的理解也就是購置稅打7.5折。因為7.5%的稅率在我國早有先例,所以小編認為這個傳言還是比較可信的。

雖然沒有了5折的優惠,但是可以打7.5折還是不錯的麽,畢竟也可以省下一部分錢了。車型的價格越高,省的錢就越多。不過7.5%並不是可靠消息,只是稅率很有可能是7.5%,具體是什麼情況還是要看政策。

所以如果車價較貴,還是建議儘快下手比較好,因為誰也無法準確預測政策將會往哪裡走。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

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

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

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