結構型模式

整體分爲3大類:
建立型模式 (5種):工廠方法、抽象工廠、單例、建造者、原型
結構型模式(7種):適配器、裝飾器、代理、外觀、橋接、組合、享元
行爲型模式(11種):策略、模板方法、觀察者、迭代子、責任鏈、命令、備忘錄、狀態、訪問者、中介者、解釋器
其它(2種):併發型、線程池
clipboard.pngnode

1、適配器

  1. 類的適配器
    有一個待適配的Source類擁有一個方法,經過Adapter類將Source的功能擴展到Targetable接口裏。
    clipboard.pngmysql

    public class Source {
        public void method1(){
            System.out.println("this is original method!");
        }
    }
    public interface Targetable {
        /* 與原類中的方法相同 */
        void method1();
    
        /* 新類的方法 */
        void method2();
    }
    public class Adapter extends Source implements Targetable {
    
        @Override
        public void method2() {
            System.out.println("this is the targetable method!");
        }
    }

    測試類spring

    public class AdapterTest {
        public static void main(String[] args) {
            Targetable target = new Adapter();
            target.method1();
            target.method2();
        }
    }

    clipboard.png

  2. 對象的適配器
    Adapter 類持有 Source 類的實例,以達到解決兼容性的問題。sql

    clipboard.png

    public class Wrapper implements Targetable {
        private Source source;
    
        public Wrapper(Source source) {
            this.source = source;
        }
    
        @Override
        public void method1() {
            source.method1();
        }
    
        @Override
        public void method2() {
            System.out.println("this is the targetable method!");
        }
    }

    測試類:數據庫

    public class AdapterTest {
        public static void main(String[] args) {
            Source source = new Source();
            Targetable target = new Wrapper(source);
            target.method1();
            target.method2();
        }
    }

    clipboard.png

  3. 接口的適配器segmentfault

    藉助一個抽象類實現接口全部的方法,寫一個類繼承該抽象類並重寫咱們須要的方法就行 :
    clipboard.png併發

    接口app

    public interface Sourceable {
        void method1();
        void method2();
    }

    抽象類ide

    public abstract class Wrapper2 implements Sourceable {
        @Override
        public void method1() {}
    
        @Override
        public void method2() {}
    }
    public class SourceSub1 extends Wrapper2 {
        @Override
        public void method1() {
            System.out.println("the sourceable interface's first Sub1!");
        }
    }
    public class SourceSub2 extends Wrapper2 {
        @Override
        public void method2() {
            System.out.println("the sourceable interface's second Sub2!");
        }
    }

    測試類:性能

    public class WrapperTest {
        public static void main(String[] args) {
            Sourceable source1 = new SourceSub1();
            Sourceable source2 = new SourceSub2();
    
            source1.method1();
            source1.method2();
            source2.method1();
            source2.method2();
        }
    }

    clipboard.png

    類的適配器:當但願將一個類轉換成知足另外一個新接口的類時,可使用類的適配器模式,建立一個新類,繼承原有的類,實現新的接口便可。
    對象的適配器模式:當但願將一個對象轉換成知足另外一個新接口的對象時,能夠建立一個Wrapper 類,持有原類的一個實例,在 Wrapper 類的方法中,調用實例的方法就行。
    接口的適配器模式:當不但願實現一個接口中全部的方法時,能夠建立一個抽象類 Wrapper,實現全部方法,咱們寫別的類的時候,繼承抽象類便可。

2、裝飾(Decorator)

動態的給一個對象增長一些新的功能,要求裝飾對象和被裝飾對象實現同一個接口,裝飾對象持有被裝飾對象的實例,關係圖以下:

clipboard.png

Source 類是被裝飾類,Decorator 類是一個裝飾類,能夠爲 Source 類動態的添加一些功能,
代碼以下 :

public interface Sourceable {
    void method();
}
public class Source implements Sourceable {
    @Override
    public void method() {
        System.out.println("the orignal method!");
    }
}
public class Decorator implements Sourceable {
    private Sourceable source;

    public Decorator(Sourceable source) {
        this.source = source;
    }

    @Override
    public void method() {
        System.out.println("before decorator!");
        source.method();
        System.out.println("after decorator!");
    }
}

測試類:

