[Java 面試突擊訓練] 常用設計模式

Adapter(適配器模式)

登場角色

  • Target(對象)
    該角色負責定義所需的方法
  • Client(請求者)
    該角色負責使用 Target 角色所定義的方法進行具體處理。
  • Adaptee(被適配)
    該角色持有既定的方法。
  • Adapter(適配)
    該角色的作用是使用 Adaptee 角色的方法來滿足 Target 角色的需求。

類圖

  • 類適配器模式的類圖(使用繼承)
    在這裏插入圖片描述
  • 對象適配器模式的類圖(使用委託)
    在這裏插入圖片描述

示例代碼

public class Banner {
    private String string;
    public Banner(String string) {
        this.string = string;
    }
    public void showWithParen() {
        System.out.println("(" + string + ")");
    }
    public void showWithAster() {
        System.out.println("*" + string + "*");
    }
}

public interface Print {
    public abstract void printWeak();
    public abstract void printStrong();
}

/* 類適配器模式(使用繼承) */
public class PrintBanner extends Banner implements Print {
    public PrintBanner(String string) {
        super(string);
    }
    public void printWeak() {
        showWithParen();
    }
    public void printStrong() {
        showWithAster();
    }
}

/* 對象適配器模式(使用委託) */
public class PrintBanner extends Print {
    private Banner banner;
    public PrintBanner(String string) {
        this.banner = new Banner(string);
    }
    public void printWeak() {
        banner.showWithParen();
    }
    public void printStrong() {
        banner.showWithAster();
    }
}

Template Method(模板方法模式)

登場角色

  • AbstractClass(抽象類或接口)
    該角色負責實現模板方法,同時負責聲明在模板方法中所使用到的抽象方法。
  • ConcreteClass(具體類)
    該角色負責具體實現 AbstractClass 角色中定義的抽象方法。

類圖

在這裏插入圖片描述

示例代碼

public abstract class AbstractDisplay { // 抽象類AbstractDisplay
    public abstract void open();        // 交給子類去實現的抽象方法(1) open
    public abstract void print();       // 交給子類去實現的抽象方法(2) print
    public abstract void close();       // 交給子類去實現的抽象方法(3) close
    public final void display() {       // 本抽象類中實現的display方法
        open();                         // 首先打開…
        for (int i = 0; i < 5; i++) {   // 循環調用5次print
            print();                    
        }
        close();                        // …最後關閉。這就是display方法所實現的功能
    }
}

public class CharDisplay extends AbstractDisplay {  // CharDisplay是AbstractDisplay的子類 
    private char ch;                                // 需要顯示的字符
    public CharDisplay(char ch) {                   // 構造函數中接收的字符被
        this.ch = ch;                               // 保存在字段中
    }
    public void open() {                            // 在父類中是抽象方法,此處重寫該方法  
        System.out.print("<<");                     // 顯示開始字符"<<"
    }
    public void print() {                           // 同樣地重寫print方法。該方法會在display中被重複調用
        System.out.print(ch);                       // 顯示保存在字段ch中的字符
    }
    public void close() {                           // 同樣地重寫close方法
        System.out.println(">>");                   // 顯示結束字符">>"
    }
}

public class StringDisplay extends AbstractDisplay {    // StringDisplay也是AbstractDisplay的子類 
    private String string;                              // 需要顯示的字符串
    private int width;                                  // 以字節爲單位計算出的字符串長度
    public StringDisplay(String string) {               // 構造函數中接收的字符串被
        this.string = string;                           // 保存在字段中
        this.width = string.getBytes().length;          // 同時將字符串的字節長度也保存在字段中,以供後面使用 
    }
    public void open() {                                // 重寫的open方法
        printLine();                                    // 調用該類的printLine方法畫線
    }
    public void print() {                               // print方法
        System.out.println("|" + string + "|");         // 給保存在字段中的字符串前後分別加上"|"並顯示出來 
    }
    public void close() {                               // close方法
        printLine();                                    // 與open方法一樣,調用printLine方法畫線
    }
    private void printLine() {                          // 被open和close方法調用。由於可見性是private,因此只能在本類中被調用 
        System.out.print("+");                          // 顯示錶示方框的角的"+"
        for (int i = 0; i < width; i++) {               // 顯示width個"-"
            System.out.print("-");                      // 組成方框的邊框
        }
        System.out.println("+");                        // /顯示錶示方框的角的"+"
    }
}

