[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);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
建议阅读本文档的方式 本文档提供详细的目录,建议大家使用电脑阅读。如果大家用手机阅读的话,可以下载一个不错的PDF阅读器,比如 很多人常用的福昕PDF阅读器。 本文档提供详细的目录,大家可以根据自己的实际需要选择自己薄弱的知识章节阅读。 前言 不论是校招还是社招都避免不了各种面试、笔试,如何去准备这些东西就显得格外重要。不论是笔试还是面试都是有 章可循的,我这个“有章可循”说的意思只是说应对技术面试是可以提前准备。 运筹帷幄之后,决胜千里之外!不打毫无准备的仗,我觉得大家可以先从下面几个方面来准备面试: 1. 自我介绍。(你可千万这样介绍:“我叫某某,性别,来自哪里,学校是那个,自己爱干什么”,记住:多说点简 历上没有的,多说点自己哪里比别人强!) 2. 自己面试中可能涉及哪些知识点、那些知识点是重点。 3. 面试中哪些问题会被经常问到、面试中自己改如何回答。(强烈不推荐背题,第一:通过背这种方式你能记住多 少?能记住多久?第二:背题的方式的学习很难坚持下去!) 4. 自己的简历该如何写。 “80%的offer掌握在20%的人手中” 这句话也不是不无道理的。决定你面试能否成功的因素中实力固然占有很大一部 分比例,但是如果你的心态或者说运气不好的话,依然无法拿到满意的 offer。运气暂且不谈,就拿心态来说,千万 不要因为面试失败而气馁或者说怀疑自己的能力,面试失败之后多总结一下失败的原因,后面你就会发现自己会越来 越强大。 另外,大家要明确的很重要的几点是: 1. 写在简历上的东西一定要慎重,这可能是面试官大量提问的地方; 2. 大部分应届生找工作的硬伤是没有工作经验或实习经历; 3. 将自己的项目经历完美的展示出来非常重要。 笔主能力有限,如果有不对的地方或者和你想法不同的地方,敬请雅正、不舍赐教。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值