Java设计模式

学习路线(掌握)

JAVA语法

Java基础

JVM

  • 类加载机制
  • 字节码执行机制
  • JVM内存模型
  • GC垃圾回收
  • JVM性能监控与故障定位
  • JVM调优

多线程

  • 并发编程的基础
  • 线程池
  • 并发容器
  • 原子类
  • JUC并发工具类### 数据结构和算法

数据结构

  • 字符串
  • 数组
  • 链表
  • 堆、栈、队列
  • 二叉树
  • 哈希

算法

  • 排序

  • 查找

  • 贪心

  • 分治

  • 动态规划

  • 回溯

计算机网络

  • ARP协议
  • IP、ICMP协议
  • TCP、UDP协议
  • DNS、HTTP/HTTPS协议
  • Session/Cookie

MySQL数据库

  • SQL语句的书写
  • SQL语句的优化
  • 事务、隔离级别
  • 索引

操作系统

  • 进程、线程
  • 并发、锁
  • 内存管理和调度
  • I/O原理

设计模式

  • 单例
  • 工厂
  • 代理
  • 策略
  • 模板方法
  • 观察者
  • 适配器
  • 责任链
  • 建造者

模式类别

  • 创建型模式:
    • 单例模式
    • 工厂模式
    • 抽象工厂模式
    • 建造者模式
    • 原型模式
  • 结构型模式:
    • 适配器模式
    • 桥接模式
    • 装饰模式
    • 组合模式
    • 外观模式
    • 享元模式
    • 代理模式
  • 行为型模式:
    • 模板方法模式
    • 命令模式
    • 迭代器模式
    • 观察者模式
    • 中介者模式
    • 备忘录模式
    • 解释器模式
    • 状态模式
    • 策略模式
    • 职责链模式
    • 访问者模式

单例模式

在JUC中有详细介绍

彻底玩转单例模式

工厂模式

作用:

  • 实现了创建者和调用者的分离
  • 详细分类:
    • 简单工厂模式
    • 工厂方法模式
    • 抽象工厂模式

OOP七大原则

  • 开闭原则: -个软件的实体应当对扩展开放,对修改关闭
  • 依赖倒转原则:要针对接口编程,不要针对实现编程
  • 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信

PBP/OBP/OOP的特征 PBP(基于过程):函数、域与生命周期的概念、函数重载、函数模板、异常处理、范型算法。 OBP(基于对象):类(接口)、类的生命周期、类模板。 OOP(面向对象):继承、多态。 三个概念步步深入,后者包括前者。其目的是实现软件开发的可重用性,解决软件危机(软件工程思想未引入之前)。 OOP不可避免设计原则的发展与设计模式的产生,这是OOP独具的特征决定的(正如前面所说,特征的产生是由其产生目的决定的)。

OOP的方法论(高内聚、低耦合、易复用) OOP的方法论--如何实现基于OOP特征的抽象?

OOP设计原则

OCP(Open-Closed Principle),开放封闭原则:软件实体应该扩展开放、修改封闭。 实现:合理划分构件,一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里;一种可变性不应当与另一个可变性混合在一起。

DIP(Dependency Inversion Principle),依赖倒置原则:摆脱面向过程编程思想中高层模块依赖于低层实现,抽象依赖于具体细节。OOP中要做到的是,高层模块不依赖于低层模块实现,二者都依赖于抽象;抽象不依赖于具体实现细节,细节依赖于抽象。 实现:应该通过抽象耦合的方式,使具体类最大可能的仅与其抽象类(接口)发生耦合;程序在需要引用一个对象时,应当尽可能的使用抽象类型作为变量的静态类型,这就是针对接口编程的含义。

LSP(Liskov Substitution Principle),Liskov替换原则:继承思想的基础。“只有当衍生类可以替换掉基类,软件单位的功能不会受到影响时,基类才真正被复用,而衍生类也才能够在基类的基础上增加新的行为。”