public class DecoratorTest {
    public static void main(String[] args) {
        Sourceable source = new Source();
        Sourceable obj = new Decorator(source);
        obj.method();
    }
}

clipboard.png

應用場景:
一、須要擴展一個類的功能。
二、動態的爲一個對象增長功能,並且還能動態撤銷。(繼承不能作到這一點,繼承的功能是靜態的,不能動態增刪。)
缺點:產生過多類似的對象,不易排錯!

3、代理(Proxy)

代理類替原對象進行一些操做,好比咱們在租房子的時候回去找中介,爲何呢?由於你對該地區房屋的信息掌握的不夠全面,但願找一個更熟悉的人去幫你作,此處的代理就是這個意思。

clipboard.png

public interface Sourceable {
    void method();
}
public class Source implements Sourceable {
    @Override
    public void method() {
        System.out.println("thi original method!");
    }
}
public class Proxy implements Sourceable {
    private Source source;

    public Proxy() {
        super();
        this.source = new Source();
    }

    @Override
    public void method() {
        before();
        source.method();
        after();
    }

    private void after() {
        System.out.println("after proxy!");
    }

    private void before() {
        System.out.println("before proxy!");
    }
}

測試類:

public class ProxyTest {
    public static void main(String[] args) {
        Sourceable source = new Proxy();
        source.method();
    }
}

clipboard.png

應用場景:
若是要對原有的方法進行改進,此時有兩種辦法:
一、修改原有的方法來適應。這樣違反了「對擴展開放,對修改關閉」的原則。
二、採用一個代理類調用原有的方法,且對產生的結果進行控制。這種方法就是代理模式。
使用代理模式,能夠將功能劃分的更加清晰,有助於後期維護!

4、外觀(Facade)

爲了解決類與類之間的依賴關係,像spring同樣能夠將類和類之間的關係配置到配置文件中,而外觀模式就是將他們的關係放在一個Facade類中,下降了類之間的耦合度:(咱們以一個計算機的啓動過程爲例)

clipboard.png

public class CPU {
    public void startup(){
        System.out.println("cpu startup!");
    }

    public void shutdown(){
        System.out.println("cpu shutdown!");
    }
}
public class Memory {
    public void startup(){
        System.out.println("memory startup!");
    }

    public void shutdown(){
        System.out.println("memory shutdown!");
    }
}
public class Disk {
    public void startup(){
        System.out.println("disk startup!");
    }

    public void shutdown(){
        System.out.println("disk shutdown!");
    }
}
public class Computer {
    private CPU cpu;
    private Memory memory;
    private Disk disk;

    public Computer() {
        cpu = new CPU();
        memory = new Memory();
        disk = new Disk();
    }

    public void startup(){
        System.out.println("start the computer!");
        cpu.startup();
        memory.startup();
        disk.startup();
        System.out.println("start computer finished!");
    }

    public void shutdown(){
        System.out.println("begin to close the computer!");
        cpu.shutdown();
        memory.shutdown();
        disk.shutdown();
        System.out.println("computer closed!");
    }
}
public class User {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.startup();
        computer.shutdown();
    }
}

clipboard.png

5、橋接(Bridge)

把事物和其具體實現分開,使他們能夠各自獨立的變化。橋接的用意是: 將抽象化與實現化解耦,使得兩者能夠獨立變化,像咱們經常使用的 JDBC 橋 DriverManager 同樣,JDBC進行鏈接數據庫的時候,在各個數據庫之間進行切換,基本不須要動太多的代碼,甚至絲絕不用動,緣由就是 JDBC 提供統一接口,每一個數據庫提供各自的實現,用一個叫作數據庫驅動的程序來橋接就好了 :

clipboard.png
先定義接口:

public interface Sourceable {
    void method();
}

定義兩個實現類:

public class SourceSub1 implements Sourceable {
    @Override
    public void method() {
        System.out.println("this is the first sub!");
    }
}
public class SourceSub2 implements Sourceable {
    @Override
    public void method() {
        System.out.println("this is the second sub!");
    }
}

定義一個橋,持有 Sourceable 的一個實例:

public abstract class Bridge {
    private Sourceable source;

    public void method(){
        source.method();
    }

    public Sourceable getSource() {
        return source;
    }

    public void setSource(Sourceable source) {
        this.source = source;
    }
}
public class MyBridge extends Bridge {
    @Override
    public void method() {
        getSource().method();
    }
}

