標籤: 如何寫文案

  • 重學 Java 設計模式:實戰裝飾器模式(SSO單點登錄功能擴展,增加攔截用戶訪問方法範圍場景)

    重學 Java 設計模式:實戰裝飾器模式(SSO單點登錄功能擴展,增加攔截用戶訪問方法範圍場景)

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

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

    一、前言

    對於代碼你有編程感覺嗎

    很多人寫代碼往往是沒有編程感覺的,也就是除了可以把功能按照固定的流程編寫出流水式的代碼外,很難去思考整套功能服務的擴展性和可維護性。尤其是在一些較大型的功能搭建上,比較缺失一些駕馭能力,從而導致最終的代碼相對來說不能做到盡善盡美。

    江洋大盜與江洋大偷

    兩個本想描述一樣的意思的詞,只因一字只差就讓人覺得一個是好牛,一個好搞笑。往往我們去開發編程寫代碼時也經常將一些不恰當的用法用於業務需求實現中,當卻不能意識到。一方面是由於編碼不多缺少較大型項目的實踐,另一方面是不思進取的總在以完成需求為目標缺少精益求精的工匠精神。

    書從來不是看的而是用的

    在這個學習資料幾乎爆炸的時代,甚至你可以輕易就獲取幾個T的視頻,小手輕輕一點就收藏一堆文章,但卻很少去看。學習的過程從不只是簡單的看一遍就可以,對於一些實操性的技術書籍,如果真的希望學習到知識,那麼一定是把這本書用起來而絕對不是看起來。

    二、開發環境

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

    三、裝飾器模式介紹

    初看上圖感覺裝飾器模式有點像俄羅斯套娃、某眾汽車,而裝飾器的核心就是再不改原有類的基礎上給類新增功能。不改變原有類,可能有的小夥伴會想到繼承、AOP切面,當然這些方式都可以實現,但是使用裝飾器模式會是另外一種思路更為靈活,可以避免繼承導致的子類過多,也可以避免AOP帶來的複雜性。

    你熟悉的場景很多用到裝飾器模式

    new BufferedReader(new FileReader(""));,這段代碼你是否熟悉,相信學習java開發到字節流、字符流、文件流的內容時都見到了這樣的代碼,一層嵌套一層,一層嵌套一層,字節流轉字符流等等,而這樣方式的使用就是裝飾器模式的一種體現。

    四、案例場景模擬

    在本案例中我們模擬一個單點登錄功能擴充的場景

    一般在業務開發的初期,往往內部的ERP使用只需要判斷賬戶驗證即可,驗證通過後即可訪問ERP的所有資源。但隨着業務的不斷髮展,團隊里開始出現專門的運營人員、營銷人員、數據人員,每個人員對於ERP的使用需求不同,有些需要創建活動,有些只是查看數據。同時為了保證數據的安全性,不會讓每個用戶都有最高的權限。

    那麼以往使用的SSO是一個組件化通用的服務,不能在裏面添加需要的用戶訪問驗證功能。這個時候我們就可以使用裝飾器模式,擴充原有的單點登錄服務。但同時也保證原有功能不受破壞,可以繼續使用。

    1. 場景模擬工程

    itstack-demo-design-9-00
    └── src
        └── main
            └── java
                └── org.itstack.demo.design
                    ├── HandlerInterceptor.java
                    └── SsoInterceptor.java
    
    • 這裏模擬的是spring中的類:HandlerInterceptor,實現起接口功能SsoInterceptor模擬的單點登錄攔截服務。
    • 為了避免引入太多spring的內容影響對設計模式的閱讀,這裏使用了同名的類和方法,盡可能減少外部的依賴。

    2. 場景簡述

    2.1 模擬Spring的HandlerInterceptor

    public interface HandlerInterceptor {
    
        boolean preHandle(String request, String response, Object handler);
    
    }
    
    • 實際的單點登錄開發會基於;org.springframework.web.servlet.HandlerInterceptor 實現。

    2.2 模擬單點登錄功能

    public class SsoInterceptor implements HandlerInterceptor{
    
        public boolean preHandle(String request, String response, Object handler) {
            // 模擬獲取cookie
            String ticket = request.substring(1, 8);
            // 模擬校驗
            return ticket.equals("success");
        }
    
    }
    
    • 這裏的模擬實現非常簡單隻是截取字符串,實際使用需要從HttpServletRequest request對象中獲取cookie信息,解析ticket值做校驗。
    • 在返回的裏面也非常簡單,只要獲取到了success就認為是允許登錄。

    五、用一坨坨代碼實現

    此場景大多數實現的方式都會採用繼承類

    繼承類的實現方式也是一個比較通用的方式,通過繼承后重寫方法,併發將自己的邏輯覆蓋進去。如果是一些簡單的場景且不需要不斷維護和擴展的,此類實現並不會有什麼,也不會導致子類過多。

    1. 工程結構

    itstack-demo-design-9-01
    └── src
        └── main
            └── java
                └── org.itstack.demo.design
                    └── LoginSsoDecorator.java
    
    • 以上工程結構非常簡單,只是通過 LoginSsoDecorator 繼承 SsoInterceptor,重寫方法功能。

    2. 代碼實現

    public class LoginSsoDecorator extends SsoInterceptor {
    
        private static Map<String, String> authMap = new ConcurrentHashMap<String, String>();
    
        static {
            authMap.put("huahua", "queryUserInfo");
            authMap.put("doudou", "queryUserInfo");
        }
    
        @Override
        public boolean preHandle(String request, String response, Object handler) {
            // 模擬獲取cookie
            String ticket = request.substring(1, 8);
            // 模擬校驗
            boolean success = ticket.equals("success");
    
            if (!success) return false;
    
            String userId = request.substring(9);
            String method = authMap.get(userId);
    
            // 模擬方法校驗
            return "queryUserInfo".equals(method);
        }
    
    }
    
    • 以上這部分通過繼承重寫方法,將個人可訪問哪些方法的功能添加到方法中。
    • 以上看着代碼還算比較清晰,但如果是比較複雜的業務流程代碼,就會很混亂。

    3. 測試驗證

    3.1 編寫測試類

    @Test
    public void test_LoginSsoDecorator() {
        LoginSsoDecorator ssoDecorator = new LoginSsoDecorator();
        String request = "1successhuahua";
        boolean success = ssoDecorator.preHandle(request, "ewcdqwt40liuiu", "t");
        System.out.println("登錄校驗:" + request + (success ? " 放行" : " 攔截"));
    }
    
    • 這裏模擬的相當於登錄過程中的校驗操作,判斷用戶是否可登錄以及是否可訪問方法。

    3.2 測試結果

    登錄校驗:1successhuahua 攔截
    
    Process finished with exit code 0
    
    • 從測試結果來看滿足我們的預期,已經做了攔截。如果你在學習的過程中,可以嘗試模擬單點登錄並繼承擴展功能。

    六、裝飾器模式重構代碼

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

    裝飾器主要解決的是直接繼承下因功能的不斷橫向擴展導致子類膨脹的問題,而是用裝飾器模式后就會比直接繼承顯得更加靈活同時這樣也就不再需要考慮子類的維護。

    在裝飾器模式中有四個比較重要點抽象出來的點;

    1. 抽象構件角色(Component) – 定義抽象接口
    2. 具體構件角色(ConcreteComponent) – 實現抽象接口,可以是一組
    3. 裝飾角色(Decorator) – 定義抽象類並繼承接口中的方法,保證一致性
    4. 具體裝飾角色(ConcreteDecorator) – 擴展裝飾具體的實現邏輯

    通過以上這四項來實現裝飾器模式,主要核心內容會體現在抽象類的定義和實現上。

    1. 工程結構

    itstack-demo-design-9-02
    └── src
        └── main
            └── java
                └── org.itstack.demo.design
                    ├── LoginSsoDecorator.java
                    └── SsoDecorator.java
    

    裝飾器模式模型結構

    • 以上是一個裝飾器實現的類圖結構,重點的類是SsoDecorator,這個類是一個抽象類主要完成了對接口HandlerInterceptor繼承。
    • 當裝飾角色繼承接口後會提供構造函數,入參就是繼承的接口實現類即可,這樣就可以很方便的擴展出不同功能組件。

    2. 代碼實現

    2.1 抽象類裝飾角色

    public abstract class SsoDecorator implements HandlerInterceptor {
    
        private HandlerInterceptor handlerInterceptor;
    
        private SsoDecorator(){}
    
        public SsoDecorator(HandlerInterceptor handlerInterceptor) {
            this.handlerInterceptor = handlerInterceptor;
        }
    
        public boolean preHandle(String request, String response, Object handler) {
            return handlerInterceptor.preHandle(request, response, handler);
        }
    
    }
    
    • 在裝飾類中有兩個重點的地方是;1)繼承了處理接口、2)提供了構造函數、3)覆蓋了方法preHandle
    • 以上三個點是裝飾器模式的核心處理部分,這樣可以踢掉對子類繼承的方式實現邏輯功能擴展。

    2.2 裝飾角色邏輯實現

    public class LoginSsoDecorator extends SsoDecorator {
    
        private Logger logger = LoggerFactory.getLogger(LoginSsoDecorator.class);
    
        private static Map<String, String> authMap = new ConcurrentHashMap<String, String>();
    
        static {
            authMap.put("huahua", "queryUserInfo");
            authMap.put("doudou", "queryUserInfo");
        }
    
        public LoginSsoDecorator(HandlerInterceptor handlerInterceptor) {
            super(handlerInterceptor);
        }
    
        @Override
        public boolean preHandle(String request, String response, Object handler) {
            boolean success = super.preHandle(request, response, handler);
            if (!success) return false;
            String userId = request.substring(8);
            String method = authMap.get(userId);
            logger.info("模擬單點登錄方法訪問攔截校驗:{} {}", userId, method);
            // 模擬方法校驗
            return "queryUserInfo".equals(method);
        }
    }
    
    • 在具體的裝飾類實現中,繼承了裝飾類SsoDecorator,那麼現在就可以擴展方法;preHandle
    • preHandle的實現中可以看到,這裏只關心擴展部分的功能,同時不會影響原有類的核心服務,也不會因為使用繼承方式而導致的多餘子類,增加了整體的靈活性。

    3. 測試驗證

    3.1 編寫測試類

    @Test
    public void test_LoginSsoDecorator() {
        LoginSsoDecorator ssoDecorator = new LoginSsoDecorator(new SsoInterceptor());
        String request = "1successhuahua";
        boolean success = ssoDecorator.preHandle(request, "ewcdqwt40liuiu", "t");
        System.out.println("登錄校驗:" + request + (success ? " 放行" : " 攔截"));
    }
    
    • 這裏測試了對裝飾器模式的使用,通過透傳原有單點登錄類new SsoInterceptor(),傳遞給裝飾器,讓裝飾器可以執行擴充的功能。
    • 同時對於傳遞者和裝飾器都可以是多組的,在一些實際的業務開發中,往往也是由於太多類型的子類實現而導致不易於維護,從而使用裝飾器模式替代。

    3.2 測試結果

    23:50:50.796 [main] INFO  o.i.demo.design.LoginSsoDecorator - 模擬單點登錄方法訪問攔截校驗:huahua queryUserInfo
    登錄校驗:1successhuahua 放行
    
    Process finished with exit code 0
    
    • 結果符合預期,擴展了對方法攔截的校驗性。
    • 如果你在學習的過程中有用到過單點登陸,那麼可以適當在裏面進行擴展裝飾器模式進行學習使用。
    • 另外,還有一種場景也可以使用裝飾器。例如;你之前使用某個實現某個接口接收單個消息,但由於外部的升級變為發送list集合消息,但你又不希望所有的代碼類都去修改這部分邏輯。那麼可以使用裝飾器模式進行適配list集合,給使用者依然是for循環后的單個消息。

    七、總結

    • 使用裝飾器模式滿足單一職責原則,你可以在自己的裝飾類中完成功能邏輯的擴展,而不影響主類,同時可以按需在運行時添加和刪除這部分邏輯。另外裝飾器模式與繼承父類重寫方法,在某些時候需要按需選擇,並不一定某一個就是最好。
    • 裝飾器實現的重點是對抽象類繼承接口方式的使用,同時設定被繼承的接口可以通過構造函數傳遞其實現類,由此增加擴展性並重寫方法里可以實現此部分父類實現的功能。
    • 就像夏天熱你穿短褲,冬天冷你穿棉褲,雨天挨澆你穿雨衣一樣,你的根本本身沒有被改變,而你的需求卻被不同的裝飾而實現。生活中往往比比皆是設計,當你可以融合這部分活靈活現的例子到代碼實現中,往往會創造出更加優雅的實現方式。

    八、推薦閱讀

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

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

    ※回頭車貨運收費標準

  • 給女朋友講解什麼是Git

    給女朋友講解什麼是Git

    前言

    在周六發現了Linus去Google演講的一個視頻,當時還發了一條朋友圈:

    有興趣的同學也可以去看看,一點兒也不無聊,在線看Linus大佬懟人

    https://www.bilibili.com/video/BV1xb411A7ac?from=search&seid=4239535088233137638

    朋友圈的評論有幾個人問我女朋友

    我又恰好給我女朋友科普過什麼是Git,所以這篇文章就有了。

    Git介紹

    Git是幹啥用的?它是一個版本控制軟件。

    Git這個玩意三歪曾經還給女朋友給科普過(三歪會經常給女朋友說點技術的東西,我也不知道她到底聽懂了沒有,反正她每次都說好像有點懂了)。

    當時情況是這樣的,某一天她跟三歪說:我做的Excel還沒保存,電腦藍屏死機了,東西全丟了。

    於是三歪說:這…我也沒怎麼用過Excel這類的軟件,要不看看你用的WPS或者Office?有沒有相關的備份功能或者說是自動保存?

    三歪順手找了一下WPS是有備份(自動保存)功能的,時間什麼的要自己定義一下。

    三歪還補了一句:這種情況要是在程序員的手上感覺發生的概率會低一點,程序員習慣會按ctrl+s。甚至有的時候,看着看着網頁還會按ctrl+s。不過像我們寫代碼的工具(IDEA)都不用自己手動保存了….

    過了一會,她說藍屏之前做的東西找不回來了,沒設置自動保存。

    三歪又感嘆一句:我們寫代碼還有版本控制的軟件,在這個過程中會記錄每次修改的內容,誰改了什麼東西。誰改錯了,誰要背鍋,一個都不能跑

    女朋友聽着三歪一頓亂吹,貌似也有點感興趣:“版本控制是什麼東西?這麼厲害的嗎?會計就經常要背鍋”

    三歪:“其實也沒啥,就是我們一般寫代碼往往都是多人協作的。你們會計可能是每個人負責一張表(Excel),然後把已完成好的表傳給下一個人。而寫代碼的不一樣,我們都是在同一個項目裡邊編寫的,不會單獨等着某個人做完了,其他的人再開始動手”

    女朋友:“嗯?然後呢”

    三歪:“你可以理解成,我們多個人會在同一個目錄下編寫代碼,裡邊可能會做更改或者添加文件的操作。項目組裡的所有人都可以對這個目錄修改,改完了我們會提交,然後發布上線系統。”

    女朋友:“啥?你是不是偏題了?這跟發布上線系統有啥關係?”

    三歪:“哦,我還是舉個例子吧。就比如我們寫論文的時候可能要對論文不斷修改,我們的修改是基於原有的基礎上改的”

    三歪繼續補充:”因為我們怕在原來的基礎改錯了東西,沒法恢復,所以,我們可能會有多個「畢業論文」的文件。而我們寫代碼的時候本身就是「多人協作」的,修改是無法避免的,我們不希望有多個文件的產生,又希望能夠記錄每次更改的內容。“

    三歪:”更改的內容指的就是:基於原有的基礎上更改了什麼,以及提交者是誰。這樣子,我們就沒法甩鍋了。說白了就是,我們能知道的文件被改了什麼,以及誰改了“。

    三歪:“到這裏,有問題嗎?”

    女朋友:“嗯,沒問題,你繼續”

    三歪:“「每一次的修改」我們稱為一個版本,它能夠實現版本與版本之間的來回穿梭。打個比方,我有篇文章寫了一周,這期間有10個版本,我能隨意回到我想要的版本。所以它叫做版本控制軟件”

    女朋友:“我大致聽懂了,大概就是每一次修改都會被記錄下來,然後你們就可以知道每一次版本修改了什麼,是誰改的,如果做錯了,可以通過這個軟件回到想要的版本”

    三歪:“嗯,就是這個意思”

    女朋友:“那我想問個問題,你一直提到的「多人協作」是在同一個目錄下對文件修改的,然後可以看到彼此改了什麼。那你是在你的電腦上改,你的同事是在他的電腦上改的,你們是怎麼看到彼此改了什麼?這現在有這麼厲害的東西了嗎?“

    三歪:”哦~你的意思大概就是:我們又不聯網,怎麼知道對方改了什麼,是這個意思吧?“

    女朋友:“嗯,是的”

    三歪:“你的理解是沒錯的,我們之間不聯網,是沒辦法知道對方改了什麼的。我漏了一點沒說,我們在改到一定程度下(比如說這個功能我們做完了、也可能做得差不多了),我們會把當前版本提交到遠程倉庫上”

    三歪繼續補充:“可以發現的是,提交到遠程倉庫后,即便我們電腦壞了,我們可以從遠程倉庫再把這份數據拉取下來。”

    女朋友:“所以呢?遠程倉庫到我這聽起來就是一個備份的功能吧?你們怎麼知道對方改了什麼?”

    三歪:“是這樣的,我們從遠程倉庫拉取代碼的時候除了會把有變動的代碼同步到自己的電腦上,還會把所有修改的記錄也同步到自己的電腦上。所以說,我們會知道彼此修改的內容。”

    女朋友:”聽着很有用啊,我平時用的word和excel可以用這個軟件嗎?即便我電腦壞了,我還可以去『遠程倉庫』拿到上一次我提交的數據,並且還有你所說的『版本控制』功能。又能備份,又能知道每次修改了什麼,很好用啊!“

    三歪:”很可惜,像你們那種工作場景,可能用不上,也可以說不太適合用“

    女朋友:”為什麼?“

    三歪:”之前我也想要用定時任務+GitHub的方式去保存我在本地寫的文章,發出來之後,被一頓噴。GitHub你可以簡單理解為就是那個遠程倉庫,定時任務我就是讓它隔一段時間就保存一次“

    女朋友:”我聽明白了,你想要的是備份功能,對吧?為什麼被噴的呢?“

    三歪:”他們說我瞎整,這每隔一段時間就提交到GitHub,網絡開銷可多大啊。然後給我列出一系列的產品,比如說:「堅果雲」「Dropbox」「OneDrive」等等“

    女朋友:”那你怎麼不用?“

    三歪:”我哪知道啊,有信息差的呢。反正當時覺得自己寫個定時任務實現了,就沒多想了。“

    女朋友:”那你是真的菜“

    三歪:”哦“

    三歪:”除了上面說的自動同步,你們的word、excel在用我們的工具也沒法查到歷史的版本記錄“

    女朋友:”為啥?你不是說你們寫代碼都可以的嗎?為什麼word和excel就沒法查?“

    三歪:”我們寫代碼的文件類型都是屬於文本文件,而你的word、excel本質上屬於二進制文件,很難去比對每次修改的差異,所以不支持“

    女朋友:”那聽你這樣說,在我的場景里這個「版本控制軟件」沒啥用啊,它不能記錄像Word、Excel這種文件每次版本的差異,要想用它做備份,還不如現有的雲產品。“

    三歪:”嗯,是的。其實現在雲產品也能提供版本控制的功能了,你用它們就足夠了。還有一點很重要的是,它有學習成本,可不是每個人都會用的。“

    女朋友:”聽你說了這麼多,好像雲產品就很行啊,為啥你們還要「偏執」去用你說的那東西?“

    三歪:“主要是我們寫代碼時遇到的問題會更多,我們用的「版本控制軟件」會更加靈活,它支持的功能會更多。”

    女朋友:“對了,你都說了這麼久了,你們用的那個軟件叫啥啊?”

    三歪:“Git

    三歪瞎扯

    其實本來想着直接講一下在工作中常用到的Git命令,但發現不太好寫(琢磨了很久

    看三歪文章的人可能很多都是大學生,對Git本身了解可能就不太清楚,直接上Git的命令可能看不太懂,所以就分開了兩篇。

    下一篇講一下三歪在工作中使用Git的糗事以及工作中是怎麼用Git的。

    涵蓋Java後端所有知識點的開源項目(已有8K+ star):

    • GitHub
    • Gitee訪問更快

    我是三歪,一個想要變強的男人,感謝大家的點贊收藏和轉發,下期見。

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • LeetCode 76,一題教會你面試算法時的思考套路

    LeetCode 76,一題教會你面試算法時的思考套路

    本文始發於個人公眾號:TechFlow,原創不易,求個關注

    今天是LeetCode專題的第45篇文章,我們一起來看看LeetCode的76題,最小窗口子串Minimum Window Substring。

    這題的官方難度是Hard,通過了也是34.2%,4202人點贊,299人反對。從通過率以及點贊比來看,這題的質量很高,稍稍有些偏難,所以小夥伴們請做好準備,這是一道有點挑戰的問題。

    題意和樣例

    我們一起來看下題意,這題的題意很短,給定兩個字符串S和T。要求設計一個複雜度為的算法,在S串當中找到一個子串,能夠包含T串當中的所有字符。要求返回合法且長度最小的窗口的內容。

    注意:

    • 如果不存在這樣的窗口,返回“”。
    • 如果窗口存在,題目保證有且只有一個。

    樣例:

    Input: S = "ADOBECODEBANC", T = "ABC"
    Output: "BANC"
    

    分析

    我們來分析一下這個問題,從題意當中大家應該都能感受到它的難度。因為上來題目當中就限定了我們使用的算法的複雜度必須是,然而我們遍歷字符串的複雜度就已經是了,也就是說我們不能引入額外的計算開銷,否則一定不滿足題目的要求。

    可能有些同學會想到傳說中在時間內判斷字符串匹配的KMP算法,如果你不知道這個算法也沒有關係,因為這個算法並不適用。因為我們要找的不是完全相等的子串的位置,而是找的是字符構成一樣的子串,所以並不能通過引入字符串匹配算法來解決。沒有學過KMP算法的同學可以松一口氣了,這題當中並不會引入新的算法。

    解題的套路

    一般來說當我們面臨一個算法問題的時候,我們常常的思考過程主要有兩種。一種是適配,說白了就是把可能可以用上的算法往問題上套。根據題意先感覺一下,大概會用到什麼樣的算法,然後詳細地推導適配的過程,看看是不是真的適用或者是有什麼坑,或者是會出現什麼新的問題。如果一切OK,能夠推理得通,那麼這個算法就是解。第二種方法是建模,也就是說從題意入手,對題意進行深入的分析,對問題進行建模和抽象,找到問題的核心,從而推導出用什麼樣的算法可以解決。

    舉個很簡單的例子,一般來說我們的動態規劃算法都是適配。都是我們先感覺或者是猜測出可以使用動態規劃,然後再去找狀態和轉移,最後建立狀態轉移方程。而一些搜索問題一般是建模,我們先對問題進行分析,然後找出需要搜索的解的存在空間,然後設計算法去搜索和剪枝,最後找到答案。

    據說一些頂級高手這兩種方法是一起使用的,所以才可以那麼快速地找到解。當然我不是頂級高手,所以這個也只是我的猜測。這個思考過程非常有用,特別是當我們面試的時候,遇到一個從未見過的問題,如果你什麼套路也沒有,頭腦一片空白或者是苦思冥想不得要領是很常見的事情。當你有了套路之後,你就可以試着慢慢找到答案了。

    回到這道題本身,我們剛才已經試過了,拿字符串匹配的算法網上套是不行的。在視野里似乎也沒有其他的算法可以套用,所以我們換一種思路,試試看建模。

    首先我們可以肯定一點,我們需要在遍歷的時候找到答案,這樣才可以保證算法的複雜度是。我們的目標是尋找子串,也就是說我們遍歷的過程應該對應一個子串,並且我們有方法可以快速判斷這個子串是否合法。這樣我們才可以做到遍歷的同時判斷答案的可行性。進而可以想到這是一個區間維護的問題,區間維護我們經常使用的方法就是two pointers。所以我們可以試試two pointers能否適用。

    實際上這道題的正解就是two pointers。

    題解

    我們維護了一個區間,我們需要判斷區間里的字符構成,這個很容易想到可以使用dict,維護每一個字符出現的次數。在這個題目當中,我們只需要考慮覆蓋的情況,也就是說字符多了並不會構成非法。所以我們可以維護一個dict,每次讀入一個字符更新它,當dict當中的字符滿足要求的時候,為了使得區間長度盡量短,我們可以試着移動區間的左側,盡量縮短區間的長度。

    從區間維護的角度來說,我們每次移動區間右側一個單位,只有當區間內已經滿足題意的時候才會移動左側。通過移動左側彈出元素來獲取能夠滿足題意的最佳區間。

    我們來看下主要的流程代碼:

    # 存儲區間內的字符
    segement = {}
    for i in range(n):
        segement[s[i]] += 1
        # 當滿足條件的時候移動區間左側
        while l <= i and satisified(segment):
            # 更新最佳答案
            if i - l + 1 < ans_len:
                ans_len = i - l + 1
                beg, end = l, i + 1
            # 彈出元素
      segement[s[l]] -= 1
            l += 1
    

    到這裏還有一個小問題,就是怎麼樣判斷這個segment是否合法呢?我們可以用一個数字matched來記錄目前已經匹配上的字符的數量。當某個字符在segment當中出現的次數和T中的次數相等的時候,matched加一。當matched的數量和T中字符種類的數量相等的時候,就可以認為已經合法了。

    我們把所有的邏輯串起來,就可以通過這題了。

    class Solution:
        def minWindow(self, s: str, t: str) -> str:
            from collections import Counter, defaultdict
            # 通過Counter直接獲取T當中的字符構成
            counter = Counter(t)
            n, m = len(s), len(counter)
            l, beg, end = 0, 0, 0
            cur = defaultdict(int)
            matched = 0
            flag = False
            # 記錄合法的字符串的長度
            ans_len = 0x3f3f3f3f
            
            for i in range(n):
                if s[i] not in counter:
                    continue
                    
                cur[s[i]] += 1
                # 當數量匹配上的時候,matched+1
                if cur[s[i]] == counter[s[i]]:
                    matched += 1
                    
                # 如果已經找到了合法的區間,嘗試縮短區間的長度
                while l <= i and matched == m:
                    if i - l + 1 < ans_len:
                        flag = True
                        beg, end = l, i+1
                        ans_len = i - l + 1
                        
                    # 彈出左側元素
                    c = s[l]
                    if c in counter:
                        cur[c] -= 1
                        if cur[c] < counter[c]:
                            matched -= 1
                            
                    l += 1
    
            
            return "" if not flag else s[beg: end]
    

    總結

    到這裏,這道題就算是解決了。很多同學可能會覺得疑惑,為什麼我們用到了兩重循環,但是它依然還是的算法呢?

    這個是two pointers算法的常見問題,也是老生常談的話題了。我們在分析複雜度的時候,不能只簡單地看用到了幾層循環,而是要抓住計算的核心。比如在這個問題當中,我們內部的while循環針對的變量是l,l這個變量對於i整體是遞增的。也就是說無論外面這個循環執行多少次,裏面的這個while循環一共最多累加只能執行n次。那麼,當然這是一個的算法。

    這題總體來說有些難度,特別是一開始的時候可能會覺得沒有頭緒無從下手。這個時候有一個清晰的頭腦以及靠譜的思考鏈非常重要,希望大家都能學到這個其中思維的過程,這樣以後才可以應付更多的算法問題。

    如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

    本文使用 mdnice 排版

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • 昆蟲間諜要出現了?美研發出可由甲蟲搭載的微型攝影機

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

    用昆蟲機器人監控敵方行動不再是電影畫面了!美國華盛頓大學研發出微型攝影機,搭載在甲蟲上可將其拍攝的畫面傳送到智慧型手機裡,相關研究已刊載在Science Robotics期刊上。

    據《BBC》報導,這款微型攝影機整個裝置的重量只有250毫克,是紙牌重量的10%左右。它安裝在可以左右移動的機械臂上,因此可以掃描環境以獲得全景圖像。為了節省電量,研究人員將攝影機改裝成只有在甲蟲移動時才會啟動,如此一來充滿電後可運作六個小時。實驗結束後甲蟲沒有受到傷害,至少再活了一年。

    研究團隊透過這次的實驗,製作了如同昆蟲大小的攝影機機器人,其透過振動的方式進行移動,每秒約可前進3公分。研發團隊也坦承微型攝影機機器人可能會引發新的監控問題,不過他們認為更重要的是把這件事放在公共領域進行討論,以便讓人們意識到其中的風險並得到解決方案。

    生物多樣性
    國際新聞
    美國
    動物福利
    經濟動物

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

    【其他文章推薦】

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

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

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

  • 飛機停飛郵輪不航行 氣象專家憂心:難發現「颱風熱點」

    摘錄自2020年7月18、19日鏡週刊、自由時報報導

    歐洲中程天氣預報中心表示,未來若所有航班都消失,預報準確率將會降低多達15%。為了準確預測天氣變化,氣象中心仰賴各種監測工具蒐集到的資訊來演算和預測,包括飛機、郵輪、衛星、浮標、氣象氣球、地面站和雷達。然而,近來受到疫情影響,從飛機和郵輪獲得的數據銳減,水面上的觀測也受到限制。

    CNN 報導,蘭卡斯特大學生態中心(Lancaster University’s Environment Centre)研究發現,疫情之下各地航班密度降低,使得今年3月到5月的地面天氣預報準確性下降。接下來颱風季即將來臨,蘭卡斯特大學生態中心的研究員陳穎(Dr.Ying Chen)表示,若無法精準掌握氣溫,就無法即時發現颱風熱點。

    陳穎也提到,在疫情之下不同地區所面臨到的天氣預報準確率降幅也大有不同,像是一些難以用既有設施觀測的地區,如格陵蘭和西伯利亞地區等,在航班減少的情況下,將會更加難以準確地進行天氣預測。

    氣候變遷
    國際新聞
    美國
    氣象預測
    天氣監測
    疫情下的食衣住行
    颱風
    氣象

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

    【其他文章推薦】

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

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

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

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

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

  • 比衣服纖維掉更多 研究:輪胎塑膠微粒隨風入海、傳送全球

    環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • 馬來西亞登嘉樓立法緊急保護海龜蛋

    摘錄自2020年7月19日法廣報導

    在馬來西亞,臨海的登嘉樓州(Terengganu)頒布了一項新法律,禁止販賣海龜蛋。當地出名的原因之一,也是因為這裡可以品嘗得到瀕臨滅絕的珍稀動物海龜的蛋。儘管現在可以看到思維開始轉變,但動物保護工作依然複雜。

    就在海龜保護機構九年來致力於搶救小海龜的同時,20公里外的Chukai市場上,海龜蛋被混在水果中,一起銷售。當地一名女商販指出,儘管這一做法沒有得到大家一致認同,但這種生意依然火紅。這名商販表示,海龜蛋的氣味在近距離真的很難聞,但它有益於防範AVC腦血管意外(中風),有人就是因此而購買,價格為2歐元三個。

    登嘉樓州的新法對保護瀕臨滅絕物種是一個進步,但當局尚未公布對違法分子如何量刑。

    生物多樣性
    國際新聞
    馬來西亞
    海龜蛋

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • 荷研發植物製塑膠瓶 一年內可生物分解

    摘錄自2020年7月21日公視報導

    荷蘭一間生化公司開發出以植物原料製造的塑膠瓶,使用完不僅可以回收,它還能在一年內自行分解。

    荷蘭生化公司技術總監古魯特表示:「而且因為從隔離膜的角度來看,PEF(生質聚酯塑膠)性能確實很好,因為紙瓶的優勢來自於紙質製造,只需要一層薄薄的PEF即可實現阻隔(液體)的性能,能好好將內容物妥善保存一段時間。」

    這種植物塑膠,是由玉米、小麥,和甜菜根作成,可以用來盛裝包括氣泡型的飲料,能大幅減少塑膠污染,跟市場對化石燃料的依賴。經過將瓶子放入淡水、鹽水、泥土跟沉澱物的實驗證明,這種塑膠經過堆肥處理後,一年內就可以完全分解,就算放在戶外,也只要幾年時間就能分解。

    生化公司認為這些瓶子,可以回收再利用,因此爭取到了美國可口可樂公司,和丹麥啤酒製造商「嘉士伯」的支持,持續開發。由於植物塑膠的製作成本,生化公司了解一開始無法在價格上占有市場優勢,因此預計先每年生產5000公噸,預計將在2023年前,會與飲料公司合作,讓產品上架。

    污染治理
    國際新聞
    荷蘭
    可生物分解
    廢棄物

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • 研究:提高空調能源效率、改用氣候友善製冷劑 將可省下數千億噸碳排

    環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

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

  • Java I/O模型及其底層原理

    Java I/O模型及其底層原理

      Java I/O是Java基礎之一,在面試中也比較常見,在這裏我們嘗試通過這篇文章闡述Java I/O的基礎概念,幫助大家更好的理解Java I/O。
      在剛開始學習Java I/O時,我很迷惑,因為網上絕大多數的文章都是講解Linux網絡I/O模型的,那時我總是搞不明白和Java I/O的關係。後來查了看了好多,才明白Java I/O的原理是以Linux網絡I/O模型為基礎的,理解了Linux網絡I/O模型再學習Java I/O就很方便了,所以這篇文章,我們先來了解I/O的基本概念,再學習Linux網絡I/O模型,最後再看Java中的幾種I/O。

    什麼是I/O?

      I/O是Input、Output的縮寫,即對應計算機中的輸入輸出,以一次文件讀取為例,我們需要將磁盤上的數據讀取到用戶空間,那麼這次數據轉移操作其實就是一次I/O操作,更具體的說是一次文件I/O。我們瀏覽網頁,其中在請求一個網頁時,服務器通過網絡把數據發送給我們,此時程序將數據從TCP緩衝區複製到用戶空間,那麼這次數據轉移操作其實也是一次I/O操作,更具體的說是一次網絡I/O。I/O到處都在,十分重要,Java對I/O對底層操作系統的各種I/O模型進行了封裝,使我們可以輕鬆開發。

    Linux網絡I/O模型

      根據UNIX網絡編程對I/O模型的分類,UNIX提供了5種I/O模型,分別是:阻塞I/O(Blocking I/O)、非阻塞I/O(Non-Blacking I/O)、I/O多路復用模型(I/O Multiplexing)、信號驅動式I/O(Signal Driven I/O)、異步I/O(Asynchronous I/O)。我們逐步了解一下其基本原理。

    阻塞I/O(Blocking I/O)

      阻塞I/O是最早最基礎的I/O模型,其在讀寫數據過程中會阻塞。通過下圖我們可以看到,當用戶進程調用了recvfrom這個系統調用后,內核開始第一階段的數據準備工作,直到內核等待數據準備完成,然後開始第二階段的將數據從內核複製到用戶空間的工作,最後內核返回結果。整個過程中用戶進程都是阻塞的,直到最後返回結果后才接觸阻塞block狀態。阻塞I/O模型適用於併發量小且對時延不敏感的系統。

    非阻塞I/O(Non-Blacking I/O)

      當用戶進程調用recvfrom這個系統調用后,如果內核尚未準備好數據,此時不再阻塞用戶進程,而是立即返回一個EWOULDBLOCK錯誤。用戶進程會不斷髮起系統調用直到內核中數據被準備好(輪詢),此時將執行第二階段的將數據從內核複製到用戶空間的工作,然後內核返回結果。非阻塞I/O模型不斷地輪詢往往需要耗費大量cpu時間。

    I/O多路復用模型(I/O Multiplexing)

      I/O多路復用的優點在於單個進程可以同時處理多個網絡連接的I/O,其基本原理就是select/epoll函數可以不斷的輪詢其負責的所有socket,當某個socket有數據到達時,就通知用戶進程。
      如下圖所示,當用戶進程調用select函數時,整個進程會被阻塞block住,但是這裏的阻塞不是被socket I/O阻塞,而是被select這個函數阻塞。同時內核會監聽改select負責的所有socket(這裏的socket一般設置為non-blocking),當任何一個socket中的數據準備好時,select就會返回給用戶進程,這時候用戶進程再此發起一個系統調用,將數據從內核複製到用戶空間,並返回結果。
      對比I/O多路復用模型和阻塞I/O模型的流程,多路復用多了一個系統調用來完成select環節,除此之外沒有太大的不同。Select的優勢在於它可以同時處理多個connection,但是會多一個系統調用。多路復用本質上也不是非阻塞的。

    信號驅動式I/O(Signal Driven I/O)

      首先我們開啟socket的信號驅動I/O功能,然後用戶進程發起sigaction系統調用給內核后立即返回並可繼續處理其他工作。收到sigaction系統調用的內核在將數據準備好後會按照要求產生一個signo信號通知給用戶進程。然後用戶進程再發起recvfrom系統調用,完成數據從內核到用戶空間的複製,並返回最終結果。其基礎原理圖示如下:

    異步I/O(Asynchronous I/O)

      用戶進程向內核發起系統調用后,就可以開始去做其他事情了。內核收到異步I/O的系統調用后,會直接retrun,所以這裏不會對用戶進程有阻塞。之後內核等待數據準備完成後會繼續將數據從內核拷貝到用戶空間(具體動作可以由異步I/O調用定義),然後內核回給用戶進程發送一個signal,告訴用戶進程I/O操作完成了,整個過程不會導致用戶請求進程阻塞。
      信號驅動I/O模型是內核通知我們可以發起I/O操作了,而異步I/O模式是內核告訴我們I/O操作已經完成了。

      以上就是Linux的5種網絡I/O模型,其中前4中都是同步I/O模型,他們真正的I/O操作環節都會將進程阻塞,只有最後一種異步I/O模型是異步I/O操作。

    Java中的I/O模型

      在JDK1.4之前,基於Java的所有socket通信都是使用阻塞I/O(BIO),JDK1.4提供了了非阻塞I/O(NIO)功能,不過雖然名字叫做NIO,實際底層模型是I/O多路復用,JDK1.7提供了針對異步I/O(AIO)功能。

    BIO

      BIO簡化了上層開發,但是性能瓶頸問題嚴重,對高併發第時延支持差。
    基於消息隊列和線程池技術優化的BIO模式雖然可以對高併發支持有一定幫助,但是還是受限於線程池大小和線程池阻塞隊列大小的制約,當併發數超過線程池的處理能力時,部分請求法務繼續處理,會導致客戶端連接超時,影響用戶體驗。

    NIO

      NIO彌補了BIO的不足,簡單說就是通過selector不斷輪詢註冊在自己上面的channel,如果channel上面有新的連接讀寫時間時就會被輪詢出來,一個selector上面可以註冊多個channel,一個線程就可以負責selector的輪詢,這樣就可以支持成千上萬的連接。Selector就是一個輪詢器,channel是一個通道,通過它來讀取或者寫入數據,通道是雙向的,可以用於讀、寫、讀和寫。Buffer用來和channel交互,數據通過channel進出buffer。
    NIO的優點是可以可靠性好以及高併發低時延,但是使用NIO的代碼開發較為複雜。

    AIO

      AIO,或者說叫做NIO2.0,引入了異步channel的概念,提供了異步文件channel和異步socket channel的實現,開發者可以通過Future類來表示異步操作的結果,也可以在執行異步操作時傳入一個channels,實現CompletionHandler接口作為回調。AIO不用開發者單獨開發獨立線程的selector,異步回調操作有JDK地城思安城池負責驅動,開發起來比NIO簡單一些,同時保持了高可靠高併發低時延的優點。

    參考:
    https://blog.csdn.net/historyasamirror/article/details/5778378
    https://juejin.im/post/5cce5019e51d453a506b0ebf

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

    【其他文章推薦】

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

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

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

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

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