ISP(Interface Insolation Principle),接口隔离原则:接口功能单一,避免接口污染。 实现:一个类对另外一个类的依赖性应当是建立在最小的接口上的。使用多个专门的接口比使用单一的总接口要好。

SRP(Single Resposibility Principle),单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。 如果一个类的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会抑止这个类完成其他职责的能力。

CARP(Composite/Aggregate Reuse Principle),合成/聚合复用原则:设计模式告诉我们对象委托优于类继承,从UML的角度讲,就是关联关系优于继承关系。尽量使用合成/聚合、尽量不使用继承。 实现:在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,以整合其功能。

LoD(Law Of Demeter or Principle of Least Knowledge),迪米特原则或最少知识原则:就是说一个对象应当对其他对象尽可能少的了解。即只直接与朋友通信,或者通过朋友与陌生人通信。 朋友的定义(或关系): (1)当前对象本身。 (2)以参量的形式传入到当前对象方法中的对象。 (3)当前对象的实例变量直接引用的对象。 (4)当前对象的实例变量如果是一个聚集,那么聚集中的元素也都是朋友。 (5)当前对象所创建的对象。 实现: (1)在类的划分上,应当创建有弱耦合的类。类之间的耦合越弱,就越有利于复用。 (2)在类的结构设计上,每一个类都应当尽量降低成员的访问权限。一个类不应当public自己的属性,而应当提供取值和赋值的方法让外界间接访问自己的属性。 (3)在类的设计上,只要有可能,一个类应当设计成不变类。 (4)在对其它对象的引用上,一个类对其它对象的引用应该降到最低。 (5)尽量限制局部变量的有效范围.

核心本质:

  • 实例化对象不使用new,用工厂方法代替
  • 将选择实现类, 创建对象统一管理和控制。 从而将调用者跟我们的实现类解耦。

三种模式:

  • 简单工厂模式
    • 用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
  • 工厂方法模式
    • 用来生产同一等级结构中的固定产品(支持增加任意产品)
  • 抽象工厂模式
    • 围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。

XXXXFactory

简单工厂模式


public interface Car {
    void name();
}

public class Tesla implements Car {
    @Override
    public void name() {
        System.out.println("Tesla!!");
    }
}

public class WuLing implements Car{


    @Override
    public void name() {
        System.out.println("五菱宏光!");
    }
}

//简单工厂又叫做静态工厂,因为其中的方法都是静态的
//增加一个新的产品,如果你不修改代码,做不到!
//开闭原则
public class CarFactory {

    /*方法一:车工厂,去里面拿车*/
    public static Car getCar(String car) {
        if (car.equals("五菱")) {
            return new WuLing();
        } else if (car.equals("特斯拉")) {
            return new Tesla();
        } else {
            return null;
        }
    }
    //方法二
    public  static Car getWuling(){
        return new WuLing();
    }
    public static Car getTesla(){
        return new Tesla();
    }
}
public class Consumer {
    public static void main(String[] args) {
        /*买车
        接口,所有的实现类!我们要直到Car接口及其实现类
        * 我们买车的时候只需要去买某种车,而不需要关注有那些车
        *
        Car car = new WuLing();
        Car car2 = new Tesla();
        car.name();
        car2.name(); */
          /*使用工厂创建*/
        Car car1 = CarFactory.getCar("五菱");
    }
}

image-20200519230335065

工厂方法模式

public interface Car {
    void name();
}

public class Tesla implements Car {
    @Override
    public void name() {
        System.out.println("Tesla!!");
    }
}

public class WuLing implements Car {
    @Override
    public void name() {
        System.out.println("五菱宏光!");
    }
}

public interface CarFactor {
    Car getCar();
}
public class TeslaFactory implements CarFactor {
    @Override
    public Car getCar() {
        return new Tesla();
    }
}
public class WulingFactory implements  CarFactor {
    @Override
    public Car getCar() {
        return new WuLing();
    }
}
public class Consumer {
    public static void main(String[] args) {
        Car car = new WulingFactory().getCar();
        Car car1 = new TeslaFactory().getCar();
        System.out.println(car);
        System.out.println(car1);
    }
}

