2022年1月30日 星期日

[Geroge]設計模式-適配器模式Adapter Pattern (下)

 2對象的適配器







與類的適配器模式一樣, 對象的適配器模式把被適配的淚的API轉換成為目標類的API, 與類的適配器模式不同的是, 對象的適配器模式不是使用繼承關係連接到Adaptee類, 而是使用委派關係連接到Adaptee的實例, 從而包裝類能夠把Adaptee的API與Target類的API銜接起來, Adapter與Adaptee是委派關係, 這決定了適配器模式是對象的(相當於Adapter把Adaptee當作成員, 並調用Adaptee做額外特別的處理)

例子:




有220V也有12V

我們將220V當作成員變數放在Adapter中

履行12V的方法, 並在其中呼叫220V並使用轉換














下面是運作類, 先New notbook, 再new Adapter將220V放入, 最後notebook使用Adapter 來turn on





下方是結果




對象與類適配器對比1

類適配器使用對象繼承方式, 是靜態的定義方式(啟動時動作)

對象適配器使用對象組合的方式, 是動態組合的方式(運作時動作)

對象與類適配器對比2

類適配器, 由於Adapter直接繼承Adaptee ,使得Adapter不能和Adaptee的子類一起工作, 因為繼承是靜態關係, 當Adapter繼承了Adaptee後, 就不可能去處理Adaptee的子類了

對象適配器, 一個Adapter可以把多種不同的源是配到同一個目標, 換言之, 同一個適配器可以把源類和它的子類都是配到目標接口, 因為對象適配器採用的是對象組合的關係, 只要對象類型正確, 是不是子類都無所謂

對象與類適配器對比3

類適配器, Adapter可以重新定義Adaptee的部分行為, 相當於子類可以覆蓋父類的部分實現方法

對象適配器, 要重新定義Adaptee的行為比較困難, 這種情況下, 需要定義Adaptee的子類來重新實現定義, 然後讓適配器組合子類, 雖然重定義Adaptee行為比較困難, 但是想要增加一些新的方法相當方便,而且新增行為可同時適用於所有的源

對象與類適配器對比4

對於類適配器, 僅僅引入了一個對象, 並不需要額外的引用來間接得到Adaptee

對於對象適配器, 需要而外的引用來間接得到Adaptee


使用建議:

建議盡量使用對象適配器的實現方式, 多用合成/聚合, 少用繼承, 當然具體問題具體分析, 根據需要來選擇實現方是, 最適合的才是最好的


適配器模式的優點:

更好的復用性: 系統需要使用現有的類, 而此類的介面不符合系統的需要, 那麼通過適配器模式就可以讓這些功能得到很好的復用

更好的擴展性: 在實現適配器功能的時候, 可以調用自己開發的功能, 從而自然地擴展系統的功能


適配器模式的缺點:

過多使用適配器, 會使系統非常凌亂, 不容易整體把握, 例如明明看到調用A介面, 但是內部被配適成B介面, 一個系統如果出現太多這種情況, 無疑是種災難, 因此如果不是必要, 可以不使用是珮配器, 而是直接對系統進行重構


實際使用場景:

一般是外部提供的庫, 或是包, 定義介面與方法不同於本系統內部定義的介面

為了不改變本系統內部調用的流程, 往往使用適配器模式來間容外部提供的褲或包

https://www.youtube.com/watch?v=cA-vt0Nf1nQ&list=PLGmd9-PCMLhb16ZxeSy00qUsBazXgJyfM&index=18

https://github.com/iw5420/geroge-design-pattern


[Geroge]設計模式-適配器模式Adapter Pattern (上)

適配器模式(Adapter pattern)

將一個類的介面轉換成客戶希望的另外一個介面, Adapter模式使得原本由於介面不兼容而不能一起工作的那些類可以一起工作-Gang of Four

就如同筆記本的插頭一般都會帶有一個黑色長方形的物體, 名字叫適配器(轉接頭)

功能就把220V轉化為12V供我們的筆記本使用, 我們今天講的模式是一樣的





適配器模式的結構 有兩種:

1類的適配器模式









Adapter是中間一個環節, 把Adaptee的API與Target類的API銜接起來

Adapter繼承Adaptee同時實現Target介面(因繼承而屬於類的適配器模式)

