為取得專利,小米持股 Ninebot 收購電動代步車始祖 Segway

  小米投資的電動代步車廠商 Ninebot 今天宣布與小米、順為資本、紅杉資本聯合出資收購電動代步車始祖 Segway,交易目前已全數完成。身為 Ninebot 的股東,這項收購除了意味著小米持續拓展海外市場外,對小米來說最大的收益,便是可取得 Segway 手中大量的專利。   總部位於美國的 Segway 成立於 2001 年,產品主要為雙輪的電動代步車,而 Ninebot 則是中國電動代步車品牌。Segway 在全球每年約只有 1 萬台左右的銷量,對增加 Ninebot 銷售量並沒有太大的直接幫助,據知情人士透露,先前 Segway 曾與包括法拉利、香奈兒等國際知名品牌合作,Ninebot 買下 Segway 主要是衝著其在海外的知名度,更重要的是可取得 Segway 手中握有的專利。   有趣的是,Segway 創辦人 Dean Kamen 曾在 2014 年控告 Ninebot 及其他中國廠商侵犯智慧財產權,美國國際貿易委員會於 11 月受理調查,而今調查結果尚未出爐,Segway 卻先被 Ninebot 收購,成為旗下全資子公司。   此外,Ninebot 執行長高祿峰同時也對外宣布已完成 8,000 萬美元的 A 輪融資,由小米、紅杉資本、順為資本共同出資,並表示未來將開發應用電動代步車、移動互聯網及人車互動技術的產品。     本文全文授權轉載自《科技新報》─〈〉

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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

推薦一種通過刷leetcode來增強技術功底的方法

背景

如果前人認為這個一種學習提高或者檢驗能力的成功實踐。而自己目前又沒有更好的方法,那就不妨試一試。

而不管作為面試官還是被面試者,編碼題最近越來越流行。而兩種角色都需要思考的問題是希望考察什麼能力,通過什麼題目,需要達到怎樣的程度可以說明面試者具有了這樣的能力。

而要找到上面這些問題的答案,比較好的方式除了看一些理論性文章和接受培訓之外,自己動手刷一刷leetcode切身實踐一下不失為一個不錯的方式。而既然要花精力去做這件事情,那就需要解決一個問題:我從中可以獲得什麼提高。以下是個人的一些經驗和感受。

 

收益

對底層有了更深入的了解和思考

leetcode一些常見題也是平時工作中常用的一些底層數據結構的實現方法。 

先舉個大家使用比較多的算法:LRU(最近最少使用),在Java的實現中實現特別簡單。只是使用了LinkedHashMap這種數據結構。而如果看一些開源包里LRU的實現,發現也是這樣實現的。實際上動手實現一遍,LRU就再也不會忘了。

再舉個數據結構的例子:字典樹又叫前綴樹。它是搜索和推薦的基礎。標準點的定義是:

字典樹又稱單詞查找樹,Tire樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計,排序和保存大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:利用字符串的公共前綴來減少查詢時間,最大限度地減少無謂的字符串比較,查詢效率比哈希樹高。

因為之前做過搜索引擎,一直也對這塊很有興趣,所以對它底層知識的補充對個人而言,感覺深度有增加。

 

  

養成評估時空開銷的習慣

我刷leetcode必看官方解答里估算的時間和空間複雜度。這也是作為一個架構師的必備基本能力。

數組、哈希這些因為數據的位置不需要進行查找,只需要算數計算就可以得到,所以它們的時間複雜度是O(1)。

鏈表如果直接在頭部或者尾部插入,因為不需要查找,所以時間複雜度也是O(1),但是定位的話因為涉及查找,按遍歷查找來算是log(n)。所以對於jdk1.7之前,hashmap底層採用的是數組+鏈表的數據結構。所以如果不算哈希衝突和擴容的話,獲取和插入數據的時間複雜度是O(1);如果出現了哈希衝突,則插入因為是頭部插入,時間複雜度還是O(1);獲取時間複雜度因為涉及先查找,所以是O(n),這個n代表衝突的數量。

對於在有序數據中進行查找,因為可採用二分查找等優化,時間複雜度可降到log(n).

對於遞歸調用,如果遞歸方法內進行2次調用。對於層數n來說,時間複雜度是2的n次方。舉個例子就是一個數等於前面兩個數之和。當然,如果是前面3個數之和,不進行優化的情況下時間複雜度就是3的n次方。

對於一個n*m的二維數組等需要進行嵌套循環遍歷的,時間複雜度是O(n*m),有個特殊情況是n*m,這時候時間複雜度是n的平方。

對於全排列的情況,時間複雜度是O(n!)。

 

代碼簡化的方法

 

我習慣的一種學習方法是先做題,有了一定自己的總結和思考之後,再看書學習別人的總結思考方法。對於刷leetcode相關性高,也比較受認可的書是《Cracking the Coding interview(6th)》,中文版翻譯是《程序員面試金典》。這本書對於面試官和面試者來說讀了都會有一定的收穫。

 

我讀了這本書,對我印象最深的是介紹了兩種代碼優化的方法:BUD和BCR。

 

BUD 

BUD是瓶頸、不必要工作、重複工作 三個詞組首字母的縮寫。

 

作者提出拿到一道編程題,可先嘗試用暴力解法將題目寫出來,之後找到解法的性能瓶頸,針對瓶頸進行優化,之後在去掉不必要的工作,最後去掉重複的工作。

這個經典的編程優化方法不只可應用於編程,還可應用於整個程序的優化,也是最常規的優化方法。

 

BCR

BCR是Best Conceivable Runtime的縮寫,意思是想知道自己可以優化到什麼程度,先估算可達到的最優情況。

比如:在一個無序數組中,查找兩個兩個相同的數。直覺來說如果找到這兩個數,最起碼需要將每個數都遍歷一遍,所以可達到的最優情況是O(n),無論怎麼優化,都不可能比這個更好。所以這就是優化的上限。

這本書里還介紹了其他的優化方法如:使用額外數據結構、通過構建測試用例、根據題目的限制和提示來尋找線索,大家看這本書的時候可以了解下。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

作為一個Java開發你用過Jib嗎

1. 前言

JibGoogle開發的可以直接構建 Java應用的DockerOCI鏡像的類庫,以MavenGradle插件形式提供。它最騷操作的是可以在沒有Docker守護程序的情況下構建,也就是說,您不必在計算機上安裝docker守護程序!儘管Spring Boot 2.3.0.RELEASE已經推出了構建鏡像的功能,胖哥還是忍不住要試試Jib

其實最騷的還是名字。

2. Docker構建流程和Jib的構建流程

沒有對比就沒有傷害。我們還是要對比一下這兩者的構建流程。

Docker構建流程需要我們先把項目打成Jar然後編寫Dockerfile,然後使用Docker構建功能進行構建鏡像、運行容器。流程如下:

而Jib是這樣構建的:

作為一個Java開發者,不用再關心各種無關的命令和操作,只需要專註於Java,而且高效穩定以及可復用的增量構建。為什麼Jib能這麼快而高效?

傳統上,將Java應用程序與應用程序Jar一起構建為單個圖像層,而Jib的構建策略將Java應用程序分為多層,以進行更細化的增量構建。更改代碼時,僅重建更改,而不重建整個應用程序。

3. Jib構建Spring Boot應用

接下來我將演示如何將Spring Boot 應用打成鏡像並上傳到Dockerhub倉庫。

Maven工程為例,我們只需要在pom.xml中引入Jib Maven 插件。默認情況下Jib會把我們打好的鏡像上傳到Googlegcr.io倉庫,實際中我們會把打好的鏡像上傳到私有倉庫,所以我們要加一些個性化配置。這裏我以dockerhub倉庫為例添加一些個性化配置:

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>2.4.0</version>
    <configuration>
        <!-- 相當於 Dockerfile 中的 FROM -->
        <from>
            <image>amazoncorretto:8</image>
        </from>
        <to>
            <!--構建鏡像名稱,這裏我使用maven中定義的項目名稱-->
            <image>daxus/${project.name}</image>
            <!--私有倉庫的賬號密碼-->
            <auth>
                <username>felordcn</username>
                <password>yourpassword</password>
            </auth>
            <!--Docker 鏡像的 tag 這裏使用maven定義的版本號-->
            <tags>
                <tag>
                    ${project.version}
                </tag>
            </tags>
        </to>
    </configuration>
</plugin>

然後在項目根目錄執行mvn clean compile jib:build就可以了。