image-20200519230941248

结构复杂度: simple
代码复杂度 simple
编程复杂度: simple
管理上的复杂度 simple

根据设计原则:工厂方法模式! 根据实际业务:简单工厂模式!

小结:

  • 简单工厂模式(静态工厂模式)

    • 虽然某种程度上不符合设计原则,但实际使用最多!
  • 工厂方法模式

    • 不修改已有类的前提下,通过增加新的工厂类实现扩展。
  • 抽象工厂 模式

    • 不可以增加产品,可以增加产品族!

应用场景:

  • JDK中Calendar的getInstance方法
  • JDBC中的Connection对象的获取
  • Spring中IOC容器创建管理bean对象
  • 反射中Class对象的newInstance方法

抽象工厂模式

定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类

适用场景:

  • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
  • 强调一系列相关的产品对象(属于同一产品族) 一起使用创建对象需要大量的重复代码
  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现

优点:

  • 具体产品在应用层的代码隔离,无需关心创建的细节
  • 将一个系列的产品统一到一起创建

缺点:

  • 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
  • 增加了系统的抽象性和理解难度

image-20200519224144512

//手机产品接口
public interface IphoneProduct {
    void start();

    void shutdown();

    void callup();

    void sendsms();
}
//路由器接口
public interface IrouterProduct {

    void start() ;

    void shutdown();

    void openwifi();

    void setting();
}
public class XiaomiPhone implements IphoneProduct {
    @Override
    public void start() {
        System.out.println("开启小米手机");
    }
    @Override
    public void shutdown() {
        System.out.println("小米手机关机");
    }
    @Override
    public void callup() {
        System.out.println("小米手机打电话");
    }
    @Override
    public void sendsms() {
        System.out.println("小米手机发短信");
    }
}
public class XiaomiRouter implements IrouterProduct {
    @Override
    public void start() {
        System.out.println("小米路由器启动");
    }
    @Override
    public void shutdown() {
        System.out.println("小米路由器关闭");
    }
    @Override
    public void openwifi() {
        System.out.println("小米路由器打开wifi");
    }
    @Override
    public void setting() {
        System.out.println("小米路由器设置");
    }
}
public class HuaWeiPhone implements IphoneProduct {
    @Override
    public void start() {
        System.out.println("华为手机开启");
    }
    @Override
    public void shutdown() {
        System.out.println("华为手机关机");
    }
    @Override
    public void callup() {
        System.out.println("华为手机打电话");
    }
    @Override
    public void sendsms() {
        System.out.println("华为手机发短信");
    }
}

public class HuaWeiRouter implements IrouterProduct {
    @Override
    public void start() {
        System.out.println("华为路由器开启");
    }
    @Override
    public void shutdown() {
        System.out.println("华为路由器关闭");
    }
    @Override
    public void openwifi() {
        System.out.println("华为路由器开启wifi");
    }
    @Override
    public void setting() {
        System.out.println("华为路由器设置");
    }
}

//抽象产品工厂
public interface IProductFactory {
    //生产手机
    IphoneProduct iphoneProduct();

    //生产路由器
    IrouterProduct irouterProduct();
}
public class XiaomiFactory implements IProductFactory {
    @Override
    public IphoneProduct iphoneProduct() {
        return new XiaomiPhone();
    }
    @Override
    public IrouterProduct irouterProduct() {
        return new XiaomiRouter();
    }
}

public class HuaWeiFactory implements IProductFactory {
    @Override
    public IphoneProduct iphoneProduct() {
        return new HuaWeiPhone();
    }
    @Override
    public IrouterProduct irouterProduct() {
        return new HuaWeiRouter();
    }
}