目標(Target)角色: 這就是期待的介面, 由於這裡是類適配器模式, 因此目標不可以是類

(Adaptee)角色: 現在需要適配的對象(ex 220V)

適配器(Adapter)角色: 類適配器適本模式的核心, 類適配器把源類別轉換成目標介面, 顯然這一角色不可以是介面, 而必須是具體類

例子:

我們有220V, 也有5V, 而NoteBook使用5V做事










這時我們就需要轉接頭(Adapter)

運作getPower5V裡面會使用父類別得到220V並轉換






運作測試, 當notbook power on的時候塞入Adapter






下面是結果




https://www.youtube.com/watch?v=cA-vt0Nf1nQ&list=PLGmd9-PCMLhb16ZxeSy00qUsBazXgJyfM&index=17

https://github.com/iw5420/geroge-design-pattern


2022年1月27日 星期四

[Geroge]設計模式-Java動態代理

靜態代理: 自己手寫的代理類, 程序運行前就已經存在的編譯好的代理類 by Geroge

反之~如果代理類程序運行之前並不存在, 需要在程序運行時動態生成(無須手工編寫代理類源碼), 那就是今天要說的動態代理了

如何生成的: 根據Java的反射機制動態生成

*****

Java動態代理(Dynamic Proxy)

在java中, 通常使用Proxy(動態代理)來實現對代理模式的支持

Java中要創建一個代理對象, 必須調用Proxy類的靜態方法newProxyInstance,該方法的原型如下

Object Proxy.newProxyInstance(ClassLoader loader, class<?>[]interfaces, InvacationHandler handler) throws IllegalArgumentExcetion

其中: 

loader: 表示類加載器, 對於不同來源(系統庫或網路等)的類需要不同的類加載器來加載, 這是Java安全模型的一部分

interfaces: 它就是被代理對象共有的介面

handler : 表示調用處理器, 它必須是實現了InvocationHandler介面的對象, 其作用是定義代理對象中需要執行的具體操作, InvocationHandler 之於Proxy, 就如Runnable之於Thread, InvocationHandler 介面中只有一個方法Invoke, 它的作用就跟Runnable中的run方法類似, 定義了代理對象在執行真實對象的方法時所希望執行的動作, 其原型如下:

Object invoke(Object proxy, Method method, Object[] args)throws Throwable

其中

proxy: 表示執行這個方法的代理對象

method: 表示真實對象實際需要執行的方法(關於Method類參見Java的反射機制)

args: 表示真實對象實際執行方法時所需的參數

例子: 

例如我們有一個KFC介面, 還有它的實現, 也有miseller介面, 和它的實現



















我們造一個Handler類別去實行InvocationHandler類別
生一個區域變數realObject, 當初始化時傳進來
下面方法調用invoke的時候它再invoke找realSubject做事










而它在使用上, 可以用Proxy. newProxyInstnce傳入當前class.getClassLoader, 第二個參數有兩種寫法如下new Class[]{將使用的介面}, 或是將使用的介面.class.getInterfaces(), 最後再new ProxyHandler類別出來將要使用的實現類別塞入




















總結
Proxy已經設計優美, 但是它有小缺憾, 始終無法擺脫interface代理, 基於代理他們已經注定有一個共同父類Proxy, 這個機制注定了動態代理類無法對class的動態代理, 原因是多重繼承無法通行
那類的動態代理有CGLib, Aspectj可以再更深入學習

https://www.youtube.com/watch?v=cA-vt0Nf1nQ&list=PLGmd9-PCMLhb16ZxeSy00qUsBazXgJyfM&index=16

https://github.com/iw5420/geroge-design-pattern


[Geroge]設計模式-代理模式

 







代理模式: 為其他對象提供一種代理以控制對這個對象的訪問, 在某些情況下, 一個對象不適合或著不能直接引用另一個對象, 而代理對象可以在客戶端和目標對象中間起到中介作用








模式中包含的對象及其職責

Subject: 抽象主題主角, 抽象主題類可以是抽象類, 也可以是介面, 是一個最普通的業務類型定義, 無特殊要求

RealSubject: 具體主題角色, 也叫做委託角色, 被代理角色, 是業務邏輯的具體執行者

Proxy: 代理主題角色, 也叫委託類, 代理類, 它被所有抽象主題類定義的方法給具體主題角色實現, 並且在具體主題角色處理完畢前後做預處理和善後工作(最簡單的比如打印日誌)

