设计模式

设计模式

概述

  • 人们在长久的生产实践中总结出来的一套方法论
  • 提高开发效率
  • 降低维护成本

单例模式

  • 在整个逻辑中一个对象只存在一份 => 在 JVM 中只产生一个实例
  • case => Runtime 存放和 Java 运行相关的一些东西
    => private static 创建单例
  • case => Spring 中的 Bean 都是单例的 => DefaultSingletonBeanRegistry => ConcurrentHashMap + 双锁检测
  • case => MyBatis 中的 ErrorContext => 每一个线程有一个单例
public class World {
    // 当前单例问题 => 1. 反射  2. 反序列化  3. 需要在类加载的时候就初始化
    // public static World SINGLETON_WORLD = new World();

    private static World SINGLETON_WORLD;

    public static World getInstance() {
        if (SINGLETON_WORLD == null) {
            // 问题:多线程的情况下可能会同时进入
            SINGLETON_WORLD = new World();
        }
        return SINGLETON_WORLD;
    }

    // 双锁检测,在工作中上述两种情况就能够满足了
    public static World getInstance() {
        if (SINGLETON_WORLD == null) {
            synchronized (World.class) {
                if (SINGLETON_WORLD == null) {
                    SINGLETON_WORLD = new World();
                }
            }
        }

        return SINGLETON_WORLD;
    }

    private World() {
    }
}

// Effective Java 推荐 => enum 由虚拟机保证 SINGLETON_INSTANCE 只有一份
// enum 默认继承 Enum 类,不能再继承其他类,但是可以实现接口
public enum Singleton {
    SINGLETON_INSTANCE;
}

工厂方法/抽象工厂方法

  • 使用一个工厂方法来创建新的对象 => Lists.newArrayList()
  • 使用一个抽象的工厂来生产抽象的产品 => LogFactory(MyBatis)
  • 构造器缺点
    1. 没有具体的业务含义
    2. 不能返回 null
    3. 不能返回子类,只能返回这个对象本身
public class Car {
    private Car(String name) {
    }

    private Car(String name, String color) {
    }

    // 工厂方法 => 
    //      1. 有业务倾向的名字  
    //      2. 构造器不能 return null,工厂方法可以,更加灵活  
    //      3. 可以将构造器封装起来,外界都通过工厂方法去获取实例  
    //      4. 可以根据传入的参数进行一些业务逻辑处理,case:返回 Car 的子类
    public static Car ofName(String name) {
        if (name == null) {
            return null;
        }
    }

    public static void main(String[] args) {
        new Car("a");
        Car.ofName("a");
    }
}

适配器模式 Adapter

  • 作为两个不兼容的接口之间的桥梁
  • case => Spring HandleAdapter

建造者模式

  • 隐藏创建对象的建造过程和细节
  • 用户不知道对象的建造过程和细节,仍可直接创建复杂的对象
  • case => HttpClientBuilder | CacheBuilder(guava)
public class Book {
    private String preface;
    private String head;
    private String content;
    private String thanks;

    public static final class BookBuilder {
        private String preface;
        private String head;
        private String content;
        private String thanks;

        private BookBuilder() {
        }

        public static BookBuilder aBook() {
            return new BookBuilder();
        }

        public BookBuilder withPreface(String preface) {
            this.preface = preface;
            return this;
        }

        public BookBuilder withHead(String head) {
            this.head = head;
            return this;
        }

        public BookBuilder withContent(String content) {
            this.content = content;
            return this;
        }

        public BookBuilder withThanks(String thanks) {
            this.thanks = thanks;
            return this;
        }

        public Book build() {
            Book book = new Book();
            book.head = this.head;
            book.preface = this.preface;
            book.thanks = this.thanks;
            book.content = this.content;
            return book;
        }
    }

    public static void main(String[] args) {
        Book book = BookBuilder.aBook().withPreface("preface")
                .withHead("Head")
                .withContent("content")
                .build();
    }
}

代理模式

  • 为其他对象提供一种代理以控制对这个对象的访问
  • case => Remote Procedure Call 框架(dubbo)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class ProxyPattern {
    public static void main(String[] args) {
        ServiceProvider serviceProvider = (ServiceProvider) Proxy.newProxyInstance(ProxyPattern.class.getClassLoader(),
                // 动态代理
                new Class[]{ServiceProvider.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 动态的,可以灵活的根据业务需求去处理
                        return null;
                    }
                }
        );
        serviceProvider.sayHello("Hi!");
    }

    static class LoadBalancerServiceProvider implements ServiceProvider {
        // 静态代理
        private final List<ServiceProvider> serviceProviderList;
        private final AtomicInteger currentIndex = new AtomicInteger(0);

        public LoadBalancerServiceProvider(List<ServiceProvider> serviceProviderList) {
            this.serviceProviderList = serviceProviderList;
        }

        @Override
        public String sayHello(String name) {
            ServiceProvider currentServiceProvider = serviceProviderList.get(currentIndex.getAndAdd(1) % serviceProviderList.size());
            return currentServiceProvider.sayHello(name);
        }
    }
}