Factory Method(工廠方法模式)

登場角色

  • Product(產品)
    該角色屬於框架一方,定義了模式中實例所持有的接口,但具體的處理則由子類 ConcreteProduct 角色決定。
  • Creator(創建者)
    該角色屬於框架一方,負責生成 Product 角色的抽象類,但具體的處理則由子類 ConcreteCreator 角色決定。
  • ConcreteProduct(具體的產品)
    該角色屬於具體加工一方,它決定了具體的產品。
  • ConcreteCreator(具體的創建者)
    該角色屬於具體加工一方,它負責生成具體的產品。

類圖

在這裏插入圖片描述

示例代碼

/* 框架 */
public abstract class Product {
    public abstract void use();
}

public abstract class Factory {
    public final Product create(String owner) {
        Product p = createProduct(owner);
        registerProduct(p);
        return p;
    }
    protected abstract Product createProduct(String owner);
    protected abstract void registerProduct(Product product);
}

/* 具體加工 */
public class IDCard extends Product {
    private String owner;
    IDCard(String owner) {
        System.out.println("製作" + owner + "的ID卡。");
        this.owner = owner;
    }
    public void use() {
        System.out.println("使用" + owner + "的ID卡。");
    }
    public String getOwner() {
        return owner;
    }
}

public class IDCardFactory extends Factory {
    private List owners = new ArrayList();
    protected Product createProduct(String owner) {
        return new IDCard(owner);
    }
    protected void registerProduct(Product product) {
        owners.add(((IDCard)product).getOwner());
    }
    public List getOwners() {
        return owners;
    }
}

Singleton(單例模式)

登場角色

  • Singleton
    只有一個角色,它返回唯一示例的 static 方法,並總是返回同一個示例(在同一個 JVM 內)。

類圖

在這裏插入圖片描述

示例代碼

/* 使用靜態內部類實現 */
/* 優點:既保證線程安全,又達到延遲加載 */
public class Singleton{
    private static class SingletonHolder{
        public static Singleton INSTANCE = new Singleton();
    }
    private Singleton() {}
    public static Singleton newInstance(){
        return SingletonHolder.INSTANCE;
    }
}

Builder(建造者模式)

登場角色

  • Builder(建造者)
    該角色負責定義用於生成實例的接口。
  • ConcreteBuilder(具體的建造者)
    該角色是負責實現 Builder 角色的接口的類。
  • Director(監工)
    該角色負責使用 Builder 角色的接口來生成實例,同時它並不依賴於 ConcreteBuilder 角色。

類圖

在這裏插入圖片描述

示例代碼

public abstract class Builder {
    public abstract void makeTitle(String title);
    public abstract void makeString(String str);
    public abstract void makeItems(String[] items);
    public abstract void close();
}

public class HTMLBuilder extends Builder {
    private String filename;                                                        // 文件名
    private PrintWriter writer;                                                     // 用於編寫文件的PrintWriter
    public void makeTitle(String title) {                                           // HTML文件的標題
        filename = title + ".html";                                                 // 將標題作爲文件名
        try {
            writer = new PrintWriter(new FileWriter(filename));                     // 生成 PrintWriter
        } catch (IOException e) {
            e.printStackTrace();
        }
        writer.println("<html><head><title>" + title + "</title></head><body>");    // 輸出標題
        writer.println("<h1>" + title + "</h1>");
    }
    public void makeString(String str) {                                            // HTML文件中的字符串
        writer.println("<p>" + str + "</p>");                                       // 用<p>標籤輸出
    }
    public void makeItems(String[] items) {                                         // HTML文件中的條目
        writer.println("<ul>");                                                     // 用<ul>和<li>輸出
        for (int i = 0; i < items.length; i++) {
            writer.println("<li>" + items[i] + "</li>");
        }
        writer.println("</ul>");
    }
    public void close() {                                                           // 完成文檔
        writer.println("</body></html>");                                           // 關閉標籤
        writer.close();                                                             // 關閉文件
    }
    public String getResult() {                                                     // 編寫完成的文檔
        return filename;                                                            // 返回文件名
    }
}