public class Client {
    public static void main(String[] args) {
        System.out.println("=====================小米系类产品");
        
        XiaomiFactory xiaomiFactory = new XiaomiFactory();
        IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
        iphoneProduct.start();
        IrouterProduct irouterProduct = xiaomiFactory.irouterProduct();
        irouterProduct.start();

        System.out.println("==================华为系列产品");
        
        HuaWeiFactory huaWeiFactory = new HuaWeiFactory();
        IphoneProduct iphoneProduct1 = huaWeiFactory.iphoneProduct();
        iphoneProduct1.start();
        IrouterProduct irouterProduct1 = huaWeiFactory.irouterProduct();
        irouterProduct1.start();
    }
}

建造者模式

建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。

用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细 节隐藏起来)

例子:

  • 工厂(建造者模式) :负责制造汽车(组装过>程和细节在工厂内)
  • 汽车购买者(用户) :你只需要说出你需要的>型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的 (车轮、车门、>发动机、方向盘等等) )

既然是建造者模式,那么我们还是继续造房吧,其实我也想不到更简单的例子。假设造房简化为如下步骤: (1)地基(2) 钢筋工程(3)铺电线(4) 粉刷; “如果”要盖一座房子,首先要找一个建筑公司或工程 承包商(指挥者)。承包商指挥工人(具体建造者)过来造房子(产品) ,最后验收。

public abstract class Builder {

    abstract void buildA();//地基
    abstract void buildB();//钢筋工程
    abstract void buildC();//铺电线
    abstract void buildD();//粉刷
    //完工了
    abstract Product getProject();
}

@Setter
@Getter
@ToString
public class Product {

    private String buildA;
    private String buildB;
    private String buildC;
    private String buildD;

}

//具体的工人;具体的建造者
public class Worker extends Builder {
    //产品
    private Product product;
    public Worker(){
        //使用new创建,而不是通过参数传递
        product = new Product();
    }




    @Override
    void buildA() {
        product.setBuildA("地基");

    }

    @Override
    void buildB() {

        product.setBuildB("B");
    }

    @Override
    void buildC() {
        product.setBuildC("C");
    }

    @Override
    void buildD() {
        product.setBuildD("D");
    }

    @Override
    Product getProject() {
        return product;
    }
}
//指挥:核心。负责指挥构建一一个工程, 工程如何构建,由它决定
public class Director {

    public Product build(Builder builder){
        //指挥工人建房子
        builder.buildA();
        builder.buildB();
        builder.buildC();
        builder.buildD();
        return builder.getProject();
    }
}

public class Test {
    public static void main(String[] args) {

        //指挥
        Director director = new Director();
        //指挥 具体的工人完成产品
        Product build = director.build(new Worker());

        System.out.println(build);

    }
}

上面示例是Builder模式的常规用法,导演类Director在Builder模式中具有很重要的作用,它用于指导具 体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统 结构,可以把Director和抽象建造者进行结合。

通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认 实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂 产品

比如:比如麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产 品),然后出售给客户。比第一 种方式少了指挥者,主要是因为第二种方式把指挥者交给用户来操作,使得 产品的创建更加简单灵活。

public abstract class Builder {
    abstract Builder buildA(String msg);//汉堡
    abstract Builder buildB(String msg);//可乐
    abstract Builder buildC(String msg);//薯条
    abstract Builder buildD(String msg);//甜点
    //生产了
    abstract Product getProject();
}
//套餐
@Setter
@Getter
@ToString
public class Product {
    //默认值
    private String buildA="汉堡";
    private String buildB="可乐";
    private String buildC="薯条";
    private String buildD="甜点";
}
//既是工人,又是指挥者
public class worker extends Builder {
    Product product;
    public worker(){
        product = new Product();
    }


    @Override
    Builder buildA(String msg) {
        product.setBuildB(msg);

        return this;
    }

    @Override
    Builder buildB(String msg) {
        product.setBuildA(msg);

        return this;
    }

    @Override
    Builder buildC(String msg) {
        product.setBuildC(msg);

        return this;
    }

    @Override
    Builder buildD(String msg) {
        product.setBuildD(msg);

        return this;
    }

    @Override
    Product getProject() {
        return product;
    }
}
public class Test {