對於subject來說, 如果傳過來的是RealSubject, 就叫RealSubject做事, 如果是Proxy就叫Proxy做事

例子:

我們有一個介面, IMacSeller, 它有一個方法buy

我們造一個類別USAMacSeller 作為真正商品來源的賣商, 實現買的功能(reasubjet)

我們再造一個類別HongKongMacSeller, 作為代理商(Proxy)當我們購買的時候, 它會將USAMacSeller 叫出來實現購買




















相當於說, 他去美國買這件事情已經看不到, 也就是香港這個代理商幫我們做了去美國買這一件事情, 今天我們換成和美國買, 也是沒有問題, 今天這個代理這一件事情, 對於我們客戶端是不可見的, 也是可以任意替換的, 今天要去泰國, 或是其他地方買, 對我們而言也是一樣的, 我們只知道我們買到了macbook, 但是代理商從哪來的就是不可見的


應用場景:

現實世界中, 秘書就相當於一個代理, 老闆開會, 通知員工開會時間, 布置會場, 會後整理會場等等工作事情就可以叫給秘書來做, 老闆只需要開會就行, 並不需要親自做那些事, 同理, 我們程式設計中可以使用代理模式, 將一系列無關邏輯組合再一起進行解耦, 比如業務代碼中的日誌代碼, 就可以在代理中進行, Spring的AOP就是典型的動態代理應用


https://www.youtube.com/watch?v=cA-vt0Nf1nQ&list=PLGmd9-PCMLhb16ZxeSy00qUsBazXgJyfM&index=15

https://github.com/iw5420/geroge-design-pattern


2022年1月26日 星期三

[Geroge]設計模式-抽象工廠

抽象工廠: 多個抽象產品類, 派生出多個舉體產品類, 一個抽象工廠類, 派生出多個具體工廠類, 每個具體工廠類可創建多個具體產品類的實例

即提供一個創建一系列相關或相互依賴對象的介面, 而無須指定他們的具體的類, 一對多的關係








抽象工廠模式是工廠方法的升級版本, 他用來創建一組相關或相互依賴的對象, 他與工廠方法的區別在於, 工廠方法模式是針對一個產品等級結構, 而抽象工廠模式則是針對多個產品等級結構, 在寫程式中, 通常一個產品結構為一介面或是抽象類, 也就是說, 工廠方法模式提供所有產品衍生自同一個介面或抽象類, 而抽象工廠模式所提供的產品則是衍生自不同的介面或抽象類

範例:

我們有一個抽象工廠IStore, 它生產一系列產品, 有薯條, 有烤雞, 而麥當勞, 德克士, 肯德基都是基於IStore創建出來, 我們用戶在使用的不關心它是哪家生產的, 我們只關心他們生產出來的產品, 比方說雞, 或是薯條, 這樣我們就把生產和產品完全分開, 同時我們解決了, 一個產品需要一個工廠生產, 這樣我們就需要非常多的工廠來生產產品, 我們產品有一系列的觀念的話, 一個工廠就能生產多種同系列的產品, 這樣就能獲得更好的擴展性


























總結: 

無論是簡單工廠模式, 工廠模式, 或是抽象工廠模式, 它們都屬於工廠模式, 在形式和特點上極為相似, 他們最終的目的都是為了解耦

在使用時我們不必去在意這個模式到底是工廠方法模式還是抽象工廠模式, 因為他們之間的演變常常琢磨不透, 你會發現明明使用工廠方法模式, 新需求來臨, 修改後加入一個新的方法, 類中產品變成不同等級中的產品系列, 它就變成抽象工廠模式, 而對於抽象工廠模式, 當減少一個方法使得提供產品, 不再是一系列產品, 它就變成工廠方法模式

所以在使用工廠模式時, 只需要關心降低耦合度的目的是否達到了

https://www.youtube.com/watch?v=cA-vt0Nf1nQ&list=PLGmd9-PCMLhb16ZxeSy00qUsBazXgJyfM&index=14

https://github.com/iw5420/geroge-design-pattern

[Geroge]設計模式-工廠方法

工廠方法: 一抽象產品類派生出多個具體產品類; 一抽象工廠類派生出多個具體工廠類; 每個具體工廠類只能創建一個具體產品類的實例