測試類:

public class BridgeTest {
    public static void main(String[] args) {
        Bridge bridge = new MyBridge();

        /*調用第一個對象*/
        Sourceable source1 = new SourceSub1();
        bridge.setSource(source1);
        bridge.method();

        /*調用第二個對象*/
        Sourceable source2 = new SourceSub2();
        bridge.setSource(source2);
        bridge.method();
    }
}

clipboard.png

這樣,就經過對 Bridge 類的調用,實現了對接口 Sourceable 的實現類 SourceSub1 和SourceSub2 的調用。接下來我再畫個圖,你們就應該明白了,由於這個圖是咱們 JDBC 鏈接的原理,有數據庫學習基礎的,一結合就都懂了。

clipboard.png

6、組合(Composite)

在處理相似樹形結構的問題時比較方便:

clipboard.png

public class TreeNode {
    private String name;
    private TreeNode parent;
    private Vector<TreeNode> children = new Vector<TreeNode>();

    public TreeNode(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public TreeNode getParent() {
        return parent;
    }

    public void setParent(TreeNode parent) {
        this.parent = parent;
    }

    //添加孩子節點
    public void add(TreeNode node){
        children.add(node);
    }

    //刪除孩子節點
    public void remove(TreeNode node){
        children.remove(node);
    }

    //取得孩子節點
    public Enumeration<TreeNode> getChildren(){
        return children.elements();
    }
}
public class Tree {
    TreeNode root = null;

    public Tree(String name){
        root = new TreeNode(name);
    }

    public static void main(String[] args) {
        Tree tree = new Tree("A");
        TreeNode nodeB = new TreeNode("B");
        TreeNode nodeC = new TreeNode("C");

        nodeB.add(nodeC);
        tree.root.add(nodeB);
        System.out.println("build the tree finished!");
    }
}

使用場景:將多個對象組合在一塊兒進行操做,經常使用於表示樹形結構中,例如二叉樹,數等。

7、享元(Flyweight)

實現對象的共享,即共享池,當系統中對象多的時候能夠減小內存的開銷,一般與工廠模式一塊兒使用。

clipboard.png

FlyWeightFactory 負責建立和管理享元單元,當一個客戶端請求時,工廠須要檢查當前對象池中是否有符合條件的對象,若是有,就返回已經存在的對象,若是沒有,則建立一個新對象,FlyWeight 是超類。一提到共享池,咱們很容易聯想到 Java 裏面的 JDBC 鏈接池,想一想每一個鏈接的特色,咱們不難總結出:適用於做共享的一些個對象,他們有一些共有的屬性,就拿數據庫鏈接池來講,url、driverClassName、username、password 及 dbname,這些屬性對於每一個鏈接來講都是同樣的,因此就適合用享元模式來處理,建一個工廠類,將上述相似屬性做爲內部數據,其它的做爲外部數據,在方法調用時,當作參數傳進來,這樣就節省了空間,減小了實例的數量。
看個例子:

clipboard.png

看下數據庫鏈接池的代碼:

public class ConnectionPool {
    
    private Vector<Connection> pool;
    
    /*公有屬性*/
    private String url = "jdbc:mysql://localhost:3306/test";
    private String username = "root";
    private String password = "root";
    private String driverClassName = "com.mysql.jdbc.Driver";

    private int poolSize = 100;
    private static ConnectionPool instance = null;
    Connection conn = null;

    /*構造方法,作一些初始化工做*/
    private ConnectionPool() {
        pool = new Vector<Connection>(poolSize);

        for (int i = 0; i < poolSize; i++) {
            try {
                Class.forName(driverClassName);
                conn = DriverManager.getConnection(url, username, password);
                pool.add(conn);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /* 返回鏈接到鏈接池 */
    public synchronized void release() {
        pool.add(conn);
    }

    /* 返回鏈接池中的一個數據庫鏈接 */
    public synchronized Connection getConnection() {
        if (pool.size() > 0) {
            Connection conn = pool.get(0);
            pool.remove(conn);
            return conn;
        } else {
            return null;
        }
    }
}

經過鏈接池的管理,實現了數據庫鏈接的共享,不須要每一次都從新建立鏈接,節省了數據庫從新建立的開銷,提高了系統的性能!

相關文章
相關標籤/搜索