    public static void main(String[] args) {
        //服务员
        worker worker = new worker();
        //具有产品的默认值   在原来的基础上可以自由组合了,如果不组合,也有默认的套餐
        //Product project = worker.getProject();
        //链式编程  return this的功劳
        Product project = worker
                            .buildA("AAAAAA")
                            .getProject();
        System.out.println(project);

    }
}

优点:

  • 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
  • 将复杂产品的创建步骤分解在不同的方法中, 使得创建过程更加清晰
  • 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则”。

缺点:

  • 建造者模式所创建的产品一-般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一-定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

应用场景:

  • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
  • 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。

建造者与抽象工厂模式的比较:

  • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回-系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
  • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于- -步步构造一个复杂对象, 返回-一个完整的对象。
  • 如果将抽象工厂模式看成汽车配件生产工厂,生产- -个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车!

原型模式

克隆(在js的继承中有原型的概念)

/*
1. 实现一个接口
2. 重写一个方法
* **/
//Video:视频类
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class Video implements Cloneable{
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
/*客户端 :   克隆*/
public class BiliBili {

    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象
        Date date = new Date();

        Video v1 = new Video("hahah", date);

        System.out.println("v1====>"+v1);
        System.out.println("v1====>hash"+v1.hashCode());
        //v1可以克隆出v2
        Video v2 = (Video)v1.clone();
        System.out.println("v2====>"+v2);
        //克隆出来的对象是属性值一摸一样的(但是hashcode是不同的)
    }
}

image-20200520220932216

这样的克隆是浅克隆,不仅会将对象的值克隆过来也会将属性值的引用也克隆过来

当我们修改了v1属性date的时候会发现v2的date的值也会发生发了改变

深克隆方法一:改造clone方法

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class Video implements Cloneable{
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {

        //实现深克隆:或者使用序列化、反序列化
        Object obj = super.clone();
        Video v = (Video) obj;
        //将这个对象的属性也进行克隆
        v.createTime = (Date)this.createTime.clone();
        return obj;
    }
}

image-20200520221234630

在我们的spring框架中就体现了这点(Bean):单例模式、原型模式

原型模式+工厂模式===>new<=>原型模式clone

适配器模式

结构性模式作用:

从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。

//要被适配的类: 网线(许多笔记本电脑没有网线接口,因此需要usb转接)
public class Adaptee {
    public void request() {
        System.out.println("连接网线上网");
    }
}

/**
 * 1. 继承(类适配器,单继承)
 * 2. 组合(对象适配器,常用)
 * */
//真正的适配器!,需要连接USB,连接上网线
public class Adapter extends Adaptee implements NetToUsb{
    @Override
    public void handleRequest() {
        super.request();//可以上网了
    }
}
//客户端类:想上网,插不上网线
public class Computer {
    //我们的电脑需要连接上转接器才可以上网
    public void net(NetToUsb adapter) {//插上一个转接头
        //上网的具体实现~,找一个转接头
        adapter.handleRequest();
    }
    public static void main(String[] args) {
        //电脑、适配器、网线~
        Computer computer = new Computer();//电脑
        Adaptee adaptee = new Adaptee();//网线
        Adapter adapter = new Adapter();//适配器
        computer.net(adapter);
    }
}
public interface NetToUsb {
    //作用:处理请求,网线===》usb
    public void handleRequest();
}

使用组合

public interface NetToUsb {
    //作用:处理请求,网线===》usb
    public void handleRequest();
}
/**
 * 1. 继承(类适配器,单继承)
 * 2. 组合(对象适配器,常用)
 * */
//真正的适配器!,需要连接USB,连接上网线
@AllArgsConstructor
public class Adapter implements NetToUsb{
    private Adaptee adaptee;

    @Override
    public void handleRequest() {
        adaptee.request();//可以上网了
    }
}