即定義一個創建對象的介面(抽象工廠類), 讓其子類(具體工廠類), 決定實例化哪一個類(具體產品類), "一對一"的關係







工廠接口: 工廠接口是工廠方法的核心, 與調用者直接交互用來提供產品, 在實際編程中, 有時候也會使用一個抽象類來做為調用者交互的接口, 其本質上是一樣的

工廠實現: 在編程中, 工廠實現決定如何實例化產品, 是實現擴展的途徑, 需要有多少種產品, 就需要有多少個具體的工廠實現

產品接口: 產品接口的主要目的是定義產品的規範, 所有的產品實現都必須遵循產品接口定義的規範, 產品接口是調用者最關心的, 產品接口的定義優劣, 直接決定了調用者程式碼的穩定性, 同樣, 產品接口也可以用抽象類代替, 但要注意最好不要違反里氏替換原則

產品實現: 實現產品接口的具體類, 決定了產品在客戶端中的具體行為


例子:

IStore是一個抽象類, 他返回是IChips也是抽象產品, 那後面IStore類的實現類, 當有需要的時候, 可以新增新的實現類, 抽象類的程式碼就不會動到了





















優點:

1 在工廠方法中, 用戶只需要知道所要產品的具體工廠, 無須關心具體的創建過程, 甚至不需要具體產品類的類名

2 在系統增加新的產品時, 我們只需要添加一個具體產品類和對應的實現工廠, 無須隊員工廠進行任何修改, 很好地符合了"開閉原則"

缺點:

每次增加一個產品時, 都需要增加一個具體類和對象實現工廠, 使得系統中類的個數成倍增加, 在一定程度上增加了系統的複雜度, 同時也增加了系統具體類的依賴, 這並不是什麼好事

與簡單工廠的對比:

工廠方法模式是簡單工廠模式的延伸, 在工廠方法模式中, 核心工廠類不在負責產品的創建, 而是將具體的創建工作交給子類去完成, 也就是核心工廠僅僅只提供創建的接口, 具體實現方法交給繼承他的子類去完成

當我們的系統需要增加其他新的對象時, 我們只需要添加一個具體的產品和它的創建工廠即可, 不需要對原工廠進行任何修改, 這樣很好地符合了"開閉原則"


https://www.youtube.com/watch?v=cA-vt0Nf1nQ&list=PLGmd9-PCMLhb16ZxeSy00qUsBazXgJyfM&index=13

https://github.com/iw5420/geroge-design-pattern

2022年1月25日 星期二

[Geroge]設計模式-簡單工廠模式

工廠模式: 1 簡單工廠 2工廠方法 3抽象工廠

簡單工廠模式(Simple Factory Pattern)

屬於類的創新型模式, 又叫靜態工廠方法模式(Static Factory Method Pattern)

是通過專門定義一個類來負責創建其他類的實例, 被創建的實例通常都具有共同的父類









工廠角色(SimpleFactory): 這是簡單工廠模式的核心, 由它負責創建所有的類的內部邏輯, 當然工廠類必須能夠被外界調用, 創建所需要的產品對象

抽象產品角色(IProduct): 簡單工廠模式所創建的所有對象的父類, 注意, 這裡的父類可以是介面, 也可以是抽象類, 它負責描述所有實例所共有的公共介面

具體產品角色(Concrete Product): 簡單工廠所創建的具體實例對象, 這些具體的產品往往都擁有相同的父類

ex 有一個麥當勞工廠, 當顧客需要麥香雞, 或是薯條的時候, 只要來找麥當勞要食物, 就可以, 他並不需要真正知道麥香雞會是薯條的生產過程































簡單工廠優點

工廠類是整個模式的關鍵, 它包含必要的判斷邏輯, 能夠根據外界給定的信息, 決定究竟應該創建哪個體系的對象, 用戶在使用時可以直接根據工廠類去創建所需的實例, 而無須了解這些對象是如何創建以及如何組織的, 有利於整體軟體體系結構的優化

簡單工廠缺點

