设计模式

设计模式

概述

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

单例模式

  • 在整个逻辑中一个对象只存在一份 => 在 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 serviceProviderList;
        private final AtomicInteger currentIndex = new AtomicInteger(0);

        public LoadBalancerServiceProvider(List 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 list = Arrays.asList();
        list.add("a");
        list.add("b");

        List decoratedList = new LoggingListDecorator(list);

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

    private static class LoggingListDecorator implements List {
        private List delegate;

        public LoggingListDecorator(List 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 commands = getCommandList();
        commands.forEach(Command::run);
    }

    private static List 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> map = new HashMap<>();

    public void subscribe(String channel, Listener listener) {
        List 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. 开闭原则 => 在面向对象编程领域中,开闭原则规定软件中的对象(类、模块、函数等等)应该对于扩展是开放的,但是对于修改是封闭的,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为

你可能感兴趣的:(设计模式)