其實也可以簡單引入Jib插件:

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>2.4.0</version>
</plugin>

只不過我們的命令會更複雜一些,需要指定一些必要的參數,例如:

mvn clean compile jib:build \
    -Djib.to.image=myregistry/myimage:latest \
    -Djib.to.auth.username=$USERNAME \
    -Djib.to.auth.password=$PASSWORD

更多的定製命令可參考官方文檔:

https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#extended-usage

4. 總結

Jib使用起來非常簡單,讓開發人員以Java的風格來完成Docker鏡像的構建,能夠大大改善編程的體驗。多多關注:碼農小胖哥 獲取更多有用的編程乾貨教程。

關注公眾號:Felordcn 獲取更多資訊

個人博客:https://felord.cn

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

重學 Java 設計模式:實戰中介者模式「按照Mybatis原理手寫ORM框架,給JDBC方式操作數據庫增加中介者場景」

作者:小傅哥
博客:https://bugstack.cn – 原創系列專題文章

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

一、前言

同齡人的差距是從什麼時候拉開的

同樣的幼兒園、同樣的小學、一樣的書本、一樣的課堂,有人學習好、有人學習差。不只是上學,幾乎人生處處都是賽道,發令槍響起的時刻,也就把人生的差距拉開。編程開發這條路也是很長很寬,有人跑得快有人跑得慢。那麼你是否想起過,這一點點的差距到遙不可及的距離,是從哪一天開始的。摸摸肚子的肉,看看遠處的路,別人講的是故事,你想起的都是事故

思想沒有產品高才寫出一片的ifelse

當你承接一個需求的時候,比如;交易、訂單、營銷、保險等各類場景。如果你不熟悉這個場景下的業務模式,以及將來的拓展方向,那麼很難設計出良好可擴展的系統。再加上產品功能初建,說老闆要的急,儘快上線。作為程序員的你更沒有時間思考,整體一看現在的需求也不難,直接上手開干(一個方法兩個if語句),這樣確實滿足了當前需求。但老闆的想法多呀,產品也跟着變化快,到你這就是改改改,加加加。當然你也不客氣,回首掏就是1024個if語句!

日積月累的技術沉澱是為了厚積薄發

粗略的估算過,如果從上大學開始每天寫200行,一個月是6000行,一年算10個月話,就是6萬行,第三年出去實習的是時候就有20萬行的代碼量。如果你能做到這一點,找工作難?有時候很多事情就是靠時間積累出來的,想走捷徑有時候真的沒有。你的技術水平、你的業務能力、你身上的肉,都是一點點積累下來的,不要浪費看似很短的時間,一年年堅持下來,留下印刻青春的痕迹,多給自己武裝上一些能力。

二、開發環境

  1. JDK 1.8
  2. Idea + Maven
  3. mysql 5.1.20
  4. 涉及工程一個,可以通過關注公眾號bugstack蟲洞棧,回復源碼下載獲取(打開獲取的鏈接,找到序號18)
工程 描述
itstack-demo-design-16-01 使用JDBC方式連接數據庫
itstack-demo-design-16-02 手寫ORM框架操作數據庫

三、中介者模式介紹

中介者模式要解決的就是複雜功能應用之間的重複調用,在這中間添加一層中介者包裝服務,對外提供簡單、通用、易擴展的服務能力。

這樣的設計模式幾乎在我們日常生活和實際業務開發中都會見到,例如;飛機降落有小姐姐在塔台喊話、無論哪個方向來的候車都從站台上下、公司的系統中有一个中台專門為你包裝所有接口和提供統一的服務等等,這些都運用了中介者模式。除此之外,你用到的一些中間件,他們包裝了底層多種數據庫的差異化,提供非常簡單的方式進行使用。

四、案例場景模擬

在本案例中我們通過模仿Mybatis手寫ORM框架,通過這樣操作數據庫學習中介者運用場景

除了這樣的中間件層使用場景外,對於一些外部接口,例如N種獎品服務,可以由中台系統進行統一包裝對外提供服務能力。也是中介者模式的一種思想體現。

在本案例中我們會把jdbc層進行包裝,讓用戶在使用數據庫服務的時候,可以和使用mybatis一樣簡單方便,通過這樣的源碼方式學習中介者模式,也方便對源碼知識的拓展學習,增強知識棧。

五、用一坨坨代碼實現

這是一種關於數據庫操作最初的方式

基本上每一個學習開發的人都學習過直接使用jdbc方式連接數據庫,進行CRUD操作。以下的例子可以當做回憶。

1. 工程結構

itstack-demo-design-16-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── JDBCUtil.java
  • 這裏的類比較簡單隻包括了一個數據庫操作類。

2. 代碼實現

public class JDBCUtil {

    private static Logger logger = LoggerFactory.getLogger(JDBCUtil.class);

    public static final String URL = "jdbc:mysql://127.0.0.1:3306/itstack-demo-design";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";

    public static void main(String[] args) throws Exception {
        //1. 加載驅動程序
        Class.forName("com.mysql.jdbc.Driver");
        //2. 獲得數據庫連接
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        //3. 操作數據庫
        Statement stmt = conn.createStatement();
        ResultSet resultSet = stmt.executeQuery("SELECT id, name, age, createTime, updateTime FROM user");
        //4. 如果有數據 resultSet.next() 返回true
        while (resultSet.next()) {
            logger.info("測試結果 姓名:{} 年齡:{}", resultSet.getString("name"),resultSet.getInt("age"));
        }
    }

}
  • 以上是使用JDBC的方式進行直接操作數據庫,幾乎大家都使用過這樣的方式。

3. 測試結果

15:38:10.919 [main] INFO  org.itstack.demo.design.JDBCUtil - 測試結果 姓名:水水 年齡:18
15:38:10.922 [main] INFO  org.itstack.demo.design.JDBCUtil - 測試結果 姓名:豆豆 年齡:18
15:38:10.922 [main] INFO  org.itstack.demo.design.JDBCUtil - 測試結果 姓名:花花 年齡:19

Process finished with exit code 0
  • 從測試結果可以看到這裏已經查詢到了數據庫中的數據。只不過如果在全部的業務開發中都這樣實現,會非常的麻煩。

六、中介模式開發ORM框架