由於工廠類集中所有實例的創建邏輯, 這就直接導致依但這個工程出了問題, 所有的客戶端都會受到牽連, 並且由於簡單工廠模式的產品基於一個共同的抽象類, 或是接口, 這樣一來, 一旦產品種類增加, 有不同的產品接口或是抽象類的時候, 工廠類就需要判斷何時何種類的產品, 這就和創建何種類產品的產品鄉混淆再一起, 違反了單一職責, 導致系統喪失靈活性和可維護性,同時也違背了開閉原則, 就是違背了"系統對擴展開放, 對修改關閉" 的原則, 因為當我新增一個攢品的時候, 就必須要修改工廠類, 相應的工廠類就需要重新編譯一遍

簡單工廠實際應用

JDBC是SUN提供一套數據庫連接介面API, 它利用Java語言提供簡單一致來訪問各種關聯式資料庫, 對於我們來說, 增刪查改, 我們只需要調用統一的JDBC介面, 而實際的實現ex mysql或是oracle都是由JDBC工廠把它創建出來, 我們來使用這樣我們客戶端和實際的數據庫就達到一個解耦的目的, 這樣我們去替換數據庫的時候就不需要改變真實的程式碼, 因為所有的差別都被封閉在這個簡單工廠裡面








https://www.youtube.com/watch?v=cA-vt0Nf1nQ&list=PLGmd9-PCMLhb16ZxeSy00qUsBazXgJyfM&index=12

https://github.com/iw5420/geroge-design-pattern

2022年1月23日 星期日

[Geroge]設計模式-單例模式(下)

單例模式進階

單例模式線程安全問題(最主要問題)

    餓漢式 hungry

    優點: 線程安全

    缺點: 過早浪費資源


    懶漢式 lazy

    優點: 使用時才創建, 資源節約

    缺點: 為線程安全要付出額外代價

解決線程安全的懶漢式單例模式:

    1 synchronized方法

    2 雙重檢測鎖單例模式

    3 內部類Holder式(建議使用)

例子:

問題點: 我們有一個懶漢式的單例模式, 我們讓他在創建的時候睡3秒鐘, 而執行的時候其中一個開執行續執行, 另外再執行創建一次, 我們會發現不同執行續下, 他就會創建出兩個物件




















要解決這個問題, 我們可以在getInstance上面加上synchronized關鍵字, 這樣就可以達到同步















然而, 同步是相當消耗資源, 這會導致說, 如果我們系統有很多地方要去getInstance的話, 它的效率就會很低, 為了提出解決效率的問題, 我們就有了"雙重檢測鎖機制"

他的意思是, 如果判斷為空, 我就去加鎖, 保證後面這段部分只有一個人執行, 相當於說, 兩個線程不能同時進到加鎖內的程式碼區塊, 這時特別注意要加入volatile才能保證他正常執行, 他的意思是對於後面的變量"SingleTonDoubleCheck"不會被改變他的執行順序, 因為new SingleTonDoubleCheck() 他有可能不是一個原子操作, 可能是先產生singleTon, 然後分配空間, 才new, 這樣兩到三個動作中, 因此是有可能他還沒有執行完整new動作, 另一個線程就近來判斷為null, 反而再產生了一個新物件, 這也產生一個問題, 就是volatile效率也是相對較低的, 每次都透過內存器去真正取值, 而沒有透過暫存器去緩存, 另外, 他有一個鎖的話, 始終還是會帶來效率問題的












那最佳的解法, 是由內部類Holder來創建Singleton, 他是由內部類中, 由內部類來放靜態的實例, 然後再getInstance中, 才去返回instance, 首先這個new SingleTinHolder這件事, 他是直接在類加載的時候來創建, 就克服了線程安全的問題, 因為它是由靜態內部類來做, 也就是它是由java內部來完成, 因此他是保證單線程訪問, 那他也結合懶漢式的優點, 是在我們使用到getInstance方法的時候才去加載, 所以不會過早創建而浪費資源











https://www.youtube.com/watch?v=w-BlQ5tsZfU&list=PLGmd9-PCMLhb16ZxeSy00qUsBazXgJyfM&index=11

https://github.com/iw5420/geroge-design-pattern

[leetcode] [KMP] KMP

ABCDABD... ABCDABF... 簡單的說, 傳統解兩字串匹配部分 可能會來個雙迴圈, 哀個比對, 當不匹配的時候, 會將下方列再後移1位 然後不匹配再後移 然而 如果像上放已經有4個屬於匹配的字串, 她就應該直接往後移四位來匹配, 而不是只移動1位 隱藏的思維是, 當...