public class TextBuilder extends Builder {
    private StringBuffer buffer = new StringBuffer();           // 文檔內容保存在該字段中
    public void makeTitle(String title) {                       // 純文本的標題
        buffer.append("==============================\n");      // 裝飾線
        buffer.append("『" + title + "』\n");                   // 爲標題添加『』
        buffer.append("\n");                                    // 換行
    }
    public void makeString(String str) {                        // 純文本的字符串
        buffer.append('■' + str + "\n");                       // 爲字符串添加■
        buffer.append("\n");                                    // 換行
    }
    public void makeItems(String[] items) {                     // 純文本的條目
        for (int i = 0; i < items.length; i++) {
            buffer.append(" ・" + items[i] + "\n");            // 爲條目添加・
        }
        buffer.append("\n");                                    // 換行
    }
    public void close() {                                       // 完成文檔
        buffer.append("==============================\n");      // 裝飾線
    }
    public String getResult() {                                 // 完成的文檔
        return buffer.toString();                               // 將StringBuffer變換爲String
    }
}

public class Director {
    private Builder builder;
    public Director(Builder builder) {              // 因爲接收的參數是Builder類的子類
        this.builder = builder;                     // 所以可以將其保存在builder字段中
    }
    public void construct() {                       // 編寫文檔
        builder.makeTitle("Greeting");              // 標題
        builder.makeString("從早上至下午");         // 字符串
        builder.makeItems(new String[]{             // 條目
            "早上好。",
            "下午好。",
        });
        builder.makeString("晚上");                 // 其他字符串
        builder.makeItems(new String[]{             // 其他條目
            "晚上好。",
            "晚安。",
            "再見。",
        });
        builder.close();                            // 完成文檔
    }
}

Strategy(策略模式)

登場角色

  • Strategy(策略)
    該角色負責決定實現策略所必需的接口。
  • ConcreteStrategy(具體的策略)
    該角色負責實現Strategy角色的接口,即負責實現具體的策略(戰略、方向、方法和算法)。
  • Context(上下文)
    該角色負責使用 Strategy,保存了 ConcreteStrategy 的實例,並使用 ConcreteStrategy 去實現需求。

類圖

在這裏插入圖片描述

示例代碼

public class Hand {
    public static final int HANDVALUE_GUU = 0;  // 表示石頭的值
    public static final int HANDVALUE_CHO = 1;  // 表示剪刀的值
    public static final int HANDVALUE_PAA = 2;  // 表示布的值
    public static final Hand[] hand = {         // 表示猜拳中3種手勢的實例
        new Hand(HANDVALUE_GUU),
        new Hand(HANDVALUE_CHO),
        new Hand(HANDVALUE_PAA),
    };
    private static final String[] name = {      // 表示猜拳中手勢所對應的字符串
        "石頭", "剪刀", "布",
    };
    private int handvalue;                      // 表示猜拳中出的手勢的值
    private Hand(int handvalue) {
        this.handvalue = handvalue;
    }
    public static Hand getHand(int handvalue) { // 根據手勢的值獲取其對應的實例
        return hand[handvalue];
    }
    public boolean isStrongerThan(Hand h) {     // 如果this勝了h則返回true
        return fight(h) == 1;
    }
    public boolean isWeakerThan(Hand h) {       // 如果this輸給了h則返回true
        return fight(h) == -1;
    }
    private int fight(Hand h) {                 // 計分:平0, 勝1, 負-1
        if (this == h) {
            return 0;
        } else if ((this.handvalue + 1) % 3 == h.handvalue) {
            return 1;
        } else {
            return -1;
        }
    }
    public String toString() {                  // 轉換爲手勢值所對應的字符串
        return name[handvalue];
    }
}

public interface Strategy {
    public abstract Hand nextHand();
    public abstract void study(boolean win);
}

public class WinningStrategy implements Strategy {
    private Random random;
    private boolean won = false;
    private Hand prevHand;
    public WinningStrategy(int seed) {
        random = new Random(seed);
    }
    public Hand nextHand() {
        if (!won) {
            prevHand = Hand.getHand(random.nextInt(3));
        }
        return prevHand;
    }
    public void study(boolean win) {
        won = win;
    }
}