//要被适配的类: 网线(许多笔记本电脑没有网线接口,因此需要usb转接)
public class Adaptee {
    public void request() {
        System.out.println("连接网线上网");
    }
}
//客户端类:想上网,插不上网线
public class Computer {
    //我们的电脑需要连接上转接器才可以上网
    public void net(NetToUsb adapter) {//插上一个转接头
        //上网的具体实现~,找一个转接头
        adapter.handleRequest();
    }
    public static void main(String[] args) {
        //电脑、适配器、网线~
        Computer computer = new Computer();//电脑
        Adaptee adaptee = new Adaptee();//网线
        Adapter adapter = new Adapter(adaptee);//适配器
        computer.net(adapter);
    }
}

将一个类的接口转换成客户希望的另外一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的 那些类可以在一起工作!

角色分析

  • 目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。
  • 需要适配的类: 需要适配的类或适配者类。
  • 适配器:通过包装-一个需要适配的对象,把原接口转换成目标对象!

image-20200520224059956

对象适配器优点

  • 一个对象适配器可以把多个不同的适配者适配到同一个目标
  • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据”里氏代换原则”,适配者的子类也可通过该适配器进行适配。

类适配器缺点

  • 对于Java、 C#等不支持多重类继承的语言,一-次最 多只能适配一个适配者类,不能同时适配多个适配者;
  • 在Java、 C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

适用场景

  • 系统需要使用一 -些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
  • 想创建一个可以重复使用的类,用于与- -些彼此之间没有太大关联的一-些类, 包括- -些可能在将来引进的类一起工作。

优先使用组合方法

InputStreamReader(InputStream)使用了适配器

springMVC中就使用了适配器:处理器映射器、处理器适配器

GUI中也含有了许多适配器

springboot自动装配中也使用了许多适配器

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层! [SpringAOP 和SpringMVC]

代理模式的分类:

  • 静态代理
  • 动态代理

image-20200520225045711

静态代理

直接找房东租房

//房东
public class Host {
    public void rent() {
        System.out.println("房东要出租房间了");
    }
}
//我:要去租房
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        host.rent();
    }
}

代理

//我:要去租房
public class Client {
    public static void main(String[] args) {
        //房东要出租房子
        Host host = new Host();
        //代理,中介帮房东租房子,代理角色一般会有一些符数操作
        Proxy proxy = new Proxy(host);
        //你不要面对房东直接去租房即可
        proxy.rent();

    }
}

//中介
@AllArgsConstructor
public class Proxy implements Rent{
    private Host host;
    @Override
    public void rent() {
        seeHouse();
        host.rent();
        fare();
        hetong();
    }
    //看房
    public void seeHouse(){
        System.out.println("中介带你看房");
    }
    //收中介费
    public void fare() {
        System.out.println("收中介费");
    }
    //签租赁合同
    public void hetong() {
        System.out.println("签租赁合同");
    }
}
//房东
public class Host {
    public void rent() {
        System.out.println("房东要出租房间了");
    }
}
//租房的接口
public interface Rent {
    public void rent();
}

角色分析:

  • 抽象角色: 一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人!

代理模式的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共也就就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!|

缺点:

  • 一个真实角色就会产生一个代理角色;代码量会翻倍开发效率会变低

代码步骤:

  1. 接口
  2. 真实角色
  3. 代理角色
  4. 客户端访问代理角色

加深理解

public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
public class UserServiceImpl implements  UserService {
    @Override
    public void add() {
        System.out.println("add了一个用户");
    }
    @Override
    public void delete() {
        System.out.println("delete了一个用户");
    }
    @Override
    public void update() {
        System.out.println("update了一个用户");
    }
    @Override
    public void query() {
        System.out.println("query了一个用户");
    }
}
@Setter
public class UserServiceProxy implements UserService {
    UserService userService = new UserServiceImpl();
    @Override
    public void add() {
        msg("add");
        userService.add();
    }
    @Override
    public void delete() {
        msg("delete");
        userService.delete();
    }
    @Override
    public void update() {
        msg("update");
        userService.update();
    }
    @Override
    public void query() {
        msg("query");
        userService.query();
    }
    public void msg(String msg) {
        System.out.println(msg);
    }
}
public class Client {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        //使用代理增加功能而不修改原有代码;只做增强,不做改变
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        userServiceProxy.setUserService(userService);
        userServiceProxy.add();
    }
}

