設計模式:(三)結構性模式

結構性模式.png
一、介面卡模式

介面卡模式.png
下面來看看介面卡的三個角色:
- Tagret目標角色:
該角色定義把其他類轉換為何種介面,也就是我們所期望介面。 - Adaptee源角色:
你想把誰轉換成目標角色,它使已經存在的、執行良好的類或物件,經過介面卡角色包裝,成為一個嶄新的角色。 - Adapter介面卡模式:
介面卡模式的核心角色,其他兩個角色都是已經存在的角色,而介面卡角色是需要新建立的,它的職責非常簡單:把源角色轉換為目標角色。
通用程式碼實現
目標角色
public interface Target { //目標角色自有的方法 public void request(); } public class ConcreteTarget implements Target { public void request (){ } }
目標角色是一個已經正式執行的角色,不可能去修改角色中的方法。
源角色
public class Adaptee { //原有的業務邏輯 public void doSomething(){ } }
介面卡角色
public class Adapter extends Adaptee implements Target { public void reqest(){ super.doSmothing(); } }
二、橋接模式

橋接模式.png
橋接模式將繼承模式轉化成關聯關係,他降低了類與類之間的耦合度,減少了系統中類的數量,也減少了程式碼量。
橋接是一個介面,它與一方應該是繫結的,也就是解耦的雙方中的一方必然是繼承這個介面的,這一方就是實現方,而另一方正是要與這一方解耦的抽象方,如果不採用橋接模式,一般我們的處理方式是直接使用繼承來實現,這樣雙方之間處於強連結,類之間關聯性極強,如要進行擴充套件,必然導致類結構急劇膨脹。

uml.png
三、組合模式

組合模式.png
組合模式,就是在一個物件中包含其他物件,這些被包含的物件可能是終點物件(不再包含別的物件),也有可能是非終點物件(其內部還包含其他物件,或叫組物件),我們將物件稱為節點,即一個根節點包含許多子節點,這些子節點有的不再包含子節點,而有的仍然包含子節點,以此類推

組合模式類圖
-
Component抽象構件角色
定義參加組合物件的共有方法和屬性,可以定義一些預設的行為或屬性。
-
Leaf葉子構件
葉子物件,其下再也沒有其他的分支,也就是遍歷的最小單位。
-
Composite樹枝構件
樹枝物件,它的作用是組合樹枝節點和葉子節點形成一個樹形結構。
public abstract class Component { //個體和整體都具有的共享 public void doSomething(){ //編寫業務邏輯 } } /** *樹枝構件 */ public class Composite extends Component { //構件容器 private ArrayList<Component> componentArrayList = new ArrayList<Component>(); //增加一個葉子構件或樹枝構件 public void add(Component component){ this.componentArrayList.add(component); } //刪除一個葉子構件或樹枝構件 public void remove(Component component){ this.componentArrayList.remove(component); } //獲得分支下的所有葉子構件和樹枝構件 public ArrayList<Component> getChildren(){ return this.componentArrayList; } } //樹葉節點是沒有子下級物件的物件,定義參加組合的原始物件行為,其通用原始碼 public class Leaf extends Component { /* * 可以覆寫父類方法 * public void doSomething(){ * * } */ }
四、裝飾器模式

裝飾器模式.png
裝飾模式通用類圖如下所示:

2018-11-13 下午5.12.56.png
在類圖中,有四個角色需要說明:
-
Component抽象構件
Component是一個介面或者是抽象類,就是定義我們最核心的物件,也就是最原始的物件。
-
ConcreteComponent 具體構件
ConcreteComponent是最核心、最原始、最基本的介面或抽象類的實現,你要裝飾的就是它。
-
Decorator裝飾角色
一般是一個抽象類,做什麼用呢?實現介面或者抽象方法,它裡面可不一定有抽象的方法,在它的屬性裡必然有一個private變數指向Component抽象構件。
-
具體裝飾角色
ConcreteDecoratorA和ConcreteDecoratorB是兩個具體的裝飾類,你要把你最核心的、最原始的、最基本的東西裝飾成其他東西
//抽象構件 public abstract class Component { //抽象的方法 public abstract void operate(); } //具體構件 public class ConcreteComponent extends Component { //具體實現 @Override public void operate() { System.out.println("do Something"); } } //抽象裝飾者 public abstract class Decorator extends Component { private Component component = null; //通過建構函式傳遞被修飾者 public Decorator(Component _component){ this.component = _component; } //委託給被修飾者執行 @Override public void operate() { this.component.operate(); } } //具體的裝飾類 public class ConcreteDecorator1 extends Decorator { //定義被修飾者 public ConcreteDecorator1(Component _component){ super(_component); } //定義自己的修飾方法 private void method1(){ System.out.println("method1 修飾"); } //重寫父類的Operation方法 public void operate(){ this.method1(); super.operate(); } } public class ConcreteDecorator2 extends Decorator { //定義被修飾者 public ConcreteDecorator2(Component _component){ super(_component); } //定義自己的修飾方法 private void method1(){ System.out.println("method1 修飾"); } //重寫父類的Operation方法 public void operate(){ this.method1(); super.operate(); } }
五、外觀模式

外觀模式.png
也就是提供一個訪問子系統的介面,除了這個介面不允許有任何訪問子系統的行為發生,其通用類圖如下:

外觀模式類圖.jpeg
-
Facade門面角色
客戶端可以呼叫這個角色的方法。此角色知曉子系統的所有功能和責任。一般情況下,本角色會將所有從客戶端發來的請求委派到相應的子系統去,也就說該角色沒有實際的業務邏輯,只是一個委託類。
-
subsystem子系統角色
可以同時有一個或者多個子系統。每一個子系統都不是一個單獨的類,而是一個類的集合。子系統並不知道門面的存在。對於子系統而言,門面僅僅是另外一個客戶端而已。
// 子系統 public class ClassA { public void doSomethingA(){ //業務邏輯 } } public class ClassB { public void doSomethingB(){ //業務邏輯 } } public class ClassC { public void doSomethingC(){ //業務邏輯 } } //門面物件 public class Facade { //被委託的物件 private ClassA a = new ClassA(); private ClassB b = new ClassB(); private ClassC c = new ClassC(); //提供給外部訪問的方法 public void methodA(){ this.a.doSomethingA(); } public void methodB(){ this.b.doSomethingB(); } public void methodC(){ this.c.doSomethingC(); } }
六、享元模式

享元模式.png
要求細粒度物件,那麼不可避免地使得物件數量多且性質相近,那我們就將這些物件的資訊分為兩個部分:內部狀態(intrinsic)與外部狀態(extrinsic)。
● 內部狀態
內部狀態是物件可共享出來的資訊,儲存在享元物件內部並且不會隨環境改變而改變,如id、name等,它們可以作為一個物件的動態附加資訊,不必直接儲存在具體某個物件中,屬於可以共享的部分。
● 外部狀態
外部狀態是物件得以依賴的一個標記,是隨環境改變而改變的、不可以共享的狀態

享元模式類圖.jpeg
-
Flyweight——抽象享元角色
它簡單地說就是一個產品的抽象類,同時定義出物件的外部狀態和內部狀態的介面或實現。
-
ConcreteFlyweight——具體享元角色
具體的一個產品類,實現抽象角色定義的業務。該角色中需要注意的是內部狀態處理應該與環境無關,不應該出現一個操作改變了內部狀態,同時修改了外部狀態,這是絕對不允許的。
-
unsharedConcreteFlyweight——不可共享的享元角色
不存在外部狀態或者安全要求(如執行緒安全)不能夠使用共享技術的物件,該物件一般不會出現在享元工廠中。
-
FlyweightFactory——享元工廠
職責非常簡單,就是構造一個池容器,同時提供從池中獲得物件的方法。
//抽象享元角色 public abstract class Flyweight { //內部狀態 private String intrinsic; //外部狀態 protected final String Extrinsic; //要求享元角色必須接受外部狀態 public Flyweight(String _Extrinsic){ this.Extrinsic = _Extrinsic; } //定義業務操作 public abstract void operate(); //內部狀態的getter/setter public String getIntrinsic() { return intrinsic; } public void setIntrinsic(String intrinsic) { this.intrinsic = intrinsic; } } //具體享元角色。實現自己的業務邏輯,然後接收外部狀態,以便內部業務邏輯對外部狀態的依賴。 //注意,我們在抽象享元中對外部狀態加上了final關鍵字, //防止意外產生,什麼意外?獲得了一個外部狀態,然後無意修改了一下,池就混亂了 public class ConcreteFlyweight1 extends Flyweight{ //接受外部狀態 public ConcreteFlyweight1(String _Extrinsic){ super(_Extrinsic); } //根據外部狀態進行邏輯處理 public void operate(){ //業務邏輯 } } public class ConcreteFlyweight2 extends Flyweight{ //接受外部狀態 public ConcreteFlyweight2(String _Extrinsic){ super(_Extrinsic); } //根據外部狀態進行邏輯處理 public void operate(){ //業務邏輯 } } //享元工廠 public class FlyweightFactory { //定義一個池容器 private staticHashMap<String,Flyweight> pool= new HashMap<String,Flyweight>(); //享元工廠 public static Flyweight getFlyweight(String Extrinsic){ //需要返回的物件 Flyweight flyweight = null; //在池中沒有該物件 if(pool.containsKey(Extrinsic)){ flyweight = pool.get(Extrinsic); }else{ //根據外部狀態建立享元物件 flyweight = new ConcreteFlyweight1(Extrinsic); //放置到池中 pool.put(Extrinsic, flyweight); } return flyweight; } }
七、代理模式

代理模式.png
代理模式通用類圖:

代理模式類圖.jpeg
-
Subject抽象主題角色
抽象主題類可以是抽象類也可以是介面,是一個最普通的業務型別定義,無特殊要求。
-
RealSubject具體主題角色
也叫做被委託角色、被代理角色。它才是冤大頭,是業務邏輯的具體執行者。
-
Proxy代理主題角色
也叫做委託類、代理類。它負責對真實角色的應用,把所有抽象主題類定義的方法限制委託給真實主題角色實現,並且在真實主題角色處理完畢前後做預處理和善後處理工作。
//抽象主題類 public interface Subject { //定義一個方法 public void request(); } //真實主題類 public class RealSubject implements Subject { //實現方法 public void request() { //業務邏輯處理 } } //代理類 public class Proxy implements Subject { //要代理哪個實現類 private Subject subject = null; //預設被代理者 public Proxy(){ this.subject = new Proxy(); } //通過建構函式傳遞代理者 public Proxy(Object...objects ){ } //實現介面中定義的方法 public void request() { this.before(); this.subject.request(); this.after(); } //預處理 private void before(){ //do something } //善後處理 private void after(){ //do something } }
JDK 自帶的動態代理:
java.lang.reflect.Proxy:生成動態代理類和物件;
java.lang.reflect.InvocationHandler(處理器介面):可以通過invoke方法實現
對真實角色的代理訪問。
每次通過 Proxy 生成的代理類物件都要指定對應的處理器物件。
- 介面:Subject.java
public interface Subject { public String speak(); }
- 真實物件:RealSubject.java
public class RealSubject implements Subject{ @Override public String speak() { System.out.println("speak"); return "speak"; } }
- 處理器物件:MyInvocationHandler.java
public class MyInvocationHandler implements InvocationHandler { /** * 因為需要處理真實角色,所以要把真實角色傳進來 */ Subject realSubject ; public MyInvocationHandler(Subject realSubject) { this.realSubject = realSubject; } /** * * @param proxy代理類 * @param method正在呼叫的方法 * @param args方法的引數 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("呼叫代理類"); if(method.getName().equals("speak")){ String str = (String)method.invoke(realSubject, args); System.out.println("呼叫的是說話的方法"); return str ; } } }
- 呼叫端:Main.java
public static void main(String[] args) { //真實物件 Subject realSubject =new RealSubject(); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject); //代理物件 Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler); proxyClass.speak(); }