interface ServiceProvider {
    String sayHello(String name);
}

装饰器模式

  • 向一个现有的对象添加新的功能,同时又不改变其结构
  • case => Collections.synchronizedList | Collections.unmodifiableList
  • case => Mybatis Cache => 大部分的实现都是在 org.apache.ibatis.cache.decorators 包里面,说明都是装饰器模式的实现
import java.util.Arrays;

public class Decorator {
    public static void main(String[] args) {
        // 需求 => 监控 list 所有的修改操作,当修改的时候打印日志
        List<String> list = Arrays.asList();
        list.add("a");
        list.add("b");

        List<String> decoratedList = new LoggingListDecorator(list);

        // 装饰可以嵌套 => 此时会打印出两条日志
        List<String> decoratedList2 = new LoggingListDecorator(decoratedList);
    }

    private static class LoggingListDecorator implements List<String> {
        private List<String> delegate;

        public LoggingListDecorator(List<String> delegate) {
            this.delegate = delegate;
        }

        // 之后所有覆盖的方法都调用 delegate 相应的方法
    }
}

组合模式

  • 是用于把一组相似的对象当做一个单一的对象
  • 特征 => 提供一个统一的接口,使得在系统中的一个整体和部分都实现了这个接口
  • 需求 => 写一个计算器
public class Calculator {
    public static void main(String[] args) {
        // 1 + 2 + 3 + 4
        Expression expression = new AddExpression(new NumberExpression(1),
                new AddExpression(new NumberExpression(2),
                        new AddExpression(new NumberExpression(3), new NumberExpression(4))
                )
        );

        System.out.println(expression.evaluate());
        //    +
        //  1   +
        //    2   +
        //      3   4
    }

    static class AddExpression implements Expression {
        Expression left;
        Expression right;

        public AddExpression(Expression left, Expression right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public int evaluate() {
            return left.evaluate() + right.evaluate();
        }
    }

    static class NumberExpression implements Expression {
        private final int value;

        public NumberExpression(int value) {
            this.value = value;
        }

        @Override
        public int evaluate() {
            return value;
        }
    }
}

interface Expression {
    int evaluate();
}

模板方法

  • 一个抽象类公开定义了执行它的方法的方式/模板,子类可以按需要重写方法实现
  • 类似于生命周期,在一定的生命周期可以绑定不同的事件
  • 一些业务方法结构相似,但是内容不同,将结构抽象化
  • case => Spring AbstractApplicationContext#refresh

策略模式

  • 一个类的行为或其算法可以在运行时更改
  • case => 线程池 RejectedExecutionHandler
// 1. 打折的逻辑和具体的业务逻辑完全分离
// 2. 如果策略变更,之前的代码都不需要更改,只需要添加一个类
// 3. 代码编译的时候,具体的策略可能还不存在
public class Discount {
    public double discount(double amount, DiscountStrategy discountStrategy) {
        return discountStrategy.calculate(amount);
//    public double discount(double amount, String customerType) {
//        switch (customerType) {
//            case "VIP":
//                return amount * 0.8;
//            case "VIPAndCoupon":
//                return amount * 0.8 - 100;
//            default:
//                return amount;
//        }
    }
}

interface DiscountStrategy {
    double calculate(double value);
}

class VIPStrategy implements DiscountStrategy {
    @Override
    public double calculate(double value) {
        return value * 0.8;
    }
}

class VIPAndCouponStrategy implements DiscountStrategy {
    @Override
    public double calculate(double value) {
        return value * 0.8 - 100;
    }
}

class NormalStrategy implements DiscountStrategy {
    @Override
    public double calculate(double value) {
        return value;
    }
}

享元模式 Flyweight

  • 使用共享对象减少创建对象的数量 => 减少内存占用和提高性能 => 特征:
    1. 不可变的
    2. 频繁创建的
    3. 小对象
  • case => String.intern() | Integer.valueOf() => IntegerCache
public class Flyweight {
    public static void main(String[] args) {
        // i 和 j 两个对象是一模一样的 => 因为有缓存 -128 - 127 都被缓存下来了
        Integer i = 1;
        Integer j = 1;
    }
}

外观模式/门面模式 Facade

  • 向外界暴露一个统一的接口(外观)=> 调用者不需要关心其中的细节
public class ShitMountains {
    // 遗留系统,有很多很多逻辑细节
    private void foo() {
    }

    private void bar() {
    }