动态代理

底层使用反射

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的!
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口—-JDK动态代理[我们在这里使用]
    • 基于类: cglib
    • java字节码实现: javasist

需要了解两个类: Proxy: 代理,InvocationHandler: 调用处理程序

InvocationHandler

image-20200521164515889

image-20200521164615618

image-20200521164641357

Proxy

image-20200521164815426

//租房的接口
public interface Rent {
    public void rent();
}
//房东
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要出租房间了");
    }
}
//等会我们使用这个类,自动生成代理类
@Setter
@Getter
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Rent rent;
    //生成得到代理类
    public Object getProxy() {
/**
 *
 * this.getClass().getClassLoader():加载的类的位置
 * rent.getClass().getInterfaces():要代理的接口
 * this:代表自己的InvocationHandler,调用InvocationHandler来处理
 * */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }
    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //动态代理的本质,就是使用反射机制来实现的
        Object invoke = method.invoke(rent, args);
        fare();
        return invoke;
    }
    public void seeHouse() {
        System.out.println("看房子啦~~");
    }
    public void fare() {
        System.out.println("收中介费了~~");
    }
}
public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色:现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(host);
        //获得代理类rent
        Rent rent = (Rent)pih.getProxy();//这里的就是动态生成的,我们并没有写
        rent.rent();
    }
}

万能代理

//等会我们使用这个类,自动生成代理类
@Setter
@Getter
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    //生成得到代理类
    public Object getProxy() {

/**
 *
 * this.getClass().getClassLoader():加载的类的位置
 * rent.getClass().getInterfaces():要代理的接口
 * this:代表自己的InvocationHandler,调用InvocationHandler来处理
 * */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制来实现的
        Object invoke = method.invoke(target, args);
        return invoke;
    }

}
public class Cleint {

    public static void main(String[] args) {
        //真实角色
        UserService userService = new UserServiceImpl();
        //代理角色,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //设置要代理的对象
        pih.setTarget(userService);
        UserService proxy = (UserService)pih.getProxy();
        proxy.query();
    }
}

动态代理的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共也就就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一一个接口, 一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!

桥接模式

桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式, 又称为柄体(Handle and Body)模式或接口(Interfce)模式。

image-20200521185119368

分析:这个场景中有两个变化的维度:品牌、类型。

image-20200521185405746

抽象出两个类:品牌类、类型类

//品牌
public interface Brand {
    void info();
}
public class Apple implements Brand {
    @Override
    public void info() {
        System.out.println("苹果");
    }
}

public class Lenovo implements Brand {

    @Override
    public void info() {
        System.out.println("联想");
    }
}

//电脑类型 抽象的电脑类
@AllArgsConstructor
@Setter
@Getter
public class Computer {

    //组合,品牌~桥

    protected Brand brand;

    public void info(){
        brand.info();//电脑自带的品牌

    }

}

class Desktop extends Computer {

    public Desktop(Brand brand) {

        super(brand);
    }

    @Override
    public void info() {
        super.info();
        System.out.println("台式电脑");
    }
}

class Laptop extends Computer {

    public Laptop(Brand brand) {

        super(brand);
    }

    @Override
    public void info() {
        super.info();
        System.out.println("笔记本点啊弄");
    }
}

public class Test {
    public static void main(String[] args) {

        //苹果笔记本
        Computer computer = new Laptop(new Apple());
        computer.info();

        //联想笔记本
        Computer computer1 = new Desktop(new Lenovo());
        computer1.info();
    }
}

image-20200521191040543

好处分析:

  • 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单- -职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本
  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度, 都不需要修改原有系统。符合开闭原则,就像一座桥, 可以把两个变化的维度连接起来!

劣势分析:

  • 桥接模式的弓|入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

最佳实践:

如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立-个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将- -个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。

  • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
  • 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

场景:

  • Java语言通过Java虚拟机实现了平台的无关性。
  • AWT中的Peer架构
  • JDBC驱动程序也是桥接模式的应用之一。