`接下來就使用中介模式的思想完成模仿Mybatis的ORM框架開發~

1. 工程結構

itstack-demo-design-16-02
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo.design
    │   │       ├── dao
    │   │       │	├── ISchool.java
    │   │       │	└── IUserDao.java
    │   │       ├── mediator
    │   │       │	├── Configuration.java
    │   │       │	├── DefaultSqlSession.java
    │   │       │	├── DefaultSqlSessionFactory.java
    │   │       │	├── Resources.java
    │   │       │	├── SqlSession.java
    │   │       │	├── SqlSessionFactory.java
    │   │       │	├── SqlSessionFactoryBuilder.java
    │   │       │	└── SqlSessionFactoryBuilder.java
    │   │       └── po
    │   │         	├── School.java
    │   │         	└── User.java
    │   └── resources
    │       ├── mapper
    │       │   ├── School_Mapper.xml
    │       │   └── User_Mapper.xml
    │       └── mybatis-config-datasource.xml
    └── test
         └── java
             └── org.itstack.demo.design.test
                 └── ApiTest.java

中介者模式模型結構

  • 以上是對ORM框架實現的核心類,包括了;加載配置文件、對xml解析、獲取數據庫session、操作數據庫以及結果返回。
  • 左上是對數據庫的定義和處理,基本包括我們常用的方法;<T> T selectOne<T> List<T> selectList等。
  • 右側藍色部分是對數據庫配置的開啟session的工廠處理類,這裏的工廠會操作DefaultSqlSession
  • 之後是紅色地方的SqlSessionFactoryBuilder,這個類是對數據庫操作的核心類;處理工廠、解析文件、拿到session等。

接下來我們就分別介紹各個類的功能實現過程。

2. 代碼實現

2.1 定義SqlSession接口

public interface SqlSession {

    <T> T selectOne(String statement);

    <T> T selectOne(String statement, Object parameter);

    <T> List<T> selectList(String statement);

    <T> List<T> selectList(String statement, Object parameter);

    void close();
}
  • 這裏定義了對數據庫操作的查詢接口,分為查詢一個結果和查詢多個結果,同時包括有參數和沒有參數的方法。

2.2 SqlSession具體實現類

public class DefaultSqlSession implements SqlSession {

    private Connection connection;
    private Map<String, XNode> mapperElement;

    public DefaultSqlSession(Connection connection, Map<String, XNode> mapperElement) {
        this.connection = connection;
        this.mapperElement = mapperElement;
    }

    @Override
    public <T> T selectOne(String statement) {
        try {
            XNode xNode = mapperElement.get(statement);
            PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
            ResultSet resultSet = preparedStatement.executeQuery();
            List<T> objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
            return objects.get(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public <T> List<T> selectList(String statement) {
        XNode xNode = mapperElement.get(statement);
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
            ResultSet resultSet = preparedStatement.executeQuery();
            return resultSet2Obj(resultSet, Class.forName(xNode.getResultType()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // ...

    private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
        List<T> list = new ArrayList<>();
        try {
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            // 每次遍歷行值
            while (resultSet.next()) {
                T obj = (T) clazz.newInstance();
                for (int i = 1; i <= columnCount; i++) {
                    Object value = resultSet.getObject(i);
                    String columnName = metaData.getColumnName(i);
                    String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
                    Method method;
                    if (value instanceof Timestamp) {
                        method = clazz.getMethod(setMethod, Date.class);
                    } else {
                        method = clazz.getMethod(setMethod, value.getClass());
                    }
                    method.invoke(obj, value);
                }
                list.add(obj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    @Override
    public void close() {
        if (null == connection) return;
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
  • 這裏包括了接口定義的方法實現,也就是包裝了jdbc層。
  • 通過這樣的包裝可以讓對數據庫的jdbc操作隱藏起來,外部調用的時候對入參、出參都有內部進行處理。

2.3 定義SqlSessionFactory接口

public interface SqlSessionFactory {

    SqlSession openSession();

}
  • 開啟一個SqlSession, 這幾乎是大家在平時的使用中都需要進行操作的內容。雖然你看不見,但是當你有數據庫操作的時候都會獲取每一次執行的SqlSession

2.4 SqlSessionFactory具體實現類

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(configuration.connection, configuration.mapperElement);
    }

}
  • DefaultSqlSessionFactory,是使用mybatis最常用的類,這裏我們簡單的實現了一個版本。
  • 雖然是簡單的版本,但是包括了最基本的核心思路。當開啟SqlSession時會進行返回一個DefaultSqlSession
  • 這個構造函數中向下傳遞了Configuration配置文件,在這個配置文件中包括;Connection connectionMap<String, String> dataSourceMap<String, XNode> mapperElement。如果有你閱讀過Mybatis源碼,對這個就不會陌生。

2.5 SqlSessionFactoryBuilder實現

public class SqlSessionFactoryBuilder {

    public DefaultSqlSessionFactory build(Reader reader) {
        SAXReader saxReader = new SAXReader();
        try {
            saxReader.setEntityResolver(new XMLMapperEntityResolver());
            Document document = saxReader.read(new InputSource(reader));
            Configuration configuration = parseConfiguration(document.getRootElement());
            return new DefaultSqlSessionFactory(configuration);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Configuration parseConfiguration(Element root) {
        Configuration configuration = new Configuration();
        configuration.setDataSource(dataSource(root.selectNodes("//dataSource")));
        configuration.setConnection(connection(configuration.dataSource));
        configuration.setMapperElement(mapperElement(root.selectNodes("mappers")));
        return configuration;
    }

    // 獲取數據源配置信息
    private Map<String, String> dataSource(List<Element> list) {
        Map<String, String> dataSource = new HashMap<>(4);
        Element element = list.get(0);
        List content = element.content();
        for (Object o : content) {
            Element e = (Element) o;
            String name = e.attributeValue("name");
            String value = e.attributeValue("value");
            dataSource.put(name, value);
        }
        return dataSource;
    }

    private Connection connection(Map<String, String> dataSource) {
        try {
            Class.forName(dataSource.get("driver"));
            return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 獲取SQL語句信息
    private Map<String, XNode> mapperElement(List<Element> list) {
        Map<String, XNode> map = new HashMap<>();

        Element element = list.get(0);
        List content = element.content();
        for (Object o : content) {
            Element e = (Element) o;
            String resource = e.attributeValue("resource");

            try {
                Reader reader = Resources.getResourceAsReader(resource);
                SAXReader saxReader = new SAXReader();
                Document document = saxReader.read(new InputSource(reader));
                Element root = document.getRootElement();
                //命名空間
                String namespace = root.attributeValue("namespace");

                // SELECT
                List<Element> selectNodes = root.selectNodes("select");
                for (Element node : selectNodes) {
                    String id = node.attributeValue("id");
                    String parameterType = node.attributeValue("parameterType");
                    String resultType = node.attributeValue("resultType");
                    String sql = node.getText();

                    // ? 匹配
                    Map<Integer, String> parameter = new HashMap<>();
                    Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                    Matcher matcher = pattern.matcher(sql);
                    for (int i = 1; matcher.find(); i++) {
                        String g1 = matcher.group(1);
                        String g2 = matcher.group(2);
                        parameter.put(i, g2);
                        sql = sql.replace(g1, "?");
                    }

                    XNode xNode = new XNode();
                    xNode.setNamespace(namespace);
                    xNode.setId(id);
                    xNode.setParameterType(parameterType);
                    xNode.setResultType(resultType);
                    xNode.setSql(sql);
                    xNode.setParameter(parameter);

                    map.put(namespace + "." + id, xNode);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }

        }
        return map;
    }

}
  • 在這個類中包括的核心方法有;build(構建實例化元素)parseConfiguration(解析配置)dataSource(獲取數據庫配置)connection(Map<String, String> dataSource) (鏈接數據庫)mapperElement (解析sql語句)
  • 接下來我們分別介紹這樣的幾個核心方法。

build(構建實例化元素)

這個類主要用於創建解析xml文件的類,以及初始化SqlSession工廠類DefaultSqlSessionFactory。另外需要注意這段代碼saxReader.setEntityResolver(new XMLMapperEntityResolver());,是為了保證在不聯網的時候一樣可以解析xml,否則會需要從互聯網獲取dtd文件。

parseConfiguration(解析配置)

是對xml中的元素進行獲取,這裏主要獲取了;dataSourcemappers,而這兩個配置一個是我們數據庫的鏈接信息,另外一個是對數據庫操作語句的解析。

connection(Map<String, String> dataSource) (鏈接數據庫)

鏈接數據庫的地方和我們常見的方式是一樣的;Class.forName(dataSource.get("driver"));,但是這樣包裝以後外部是不需要知道具體的操作。同時當我們需要鏈接多套數據庫的時候,也是可以在這裏擴展。

mapperElement (解析sql語句)

這部分代碼塊內容相對來說比較長,但是核心的點就是為了解析xml中的sql語句配置。在我們平常的使用中基本都會配置一些sql語句,也有一些入參的佔位符。在這裏我們使用正則表達式的方式進行解析操作。

解析完成的sql語句就有了一個名稱和sql的映射關係,當我們進行數據庫操作的時候,這個組件就可以通過映射關係獲取到對應sql語句進行操作。

3. 測試驗證

在測試之前需要導入sql語句到數據庫中;

  • 庫名:itstack-demo-design
  • 表名:userschool
CREATE TABLE school ( id bigint NOT NULL AUTO_INCREMENT, name varchar(64), address varchar(256), createTime datetime, updateTime datetime, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into school (id, name, address, createTime, updateTime) values (1, '北京大學', '北京市海淀區頤和園路5號', '2019-10-18 13:35:57', '2019-10-18 13:35:57');
insert into school (id, name, address, createTime, updateTime) values (2, '南開大學', '中國天津市南開區衛津路94號', '2019-10-18 13:35:57', '2019-10-18 13:35:57');
insert into school (id, name, address, createTime, updateTime) values (3, '同濟大學', '上海市彰武路1號同濟大廈A樓7樓7區', '2019-10-18 13:35:57', '2019-10-18 13:35:57');
CREATE TABLE user ( id bigint(11) NOT NULL AUTO_INCREMENT, name varchar(32), age int(4), address varchar(128), entryTime datetime, remark varchar(64), createTime datetime, updateTime datetime, status int(4) DEFAULT '0', dateTime varchar(64), PRIMARY KEY (id), INDEX idx_name (name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status, dateTime) values (1, '水水', 18, '吉林省榆樹市黑林鎮尹家村5組', '2019-12-22 00:00:00', '無', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 0, '20200309');
insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status, dateTime) values (2, '豆豆', 18, '遼寧省大連市清河灣司馬道407路', '2019-12-22 00:00:00', '無', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 1, null);
insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status, dateTime) values (3, '花花', 19, '遼寧省大連市清河灣司馬道407路', '2019-12-22 00:00:00', '無', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 0, '20200310');

3.1 創建數據庫對象類

用戶類

public class User {

    private Long id;
    private String name;
    private Integer age;
    private Date createTime;
    private Date updateTime;
    
    // ... get/set
}

學校類

public class School {

    private Long id;
    private String name;
    private String address;
    private Date createTime;
    private Date updateTime;  
  
    // ... get/set
}
  • 這兩個類都非常簡單,就是基本的數據庫信息。

3.2 創建DAO包

用戶Dao

public interface IUserDao {

     User queryUserInfoById(Long id);

}

學校Dao

public interface ISchoolDao {

    School querySchoolInfoById(Long treeId);

}

3.3 ORM配置文件

鏈接配置

<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/itstack_demo_design?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/User_Mapper.xml"/>
        <mapper resource="mapper/School_Mapper.xml"/>
    </mappers>

</configuration>
  • 這個配置與我們平常使用的mybatis基本是一樣的,包括了數據庫的連接池信息以及需要引入的mapper映射文件。

操作配置(用戶)

<mapper namespace="org.itstack.demo.design.dao.IUserDao">

    <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="org.itstack.demo.design.po.User">
        SELECT id, name, age, createTime, updateTime
        FROM user
        where id = #{id}
    </select>

    <select id="queryUserList" parameterType="org.itstack.demo.design.po.User" resultType="org.itstack.demo.design.po.User">
        SELECT id, name, age, createTime, updateTime
        FROM user
        where age = #{age}
    </select>

</mapper>

操作配置(學校)

<mapper namespace="org.itstack.demo.design.dao.ISchoolDao">

    <select id="querySchoolInfoById" resultType="org.itstack.demo.design.po.School">
        SELECT id, name, address, createTime, updateTime
        FROM school
        where id = #{id}
    </select>

</mapper>

3.4 單個結果查詢測試

@Test
public void test_queryUserInfoById() {
    String resource = "mybatis-config-datasource.xml";
    Reader reader;
    try {
        reader = Resources.getResourceAsReader(resource);
        SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sqlMapper.openSession();
        try {
            User user = session.selectOne("org.itstack.demo.design.dao.IUserDao.queryUserInfoById", 1L);
            logger.info("測試結果:{}", JSON.toJSONString(user));
        } finally {
            session.close();
            reader.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  • 這裏的使用方式和Mybatis是一樣的,都包括了;資源加載和解析、SqlSession工廠構建、開啟SqlSession以及最後執行查詢操作selectOne

測試結果

16:56:51.831 [main] INFO  org.itstack.demo.design.demo.ApiTest - 測試結果:{"age":18,"createTime":1576944000000,"id":1,"name":"水水","updateTime":1576944000000}

Process finished with exit code 0
  • 從結果上看已經滿足了我們的查詢需求。

3.5 集合結果查詢測試

@Test
public void test_queryUserList() {
    String resource = "mybatis-config-datasource.xml";
    Reader reader;
    try {
        reader = Resources.getResourceAsReader(resource);
        SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sqlMapper.openSession();
        try {
            User req = new User();
            req.setAge(18);
            List<User> userList = session.selectList("org.itstack.demo.design.dao.IUserDao.queryUserList", req);
            logger.info("測試結果:{}", JSON.toJSONString(userList));
        } finally {
            session.close();
            reader.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  • 這個測試內容與以上只是查詢方法有所不同;session.selectList,是查詢一個集合結果。

測試結果

16:58:13.963 [main] INFO  org.itstack.demo.design.demo.ApiTest - 測試結果:[{"age":18,"createTime":1576944000000,"id":1,"name":"水水","updateTime":1576944000000},{"age":18,"createTime":1576944000000,"id":2,"name":"豆豆","updateTime":1576944000000}]

Process finished with exit code 0
  • 測試驗證集合的結果也是正常的,目前位置測試全部通過。

七、總結

  • 以上通過中介者模式的設計思想我們手寫了一個ORM框架,隱去了對數據庫操作的複雜度,讓外部的調用方可以非常簡單的進行操作數據庫。這也是我們平常使用的Mybatis的原型,在我們日常的開發使用中,只需要按照配置即可非常簡單的操作數據庫。
  • 除了以上這種組件模式的開發外,還有服務接口的包裝也可以使用中介者模式來實現。比如你們公司有很多的獎品接口需要在營銷活動中對接,那麼可以把這些獎品接口統一收到中台開發一個獎品中心,對外提供服務。這樣就不需要每一個需要對接獎品的接口,都去找具體的提供者,而是找中台服務即可。
  • 在上述的實現和測試使用中可以看到,這種模式的設計滿足了;單一職責開閉原則,也就符合了迪米特原則,即越少人知道越好。外部的人只需要按照需求進行調用,不需要知道具體的是如何實現的,複雜的一面已經有組件合作服務平台處理。

八、推薦閱讀

  • 1. 重學 Java 設計模式:實戰工廠方法模式「多種類型商品不同接口,統一發獎服務搭建場景」
  • 2. 重學 Java 設計模式:實戰原型模式「上機考試多套試,每人題目和答案亂序排列場景」
  • 3. 重學 Java 設計模式:實戰橋接模式「多支付渠道(微信、支付寶)與多支付模式(刷臉、指紋)場景」
  • 4. 重學 Java 設計模式:實戰組合模式「營銷差異化人群發券,決策樹引擎搭建場景」
  • 5. 重學 Java 設計模式:實戰外觀模式「基於SpringBoot開發門面模式中間件,統一控制接口白名單場景」
  • 6. 重學 Java 設計模式:實戰享元模式「基於Redis秒殺,提供活動與庫存信息查詢場景」

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

為什麼建議你使用枚舉?

枚舉是 JDK 1.5 新增的數據類型,使用枚舉我們可以很好的描述一些特定的業務場景,比如一年中的春、夏、秋、冬,還有每周的周一到周天,還有各種顏色,以及可以用它來描述一些狀態信息,比如錯誤碼等。

枚舉類型不止存在在 Java 語言中,在其它語言中也都能找到它的身影,例如 C# 和 Python 等,但我發現在實際的項目中使用枚舉的人很少,所以本文就來聊一聊枚舉的相關內容,好讓朋友們對枚舉有一個大概的印象,這樣在編程時起碼還能想到有“枚舉”這樣一個類型。

本文的結構目錄如下:

枚舉的 7 種使用方法

很多人不使用枚舉的一個重要的原因是對枚舉不夠熟悉,那麼我們就先從枚舉的 7 種使用方法說起。

用法一:常量

在 JDK 1.5 之前,我們定義常量都是 public static final... ,但有了枚舉,我們就可以把這些常量定義成一個枚舉類了,實現代碼如下:

public enum ColorEnum {  
  RED, GREEN, BLANK, YELLOW  
} 

用法二:switch

將枚舉用在 switch 判斷中,使得代碼可讀性更高了,實現代碼如下:

enum ColorEnum {
    GREEN, YELLOW, RED
}
public class ColorTest {
    ColorEnum color = ColorEnum.RED;

    public void change() {
        switch (color) {
            case RED:
                color = ColorEnum.GREEN;
                break;
            case YELLOW:
                color = ColorEnum.RED;
                break;
            case GREEN:
                color = ColorEnum.YELLOW;
                break;
        }
    }
}

用法三:枚舉中增加方法

我們可以在枚舉中增加一些方法,讓枚舉具備更多的特性,實現代碼如下:

public class EnumTest {
    public static void main(String[] args) {
        ErrorCodeEnum errorCode = ErrorCodeEnum.SUCCESS;
        System.out.println("狀態碼:" + errorCode.code() + 
                           " 狀態信息:" + errorCode.msg());
    }
}

enum ErrorCodeEnum {
    SUCCESS(1000, "success"),
    PARAM_ERROR(1001, "parameter error"),
    SYS_ERROR(1003, "system error"),
    NAMESPACE_NOT_FOUND(2001, "namespace not found"),
    NODE_NOT_EXIST(3002, "node not exist"),
    NODE_ALREADY_EXIST(3003, "node already exist"),
    UNKNOWN_ERROR(9999, "unknown error");

    private int code;
    private String msg;

    ErrorCodeEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int code() {
        return code;
    }

    public String msg() {
        return msg;
    }

    public static ErrorCodeEnum getErrorCode(int code) {
        for (ErrorCodeEnum it : ErrorCodeEnum.values()) {
            if (it.code() == code) {
                return it;
            }
        }
        return UNKNOWN_ERROR;
    }
}

以上程序的執行結果為:

狀態碼:1000 狀態信息:success

用法四:覆蓋枚舉方法

我們可以覆蓋一些枚舉中的方法用於實現自己的業務,比如我們可以覆蓋 toString() 方法,實現代碼如下:

public class EnumTest {
    public static void main(String[] args) {
        ColorEnum colorEnum = ColorEnum.RED;
        System.out.println(colorEnum.toString());
    }
}

enum ColorEnum {
    RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLOW("黃色", 4);
    //  成員變量
    private String name;
    private int index;

    //  構造方法
    private ColorEnum(String name, int index) {
        this.name = name;
        this.index = index;
    }

    //覆蓋方法
    @Override
    public String toString() {
        return this.index + ":" + this.name;
    }
}

以上程序的執行結果為:

1:紅色

用法五:實現接口

枚舉類可以用來實現接口,但不能用於繼承類,因為枚舉默認繼承了 java.lang.Enum 類,在 Java 語言中允許實現多接口,但不能繼承多個父類,實現代碼如下:

public class EnumTest {
    public static void main(String[] args) {
        ColorEnum colorEnum = ColorEnum.RED;
        colorEnum.print();
        System.out.println("顏色:" + colorEnum.getInfo());
    }
}

interface Behaviour {
    void print();

    String getInfo();
}

enum ColorEnum implements Behaviour {
    RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLOW("黃色", 4);
    private String name;
    private int index;

    private ColorEnum(String name, int index) {
        this.name = name;
        this.index = index;
    }

    @Override
    public void print() {
        System.out.println(this.index + ":" + this.name);
    }

    @Override
    public String getInfo() {
        return this.name;
    }
}

以上程序的執行結果為:

1:紅色

顏色:紅色

用法六:在接口中組織枚舉類

我們可以在一個接口中創建多個枚舉類,用它可以很好的實現“多態”,也就是說我們可以將擁有相同特性,但又有細微實現差別的枚舉類聚集在一個接口中,實現代碼如下:

public class EnumTest {
    public static void main(String[] args) {
        // 賦值第一個枚舉類
        ColorInterface colorEnum = ColorInterface.ColorEnum.RED;
        System.out.println(colorEnum);
        // 賦值第二個枚舉類
        colorEnum = ColorInterface.NewColorEnum.NEW_RED;
        System.out.println(colorEnum);
    }
}

interface ColorInterface {
    enum ColorEnum implements ColorInterface {
        GREEN, YELLOW, RED
    }
    enum NewColorEnum implements ColorInterface {
        NEW_GREEN, NEW_YELLOW, NEW_RED
    }
}

以上程序的執行結果為:

RED

NEW_RED

用法七:使用枚舉集合

在 Java 語言中和枚舉類相關的,還有兩個枚舉集合類 java.util.EnumSetjava.util.EnumMap,使用它們可以實現更多的功能。

使用 EnumSet 可以保證元素不重複,並且能獲取指定範圍內的元素,示例代碼如下:

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

public class EnumTest {
    public static void main(String[] args) {
        List<ColorEnum> list = new ArrayList<ColorEnum>();
        list.add(ColorEnum.RED);
        list.add(ColorEnum.RED);  // 重複元素
        list.add(ColorEnum.YELLOW);
        list.add(ColorEnum.GREEN);
        // 去掉重複數據
        EnumSet<ColorEnum> enumSet = EnumSet.copyOf(list);
        System.out.println("去重:" + enumSet);

        // 獲取指定範圍的枚舉(獲取所有的失敗狀態)
        EnumSet<ErrorCodeEnum> errorCodeEnums = EnumSet.range(ErrorCodeEnum.ERROR, ErrorCodeEnum.UNKNOWN_ERROR);
        System.out.println("所有失敗狀態:" + errorCodeEnums);
    }
}

enum ColorEnum {
    RED("紅色", 1), GREEN("綠色", 2), BLANK("白色", 3), YELLOW("黃色", 4);
    private String name;
    private int index;

    private ColorEnum(String name, int index) {
        this.name = name;
        this.index = index;
    }
}

enum ErrorCodeEnum {
    SUCCESS(1000, "success"),
    ERROR(2001, "parameter error"),
    SYS_ERROR(2002, "system error"),
    NAMESPACE_NOT_FOUND(2003, "namespace not found"),
    NODE_NOT_EXIST(3002, "node not exist"),
    NODE_ALREADY_EXIST(3003, "node already exist"),
    UNKNOWN_ERROR(9999, "unknown error");

    private int code;
    private String msg;

    ErrorCodeEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public int code() {
        return code;
    }

    public String msg() {
        return msg;
    }
}

以上程序的執行結果為:

去重:[RED, GREEN, YELLOW]

所有失敗狀態:[ERROR, SYS_ERROR, NAMESPACE_NOT_FOUND, NODE_NOT_EXIST, NODE_ALREADY_EXIST, UNKNOWN_ERROR]

EnumMapHashMap 類似,不過它是一個專門為枚舉設計的 Map 集合,相比 HashMap 來說它的性能更高,因為它內部放棄使用鏈表和紅黑樹的結構,採用數組作為數據存儲的結構。

EnumMap 基本使用示例如下:

import java.util.EnumMap;

public class EnumTest {
    public static void main(String[] args) {
        EnumMap<ColorEnum, String> enumMap = new EnumMap<>(ColorEnum.class);
        enumMap.put(ColorEnum.RED, "紅色");
        enumMap.put(ColorEnum.GREEN, "綠色");
        enumMap.put(ColorEnum.BLANK, "白色");
        enumMap.put(ColorEnum.YELLOW, "黃色");
        System.out.println(ColorEnum.RED + ":" + enumMap.get(ColorEnum.RED));
    }
}

enum ColorEnum {
    RED, GREEN, BLANK, YELLOW;
}

以上程序的執行結果為:

RED:紅色

使用注意事項

阿里《Java開發手冊》對枚舉的相關規定如下,我們在使用時需要稍微注意一下。

【強制】所有的枚舉類型字段必須要有註釋,說明每個數據項的用途。

【參考】枚舉類名帶上 Enum 後綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。說明:枚舉其實就是特殊的常量類,且構造方法被默認強制是私有。正例:枚舉名字為 ProcessStatusEnum 的成員名稱:SUCCESS / UNKNOWN_REASON。

假如不使用枚舉

在枚舉沒有誕生之前,也就是 JDK 1.5 版本之前,我們通常會使用 int 常量來表示枚舉,實現代碼如下:

public static final int COLOR_RED = 1;
public static final int COLOR_BLUE = 2;
public static final int COLOR_GREEN = 3;

但是使用 int 類型可能存在兩個問題:

第一, int 類型本身並不具備安全性,假如某個程序員在定義 int 時少些了一個 final 關鍵字,那麼就會存在被其他人修改的風險,而反觀枚舉類,它“天然”就是一個常量類,不存在被修改的風險(原因詳見下半部分);

第二,使用 int 類型的語義不夠明確,比如我們在控制台打印時如果只輸出 1…2…3 這樣的数字,我們肯定不知道它代表的是什麼含義。

那有人就說了,那就使用常量字符唄,這總不會還不知道語義吧?實現示例代碼如下:

public static final String COLOR_RED = "RED";
public static final String COLOR_BLUE = "BLUE";
public static final String COLOR_GREEN = "GREEN";

但是這樣同樣存在一個問題,有些初級程序員會不按套路出牌,他們可能會直接使用字符串的值進行比較,而不是直接使用枚舉的字段,實現示例代碼如下:

public class EnumTest {
    public static final String COLOR_RED = "RED";
    public static final String COLOR_BLUE = "BLUE";
    public static final String COLOR_GREEN = "GREEN";
    public static void main(String[] args) {
        String color = "BLUE";
        if ("BLUE".equals(color)) {
            System.out.println("藍色");
        }
    }
}

這樣當我們修改了枚舉中的值,那程序就涼涼了。

枚舉使用場景

枚舉的常見使用場景是單例,它的完整實現代碼如下:

public class Singleton {
    // 枚舉類型是線程安全的,並且只會裝載一次
    private enum SingletonEnum {
        INSTANCE;
        // 聲明單例對象
        private final Singleton instance;
        // 實例化
        SingletonEnum() {
            instance = new Singleton();
        }
        private Singleton getInstance() {
            return instance;
        }
    }
    // 獲取實例(單例對象)
    public static Singleton getInstance() {
        return SingletonEnum.INSTANCE.getInstance();
    }
    private Singleton() {
    }
    // 類方法
    public void sayHi() {
        System.out.println("Hi,Java.");
    }
}
class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.sayHi();
    }
}

因為枚舉只會在類加載時裝載一次,所以它是線程安全的,這也是《Effective Java》作者極力推薦使用枚舉來實現單例的主要原因。

知識擴展

枚舉為什麼是線程安全的?

這一點要從枚舉最終生成的字節碼說起,首先我們先來定義一個簡單的枚舉類:

public enum ColorEnumTest {
    RED, GREEN, BLANK, YELLOW;
}

然後我們再將上面的那段代碼編譯為字節碼,具體內容如下:

public final class ColorEnumTest extends java.lang.Enum<ColorEnumTest> {
  public static final ColorEnumTest RED;
  public static final ColorEnumTest GREEN;
  public static final ColorEnumTest BLANK;
  public static final ColorEnumTest YELLOW;
  public static ColorEnumTest[] values();
  public static ColorEnumTest valueOf(java.lang.String);
  static {};
}

從上述結果可以看出枚舉類最終會被編譯為被 final 修飾的普通類,它的所有屬性也都會被 staticfinal 關鍵字修飾,所以枚舉類在項目啟動時就會被 JVM 加載並初始化,而這個執行過程是線程安全的,所以枚舉類也是線程安全的類。

小貼士:代碼反編譯的過程是先用 javac 命令將 java 代碼編譯字節碼(.class),再使用 javap 命令查看編譯的字節碼。

枚舉比較小技巧

我們在枚舉比較時使用 == 就夠了,因為枚舉類是在程序加載時就創建了(它並不是 new 出來的),並且枚舉類不允許在外部直接使用 new 關鍵字來創建枚舉實例,所以我們在使用枚舉類時本質上只有一個對象,因此在枚舉比較時使用 == 就夠了。

並且我們在查看枚舉的 equlas() 源碼會發現,它的內部其實還是直接調用了 == 方法,源碼如下:

public final boolean equals(Object other) {
    return this==other;
}

總結

本文我們介紹了枚舉類的 7 種使用方法:常量、switch、枚舉中添加方法、覆蓋枚舉方法、實現接口、在接口中組織枚舉類和使用枚舉集合等,然後講了如果不使用枚舉類使用 int 類型和 String 類型存在的一些弊端:語義不夠清晰、容易被修改、存在被誤用的風險,所以我們在適合的環境下應該盡量使用枚舉類。並且我們還講了枚舉類的使用場景——單例,以及枚舉類為什麼是安全的,最後我們講了枚舉比較的小技巧,希望本文對你有幫助。

查看 & 鳴謝

https://www.iteye.com/blog/softbeta-1185573

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

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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

特斯拉將公布儲能電池計畫 F-貿聯可望受惠

特斯拉傳將於 4 月 30 日公布儲能電池計畫,市場推測,該計畫將包括住宅與公共規模市場,業界傳出,本來就是特斯拉重要夥伴的 F-貿聯,有機會獲新電力線訂單。而今年在特斯拉需求持續強勁,與 Type C 等外接式擴充介面的需求爆發下,法人預期,該公司今年營運將逐季走高,全年營收看增 15%。   F-貿聯首季營收 19.04 億元,年增 10%,創下歷年同期新高。而特斯拉傳出將於下周公布最新的儲能電池計畫,據市場傳言表示,貿聯本來就是特斯拉在電動車電池模組用線與超級充電樁的線束的主力供應商,未來不排除大型公共規模用儲能電池,將與現有充電站結合。而貿聯有機會順勢切入取得新產品,並以公用市場應用的電力線為主,近日有望已小量出貨。   法人預估,F-貿聯今年除來自特斯拉的需求成長外,另在其他車用佈局方面,包括全地形車大客戶全車線束、擴大歐美客戶於倒車雷達等新產品的開發;另外針對美國官方對公營單位節能環保的需求,也配合客戶開發出得以改裝現有車為電動車的配備裝置,隨未來該商業模式成型,貢獻將逐步擴大。  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

比亞迪Q1財報亮眼:淨利暴漲9倍

比亞迪昨(27)日晚間公佈2015年第1季財報,其營業收入為人民幣152.83億元(約新台幣762億元),較去年同期成長30.35%;歸屬於上市公司股東的淨利潤為人民幣1.21億元(約新台幣6億元),較去年同期成長910.75%;基本每股收益為人民幣0.05元(約新台幣0.25元)。   比亞迪表示,營業收入增長主要是汽車業務增加所致。同時比亞迪預估今年1至6月的淨利潤將達到人民幣4至4.8億元(約新台幣20至24億元),較去年同期的人民幣3.61億元(約新台幣18億元)成長10.90%至33.08%。   據證券時報網報導,在今年的佈局規劃方面,比亞迪將啟動「7+4」佈局,涵蓋私家車、公車在內的七個主要目標市場和四個特種車市場,並將憑藉自主品牌在SUV市場的突破性契機,將新能源汽車從轎車拓展至SUV車型,豐富比亞迪的產品類型。此外,比亞迪還將開發出集約用地的循環式立體充電機,積極尋求與協力廠商的合作,加快充電設施的普及。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

迄今最大海外訂單!比亞迪獲美國60輛電動大巴訂購

4月28日,比亞迪宣佈,洛杉磯大都會部分地區提供服務的長灘運輸署將訂購比亞迪的10輛電池動力巴士,並有增購不超過50輛電動巴士的選擇權。這是比亞迪迄今為止在國際市場上最大一筆電動巴士訂單。   據比亞迪的資訊顯示,其一年前在加州開設一家巴士工廠,以打入美國、加拿大和拉美市場,目前美國大約有50輛比亞迪電動巴士在運營。而比亞迪銷售總監侯雁上週在上海車展表示,比亞迪2015年計畫銷售大約15,000輛電動計程車和大約6,000輛電動巴士。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

特斯拉 Model 3 開發進度落後 量產恐待 2017 年底

期待特斯拉平價電動車 Model 3 明年上市的消費者可能要失望了,消息傳出 Model 3 Alpha 版原型車目前連影子都沒出現,意味者上市時間可能往後順延。   特斯拉執行長馬斯克(Elon Musk)宣布投入平價電動車市場時,曾信誓旦旦將在 2016 年底進入量產,但據科技網站 Venturebeat.com 報導,業界普遍預期 Model 3 量產最快可能要等到 2017 年底才會實現。   若跟隨 Model S 的開發時程走,Model 3 至少還需要 30 個月的開發時間,也就是 2017 年 12 月,首輛 Model 3 才有可能交車。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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

最新的一波Vue實戰技巧,不用則已,一用驚人

Vue中,不同的選項有不同的合併策略,比如 data,props,methods是同名屬性覆蓋合併,其他直接合併,而生命周期鈎子函數則是將同名的函數放到一個數組中,在調用的時候依次調用

Vue中,提供了一個api, Vue.config.optionMergeStrategies,可以通過這個api去自定義選項的合併策略。

在代碼中打印

console.log(Vue.config.optionMergeStrategies)

  

 通過合併策略自定義生命周期函數

背景

最近客戶給領導反饋,我們的系統用一段時間,瀏覽器就變得有點卡,不知道為什麼。問題出來了,本來想甩鍋到後端,但是瀏覽器問題,沒法甩鍋啊,那就排查吧。

後來發現頁面有許多定時器,ajax輪詢還有動畫,打開一個瀏覽器頁簽沒法問題,打開多了,瀏覽器就變得卡了,這時候我就想如果能在用戶切換頁簽時候將這些都停掉,不久解決了。百度裏面上下檢索,找到了一個事件visibilitychange,可以用來判斷瀏覽器頁簽是否显示。

有方法了,就寫唄

export default {
  created() {
    window.addEventListener('visibilitychange', this.$_hanldeVisiblityChange)
    // 此處用了hookEvent,可以參考小編前一篇文章
    this.$on('hook:beforeDestroy', () => {
      window.removeEventListener(
        'visibilitychange',
        this.$_hanldeVisiblityChange
      )
    })
  },
  methods: {
    $_hanldeVisiblityChange() {
      if (document.visibilityState === 'hidden') {
        // 停掉那一堆東西
      }
      if (document.visibilityState === 'visible') {
        // 開啟那一堆東西
      }
    }
  }
}

通過上面的代碼,可以看到在每一個需要監聽處理的文件都要寫一堆事件監聽,判斷頁面是否显示的代碼,一處兩處還可以,文件多了就頭疼了,這時候小編突發奇想,定義一個頁面显示隱藏的生命周期鈎子,把這些判斷都封裝起來

自定義生命周期鈎子函數

定義生命周期函數 pageHidden 與 pageVisible

import Vue from 'vue'

// 通知所有組件頁面狀態發生了變化
const notifyVisibilityChange = (lifeCycleName, vm) => {
  // 生命周期函數會存在$options中,通過$options[lifeCycleName]獲取生命周期
  const lifeCycles = vm.$options[lifeCycleName]
  // 因為使用了created的合併策略,所以是一個數組
  if (lifeCycles && lifeCycles.length) {
    // 遍歷 lifeCycleName對應的生命周期函數列表,依次執行
    lifeCycles.forEach(lifecycle => {
      lifecycle.call(vm)
    })
  }
  // 遍歷所有的子組件,然後依次遞歸執行
  if (vm.$children && vm.$children.length) {
    vm.$children.forEach(child => {
      notifyVisibilityChange(lifeCycleName, child)
    })
  }
}

// 添加生命周期函數
export function init() {
  const optionMergeStrategies = Vue.config.optionMergeStrategies
  // 定義了兩個生命周期函數 pageVisible, pageHidden
  // 為什麼要賦值為 optionMergeStrategies.created呢
  // 這個相當於指定 pageVisible, pageHidden 的合併策略與 created的相同(其他生命周期函數都一樣)
  optionMergeStrategies.pageVisible = optionMergeStrategies.beforeCreate
  optionMergeStrategies.pageHidden = optionMergeStrategies.created
}


// 將事件變化綁定到根節點上面
// rootVm vue根節點實例
export function bind(rootVm) {
  window.addEventListener('visibilitychange', () => {
    // 判斷調用哪個生命周期函數
    let lifeCycleName = undefined
    if (document.visibilityState === 'hidden') {
      lifeCycleName = 'pageHidden'
    } else if (document.visibilityState === 'visible') {
      lifeCycleName = 'pageVisible'
    }
    if (lifeCycleName) {
      // 通過所有組件生命周期發生變化了
      notifyVisibilityChange(lifeCycleName, rootVm)
    }
  })
}

應用

  1. main.js主入口文件引入
import { init, bind } from './utils/custom-life-cycle'

// 初始化生命周期函數, 必須在Vue實例化之前確定合併策略
init()

const vm = new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

// 將rootVm 綁定到生命周期函數監聽裏面
bind(vm)

  2. 在需要的地方監聽生命周期函數

export default {
  pageVisible() {
    console.log('頁面显示出來了')
  },
  pageHidden() {
    console.log('頁面隱藏了')
  }
}

  

provideinject,不止父子傳值,祖宗傳值也可以

Vue相關的面試經常會被面試官問道,Vue父子之間傳值的方式有哪些,通常我們會回答,props傳值,$emit事件傳值,vuex傳值,還有eventbus傳值等等,今天再加一種provideinject傳值,離offer又近了一步。(對了,下一節還有一種)

使用過React的同學都知道,在React中有一個上下文Context,組件可以通過Context向任意後代傳值,而Vueprovideinject的作用於Context的作用基本一樣

先舉一個例子

使用過elemment-ui的同學一定對下面的代碼感到熟悉

<template>
  <el-form :model="formData" size="small">
    <el-form-item label="姓名" prop="name">
      <el-input v-model="formData.name" />
    </el-form-item>
    <el-form-item label="年齡" prop="age">
      <el-input-number v-model="formData.age" />
    </el-form-item>
    <el-button>提交</el-button>
  </el-form>
</template>
<script>
export default {
  data() {
    return {
      formData: {
        name: '',
        age: 0
      }
    }
  }
}
</script>

  

看了上面的代碼,貌似沒啥特殊的,天天寫啊。在el-form上面我們指定了一個屬性size="small",然後有沒有發現表單裏面的所有表單元素以及按鈕的 size都變成了small,這個是怎麼做到的?接下來我們自己手寫一個表單模擬一下

自己手寫一個表單

自定義表單custom-form.vue

<template>
  <form class="custom-form">
    <slot></slot>
  </form>
</template>
<script>
export default {
  props: {
    // 控製表單元素的大小
    size: {
      type: String,
      default: 'default',
      // size 只能是下面的四個值
      validator(value) {
        return ['default', 'large', 'small', 'mini'].includes(value)
      }
    },
    // 控製表單元素的禁用狀態
    disabled: {
      type: Boolean,
      default: false
    }
  },
  // 通過provide將當前表單實例傳遞到所有後代組件中
  provide() {
    return {
      customForm: this
    }
  }
}
</script>

  

在上面代碼中,我們通過provide將當前組件的實例傳遞到後代組件中,provide是一個函數,函數返回的是一個對象

自定義表單項custom-form-item.vue

沒有什麼特殊的,只是加了一個label,element-ui更複雜一些

<template>
  <div class="custom-form-item">
    <label class="custom-form-item__label">{{ label }}</label>
    <div class="custom-form-item__content">
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    label: {
      type: String,
      default: ''
    }
  }
}
</script>

自定義輸入框 custom-input.vue

<template>
  <div
    class="custom-input"
    :class="[
      `custom-input--${getSize}`,
      getDisabled && `custom-input--disabled`
    ]"
  >
    <input class="custom-input__input" :value="value" @input="$_handleChange" />
  </div>
</template>
<script>
/* eslint-disable vue/require-default-prop */
export default {
  props: {
    // 這裏用了自定義v-model
    value: {
      type: String,
      default: ''
    },
    size: {
      type: String
    },
    disabled: {
      type: Boolean
    }
  },
  // 通過inject 將form組件注入的實例添加進來
  inject: ['customForm'],
  computed: {
    // 通過計算組件獲取組件的size, 如果當前組件傳入,則使用當前組件的,否則是否form組件的
    getSize() {
      return this.size || this.customForm.size
    },
    // 組件是否禁用
    getDisabled() {
      const { disabled } = this
      if (disabled !== undefined) {
        return disabled
      }
      return this.customForm.disabled
    }
  },
  methods: {
    // 自定義v-model
    $_handleChange(e) {
      this.$emit('input', e.target.value)
    }
  }
}
</script>

  


form中,我們通過
provide返回了一個對象,在
input中,我們可以通過
inject獲取
form中返回對象中的項,如上代碼
inject:['customForm']所示,然後就可以在組件內通過
this.customForm調用
form實例上面的屬性與方法了

在項目中使用

<template>
  <custom-form size="small">
    <custom-form-item label="姓名">
      <custom-input v-model="formData.name" />
    </custom-form-item>
  </custom-form>
</template>
<script>
import CustomForm from '../components/custom-form'
import CustomFormItem from '../components/custom-form-item'
import CustomInput from '../components/custom-input'
export default {
  components: {
    CustomForm,
    CustomFormItem,
    CustomInput
  },
  data() {
    return {
      formData: {
        name: '',
        age: 0
      }
    }
  }
}
</script>

  執行上面代碼,運行結果為:

<form class="custom-form">
  <div class="custom-form-item">
    <label class="custom-form-item__label">姓名</label>
    <div class="custom-form-item__content">
      <!--size=small已經添加到指定的位置了-->
      <div class="custom-input custom-input--small">
        <input class="custom-input__input">
      </div>
    </div>
  </div>
</form>

  

通過上面的代碼可以看到,input組件已經設置組件樣式為custom-input--small

inject格式說明

除了上面代碼中所使用的inject:['customForm']寫法之外,inject還可以是一個對象。且可以指定默認值

修改上例,如果custom-input外部沒有custom-form,則不會注入customForm,此時為customForm指定默認值

{
  inject: {
    customForm: {
      // 對於非原始值,和props一樣,需要提供一個工廠方法
      default: () => ({
        size: 'default'
      })
    }
  }
}

  

使用限制

1.provideinject的綁定不是可響應式的。但是,如果你傳入的是一個可監聽的對象,如上面的customForm: this,那麼其對象的屬性還是可響應的。

2.Vue官網建議provideinject 主要在開發高階插件/組件庫時使用。不推薦用於普通應用程序代碼中。因為provideinject在代碼中是不可追溯的(ctrl + f可以搜),建議可以使用Vuex代替。 但是,也不是說不能用,在局部功能有時候用了作用還是比較大的。

 

插槽,我要鑽到你的懷裡

插槽,相信每一位Vue都有使用過,但是如何更好的去理解插槽,如何去自定義插槽,今天小編為你帶來更形象的說明。

默認插槽

<template>
  <!--這是一個一居室-->
  <div class="one-bedroom">
    <!--添加一個默認插槽,用戶可以在外部隨意定義這個一居室的內容-->
    <slot></slot>
  </div>
</template>

  

<template>
  <!--這裏一居室-->
  <one-bedroom>
    <!--將傢具放到房間裏面,組件內部就是上面提供的默認插槽的空間-->
    <span>先放一個小床,反正沒有女朋友</span>
    <span>再放一個電腦桌,在家還要加班寫bug</span>
  </one-bedroom>
</template>
<script>
import OneBedroom from '../components/one-bedroom'
export default {
  components: {
    OneBedroom
  }
}
</script>

具名插槽

<template>
  <div class="two-bedroom">
    <!--這是主卧-->
    <div class="master-bedroom">
      <!---主卧使用默認插槽-->
      <slot></slot>
    </div>
    <!--這是次卧-->
    <div class="secondary-bedroom">
      <!--次卧使用具名插槽-->
      <slot name="secondard"></slot>
    </div>
  </div>
</template>

  

<template>
  <two-bedroom>
    <!--主卧使用默認插槽-->
    <div>
      <span>放一個大床,要結婚了,嘿嘿嘿</span>
      <span>放一個衣櫃,老婆的衣服太多了</span>
      <span>算了,還是放一個電腦桌吧,還要寫bug</span>
    </div>
    <!--次卧,通過v-slot:secondard 可以指定使用哪一個具名插槽, v-slot:secondard 也可以簡寫為 #secondard-->
    <template v-slot:secondard>
      <div>
        <span>父母要住,放一個硬一點的床,軟床對腰不好</span>
        <span>放一個衣櫃</span>
      </div>
    </template>
  </two-bedroom>
</template>
<script>
import TwoBedroom from '../components/slot/two-bedroom'
export default {
  components: {
    TwoBedroom
  }
}
</script>

作用域插槽

<template>
  <div class="two-bedroom">
    <!--其他內容省略-->
    <div class="toilet">
      <!--通過v-bind 可以向外傳遞參數, 告訴外面衛生間可以放洗衣機-->
      <slot name="toilet" v-bind="{ washer: true }"></slot>
    </div>
  </div>
</template>

  

<template>
  <two-bedroom>
    <!--其他省略-->
    <!--衛生間插槽,通過v-slot="scope"可以獲取組件內部通過v-bind傳的值-->
    <template v-slot:toilet="scope">
      <!--判斷是否可以放洗衣機-->
      <span v-if="scope.washer">這裏放洗衣機</span>
    </template>
  </two-bedroom>
</template>  

插槽默認值

<template>
  <div class="second-hand-house">
    <div class="master-bedroom">
      <!--插槽可以指定默認值,如果外部調用組件時沒有修改插槽內容,則使用默認插槽-->
      <slot>
        <span>這裡有一張水床,玩的夠嗨</span>
        <span>還有一個衣櫃,有點舊了</span>
      </slot>
    </div>
    <!--這是次卧-->
    <div class="secondary-bedroom">
      <!--次卧使用具名插槽-->
      <slot name="secondard">
        <span>這裡有一張嬰兒床</span>
      </slot>
    </div>
  </div>
</template>

  

<second-hand-house>
    <!--主卧使用默認插槽,只裝修主卧-->
    <div>
      <span>放一個大床,要結婚了,嘿嘿嘿</span>
      <span>放一個衣櫃,老婆的衣服太多了</span>
      <span>算了,還是放一個電腦桌吧,還要寫bug</span>
    </div>
  </second-hand-house>

dispatchbroadcast,這是一種有歷史的組件通信方式

dispatch
broadcast是一種有歷史的組件通信方式,為什麼是有歷史的,因為他們是
Vue1.0提供的一種方式,在
Vue2.0中廢棄了。但是廢棄了不代表我們不能自己手動實現,像許多UI庫內部都有實現。本文以
element-ui實現為基礎進行介紹。同時看完本節,你會對組件的
$parent,
$children,
$options有所了解。

方法介紹

$dispatch: $dispatch會向上觸發一個事件,同時傳遞要觸發的祖先組件的名稱與參數,當事件向上傳遞到對應的組件上時會觸發組件上的事件偵聽器,同時傳播會停止。

$broadcast: $broadcast會向所有的後代組件傳播一個事件,同時傳遞要觸發的後代組件的名稱與參數,當事件傳遞到對應的後代組件時,會觸發組件上的事件偵聽器,同時傳播會停止(因為向下傳遞是樹形的,所以只會停止其中一個恭弘=叶 恭弘子分支的傳遞)。

$dispatch實現與應用

1. 代碼實現

 // 向上傳播事件
 // @param {*} eventName 事件名稱
 // @param {*} componentName 接收事件的組件名稱
 // @param {...any} params 傳遞的參數,可以有多個
 
function dispatch(eventName, componentName, ...params) {
  // 如果沒有$parent, 則取$root
  let parent = this.$parent || this.$root
  while (parent) {
    // 組件的name存儲在組件的$options.componentName 上面
    const name = parent.$options.name
    // 如果接收事件的組件是當前組件
    if (name === componentName) {
      // 通過當前組件上面的$emit觸發事件,同事傳遞事件名稱與參數
      parent.$emit.apply(parent, [eventName, ...params])
      break
    } else {
      // 否則繼續向上判斷
      parent = parent.$parent
    }
  }
}

// 導出一個對象,然後在需要用到的地方通過混入添加
export default {
  methods: {
    $dispatch: dispatch
  }
}  

2. 代碼應用

  • 在子組件中通過$dispatch向上觸發事件

    import emitter from '../mixins/emitter'
    export default {
      name: 'Chart',
      // 通過混入將$dispatch加入進來
      mixins: [emitter],
       mounted() {
         // 在組件渲染完之後,將組件通過$dispatch將自己註冊到Board組件上
        this.$dispatch('register', 'Board', this)
      }
    }
  • Board組件上通過$on監聽要註冊的事件

$broadcast實現與應用

1. 代碼實現

  //向下傳播事件
  // @param {*} eventName 事件名稱
  // @param {*} componentName 要觸發組件的名稱
  // @param  {...any} params 傳遞的參數
 
function broadcast(eventName, componentName, ...params) {
  this.$children.forEach(child => {
    const name = child.$options.name
    if (name === componentName) {
      child.$emit.apply(child, [eventName, ...params])
    } else {
      broadcast.apply(child, [eventName, componentName, ...params])
    }
  })
}

// 導出一個對象,然後在需要用到的地方通過混入添加
export default {
  methods: {
    $broadcast: broadcast
  }
}  

2. 代碼應用

在父組件中通過$broadcast向下觸發事件

import emitter from '../mixins/emitter'
export default {
  name: 'Board',
  // 通過混入將$dispatch加入進來
  mixins: [emitter],
  methods:{
  	//在需要的時候,刷新組件
  	$_refreshChildren(params) {
  		this.$broadcast('refresh', 'Chart', params)
  	}
  }
}

在後代組件中通過$on監聽刷新事件

export default {
  name: 'Chart',
  created() {
    this.$on('refresh',(params) => {
      // 刷新事件
    })
  }
}

總結

通過上面的例子,同學們應該都能對$dispatch$broadcast有所了解,但是為什麼Vue2.0要放棄這兩個方法呢?官方給出的解釋是:”因為基於組件樹結構的事件流方式實在是讓人難以理解,並且在組件結構擴展的過程中會變得越來越脆弱。這種事件方式確實不太好,我們也不希望在以後讓開發者們太痛苦。並且 $dispatch$broadcast 也沒有解決兄弟組件間的通信問題。“

確實如官網所說,這種事件流的方式確實不容易讓人理解,而且後期維護成本比較高。但是在小編看來,不管黑貓白貓,能抓老鼠的都是好貓,在許多特定的業務場景中,因為業務的複雜性,很有可能使用到這樣的通信方式。但是使用歸使用,但是不能濫用,小編一直就在項目中有使用。

 

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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