    private void baz() {
    }
}

class FacadeForShitMountains {
    // 以后只需要和 Facade 打交道,调用者并不知道具体的细节
    // 之后其他调用者只调用 Facade 提供的接口
    // 如果想更换遗留系统,只需要将 Facade 更新实现即可
    public process() {
        ShitMountains shitMountains = new ShitMountains();
        shitMountains.foo();
        shitMountains.bar();
    }
}

class System {
    public process() {
        FacadeForShitMountains facadeForShitMountains = new FacadeForShitMountains();
        facadeForShitMountains.process();

//        ShitMountains shitMountains = new ShitMountains();
//        shitMountains.foo();
//        shitMountains.bar();
    }
}

命令模式 Command

  • 命令的调用者与命令的具体实现解耦
  • 命令的调用者不关系命令的具体实现
  • case => 线程池 -> 一个 Runnable 本身就是一个命令
import java.util.List;

public class Commander {
    public static void main(String[] args) {
        // 和具体的命令耦合在一起
//        Eat eat = new Eat();
//        eat.run();
//
//        Sleep sleep = new Sleep();
//        sleep.run();

        List<Command> commands = getCommandList();
        commands.forEach(Command::run);
    }

    private static List<Command> getCommandList() {
        // 可以从其他地方获取命令,如 Eat 或 Sleep
        return null;
    }
}

interface Command {
    // Command 就是一个 Runnable
    void run();
}

class Eat implements Command {
    @Override
    public void run() {
        System.out.println("eat!");
    }
}

class Sleep implements Command {
    @Override
    public void run() {
        System.out.println("sleep!");
    }
}

责任链模式

  • 将请求沿着所有处理者组成的链发送
  • 每个处理者都可以决定自己是否处理,或是否继续处理
  • 实现请求和处理者之间的解耦
  • case => Servlet Filter | Redux 的中间件

观察者模式/发布订阅模式

  • 消息的发布者和订阅者互相不知道对方的存在,双方都可以灵活地变化
  • case => EventBus
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RadioStation {
    public static void main(String[] args) {
        EventBus eventBus = EventBus.INSTANCE;
        Car car = new Car(eventBus);
        car.subscribe("100.1");
        eventBus.publish("100.1", "ring!");
    }
}

interface Listener {
    void processMessage(String message);
}

enum EventBus {
    INSTANCE;

    final Map<String, List<Listener>> map = new HashMap<>();

    public void subscribe(String channel, Listener listener) {
        List<Listener> listeners = map.getOrDefault(channel, new ArrayList<>());
        listeners.add(listener);
        map.put(channel, listeners);
    }

    public void publish(String channel, String message) {
        map.getOrDefault(channel, new ArrayList<>()).forEach(listener -> listener.processMessage(message));
    }
}

class Car implements Listener {
    EventBus eventBus;

    public Car(EventBus eventBus) {
        this.eventBus = eventBus;
    }

    public void subscribe(String channel) {
        eventBus.subscribe(channel, this);
    }

    @Override
    public void processMessage(String message) {
        System.out.println(message);
    }
}

迭代器模式

  • 将集合对象和遍历的行为进行分离
  • 客户端不需要依赖集合对象的内部表示即可遍历之
  • case => Iterator
import java.util.Iterator;

public class IteratorPattern {
    public static void main(String[] args) {
        ArrayList list1 = new ArrayList();
//        for (Object element : list1.elements) {
//            System.out.println(element);
//        }
        list1.forEach(System.out::println);

        LinkedList list2 = new LinkedList();
//        Node currentNode = list2.head;
//        while (currentNode != null) {
//            System.out.println(currentNode.value);
//            currentNode = currentNode.next;
//        }
        list2.forEach(System.out::println);
    }
}

class ArrayListIterator implements Iterator {
    int current = 0;
    Object[] elements;

    public ArrayListIterator(Object[] elements) {
        this.elements = elements;
    }

    @Override
    public boolean hasNext() {
        return current < elements.length;
    }

    @Override
    public Object next() {
        return elements[current++];
    }
}

class ArrayList implements Iterable {
    Object[] elements;

    @Override
    public Iterator iterator() {
        return new ArrayListIterator(elements);
    }
}

class LinkedListIterator implements Iterator {
    Node head;

    public LinkedListIterator(Node head) {
        this.head = head;
    }

    @Override
    public boolean hasNext() {
        return head.next != null;
    }

    @Override
    public Object next() {
        Node next = head.next;
        head = next;
        return next;
    }
}

class LinkedList implements Iterable {
    Node head;

    @Override
    public Iterator iterator() {
        return new LinkedListIterator(head);
    }
}

class Node {
    Object value;
    Node next;
}

访问者模式 Visitor

  • 适用于结构固定的数据
  • 将访问它的操作分离出来,使得访问它的行为与具体的数据相分离
  • case => ASTVisitor => 抽象语法树
// 给定一个 Java 文件,返回这个 Java 文件中所有的类定义

知识点

  1. 开闭原则 => 在面向对象编程领域中,开闭原则规定软件中的对象(类、模块、函数等等)应该对于扩展是开放的,但是对于修改是封闭的,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容