public class ProbStrategy implements Strategy {
    private Random random;
    private int prevHandValue = 0;
    private int currentHandValue = 0;
    private int[][] history = {
        { 1, 1, 1, },
        { 1, 1, 1, },
        { 1, 1, 1, },
    };
    public ProbStrategy(int seed) {
        random = new Random(seed);
    }
    public Hand nextHand() {
        int bet = random.nextInt(getSum(currentHandValue));
        int handvalue = 0;
        if (bet < history[currentHandValue][0]) {
            handvalue = 0;
        } else if (bet < history[currentHandValue][0] + history[currentHandValue][1]) {
            handvalue = 1;
        } else {
            handvalue = 2;
        }
        prevHandValue = currentHandValue;
        currentHandValue = handvalue;
        return Hand.getHand(handvalue);
    }
    private int getSum(int hv) {
        int sum = 0;
        for (int i = 0; i < 3; i++) {
            sum += history[hv][i];
        }
        return sum;
    }
    public void study(boolean win) {
        if (win) {
            history[prevHandValue][currentHandValue]++;
        } else {
            history[prevHandValue][(currentHandValue + 1) % 3]++;
            history[prevHandValue][(currentHandValue + 2) % 3]++;
        }
    }
}

public class Player {
    private String name;
    private Strategy strategy;
    private int wincount;
    private int losecount;
    private int gamecount;
    public Player(String name, Strategy strategy) {         // 賦予姓名和策略
        this.name = name;
        this.strategy = strategy;
    }
    public Hand nextHand() {                                // 策略決定下一局要出的手勢
        return strategy.nextHand();
    }
    public void win() {                 // 勝
        strategy.study(true);
        wincount++;
        gamecount++;
    }
    public void lose() {                // 負
        strategy.study(false);
        losecount++;
        gamecount++;
    }
    public void even() {                // 平
        gamecount++;
    }
    public String toString() {
        return "[" + name + ":" + gamecount + " games, " + wincount + " win, " + losecount + " lose" + "]";
    }
}

Proxy(代理模式)

登場角色

  • Subject(主體)
    該角色定義了使 Proxy 和 RealSubject 之間具有一致性的接口。
  • Proxy(代理人)
    該角色會盡量處理來自 Client 的請求。只有當自己不能處理時,它纔會將工作交給 RealSubject。
  • RealSubject(實際的主體)
    該角色會在 Proxy 無法勝任工作時出場。
  • Client(請求者)
    使用 Proxy 模式的角色。

類圖

在這裏插入圖片描述

示例代碼

public interface Printable {
    public abstract void setPrinterName(String name);   // 設置名字
    public abstract String getPrinterName();            // 獲取名字
    public abstract void print(String string);          // 顯示文字(打印輸出)
}

public class Printer implements Printable {
    private String name;
    public Printer() {
        heavyJob("正在生成Printer的實例");
    }
    public Printer(String name) {                   // 構造函數
        this.name = name;
        heavyJob("正在生成Printer的實例(" + name + ")");
    }
    public void setPrinterName(String name) {       // 設置名字
        this.name = name;
    }
    public String getPrinterName() {                // 獲取名字
        return name;
    }
    public void print(String string) {              // 顯示帶打印機名字的文字
        System.out.println("=== " + name + " ===");
        System.out.println(string);
    }
    private void heavyJob(String msg) {             // 重活
        System.out.print(msg);
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            System.out.print(".");
        }
        System.out.println("結束。");
    }
}

public class PrinterProxy implements Printable {
    private String name;            // 名字
    private Printer real;           // “本人”
    public PrinterProxy() {
    }
    public PrinterProxy(String name) {      // 構造函數
        this.name = name;
    }
    public synchronized void setPrinterName(String name) {  // 設置名字
        if (real != null) {
            real.setPrinterName(name);  // 同時設置“本人”的名字
        }
        this.name = name;
    }
    public String getPrinterName() {    // 獲取名字
        return name;
    }
    public void print(String string) {  // 顯示
        realize();
        real.print(string);
    }
    private synchronized void realize() {   // 生成“本人”
        if (real == null) {
            real = new Printer(name);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章