Java常用设计模式

  • 一.创建型模式
    • 1.单例模式
      • 1.1 饿汉式
      • 1.2 懒汉式
    • 2.工厂模式
      • 2.1 简单工厂模式
      • 2.2 工厂方法模式
      • 2.3.抽象工厂模式
    • 3.建造者模式
      • 3.1 概述
      • 3.2 结构
      • 3.3 优缺点
      • 3.4 使用场景
    • 4.原型模式
  • 二.结构型模式
    • 1.适配器模式
    • 2.代理模式(Proxy pattern)
      • 2.1 概述
      • 2.2 结构
      • 2.1 静态代理
      • 2.2 动态代理
        • 2.2.1 动态代理类Proxy
    • 3.桥接模式
    • 4.装饰模式
    • 5.组合模式
    • 6.外观模式
    • 7.享元模式
  • 行为型模式

一.创建型模式

1.单例模式

(1)核心作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
(2)常见使用场景
①Windows的Task Manager(任务管理器)就是很典型的单例模式
②Windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例
③项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取
④网站的计数器,一般也是采用单例模式实现,否则难以同步。
⑤应用程序的日志应用,一般都采用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加
⑥数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源
⑦操作系统的文件系统,也是单例模式实现的具体例子,一个操作系统只能有一个文件系统
⑧Application也是单例的典型应用(Servlet编程中会涉及到)
⑨在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理
⑩在servlet编程中,每个Servlet也是单例
⑪在SpringMVC框架/strus1框架中,控制器对象也是单例的
(3)单例模式的优点
①由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
②单例模式可以在系统设置全局的访问点,优化共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
(4)常见的五种单例模式实现方式
主要:
①饿汉式(线程安全,调用效率高。不能延时加载)
②懒汉式(线程安全,调用效率不高。可以延时加载)
其他:
①双重检锁式(由于JVM底层内部模型原因,偶尔会出现问题。不建议使用)
②静态内部类式(线程安全,调用效率高。但是,可以延时加载)
③枚举单例(线程安全,调用效率高,不能延时加载)

1.1 饿汉式

//饿汉式单例模式
public class SingletonDemo1 {
    //    类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然线程安全
    private static SingletonDemo1 instance = new SingletonDemo1();
    private SingletonDemo1() {//私有化构造器
    }
    //   方法没有同步,调用效率高
    public static SingletonDemo1 getInstance() {
        return instance;
    } }

1.2 懒汉式

//懒汉式单例设计模式
public class SingletonDemo2 {
    //    类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
    private static SingletonDemo2 s;
    private SingletonDemo2() {
    }
    //方法同步,调用效率低
    public static synchronized SingletonDemo2 getInstance() {
        if (s == null) {
            s = new SingletonDemo2();
        }
        return s;
    } }

2.工厂模式

(1)实现了创建者和调用者的分离
(2)分类:
①简单工厂模式
用来生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码)
②工厂方法模式
用来生产同一等级结构中的固定产品(支持增加任意产品)
③抽象工厂模式
用来生产不同产品族的全部产品(对于增加新的产品,无能为力;支持增加产品族)
(3)面向对象设计的基本原则
①OCP(开闭原则):一个软件的实体应当对扩展开放,对修改关闭
②DIP(依赖倒转原则):要针对接口编程,不要针对实现编程
③LoD(迪米特法则):只与你直接的朋友通信,而避免和陌生人通信
(3)核心本质
①实例化对象,用工厂方法代替new操作
②将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦
(4)工厂模式要点
①简单工厂模式(静态工厂模式)
虽然某种程度不符合设计原则,但实际使用最多
②工厂方法模式
不修改已有类的前提下,通过增加新的工厂类来实现扩展
③抽象工厂模式
不可以增加产品,可以增加产品族
(5)应用场景
①JDK中Calendar的getInstance方法
②JDBC中Connection对象的获取
③Hibernate中SessionFactory创建Session
④Spring中IOC容器创建管理bean对象
⑤XML解析时的DocumentBuilderFactory创建解析器对象
⑥反射中Class对象的newInstance()

2.1 简单工厂模式

(1)要点
①简单工厂模式也叫静态工厂模式,工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的
对象实例
②对于增加新的产品无能为力!不修改代码的话,是无法扩展的。
(2)实现代码

//接口Car
public interface Car {
    void run();
}
// Audi类
public class Audi implements Car{
    @Override
    public void run() {
        System.out.println("奥迪在跑");
    } }
// Byd类
public class Byd implements Car {
    @Override
    public void run() {
        System.out.println("比亚迪在跑");
    } }
//CarFactory工厂
public class CarFactory {
//    方法1
    public static Car createCar(String type) {
        if ("奥迪".equals(type)) {
            return new Audi();
        } else if ("比亚迪".equals(type)) {
            return new Byd();
        } else {
            return null;
        } }
//    方法2
//    public static Car createAudi(){
//        return new Audi();
//    }
//    public static Car createByd(){
//        return new Byd();
//    } }

// 测试
public class Client {
    public static void main(String[] args) {
        Car c1 = CarFactory.createCar("奥迪");
        Car c2 = CarFactory.createCar("比亚迪");
        c1.run();
        c2.run();
    }
}

2.2 工厂方法模式

(1)要点
①为了避免简单工厂模式的缺点,不完全满足OCP
②工厂模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)
工厂类,而工厂方法模式一组实现了相同接口的工厂类

(2)代码

//接口Car
public interface Car {
    void run();
}
// Audi类
public class Audi implements Car{
    @Override
    public void run() {
        System.out.println("奥迪在跑");
    } }
// Byd类
public class Byd implements Car {
    @Override
    public void run() {
        System.out.println("比亚迪在跑");
    } }

//CartFactory工厂接口
public interface CarFactory {
    Car createCar();
}
//AudiFactory工厂类
public class AudiFactory implements CarFactory{
    @Override
    public Car createCar() {
        return new Audi();
    } }
//BydFactory工厂类
public class BydFactory implements CarFactory{
    @Override
    public Car createCar() {
        return new Byd();
    } }
//测试
public class Client {
    public static void main(String[] args) {
        Car c1 = new AudiFactory().createCar();
        Car c2 = new BydFactory().createCar();
        c1.run();
        c2.run();
    } }

(3)简单工厂模式和工厂方法模式比较
①结构复杂度
从这个角度比较,显然简单工厂模式要占优。简单工厂模式只需要一个工厂类,而工厂方法模式的工厂类随着产品类
的个数增加而增加,这个无疑会使类的个数越来越多,从而增加结构的复杂程度
②代码复杂度
代码复杂度和结构复杂度是一对矛盾,既然简单工厂模式在结构方面相对简洁,那么它在代码方面肯定是比工厂方法
模式复杂的了。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类
只完成单一任务,代码简洁
③客户端编程难度
工厂方法模式虽然在工厂类结构中引入了接口从而满足OCP,但是在客户端编码中需要对工厂类进行实例化。而简单工
厂模式的工厂类是个静态类,在客户端无需实例化。而简单工厂模式的工厂类是个静态类,在客户端无需实例化,这无疑
是个吸引人的优点。
④管理上的难度
这是个关键的问题
1.我们先谈扩展,众所周知,工厂方法模式完全满足OCP,即它有非常良好的扩展性。那是否就说明了简单工厂模式就没有扩展
性呢?答案是否定的。简单工厂模式同样具备良好的扩展性——扩展的时候仅需要修改少量的代码(修改工厂类的代码)就可以
满足扩展性的要求了。尽管这没有完全满足OCP,但我们不需要太拘泥于设计理念,要知道,sun提供的Java官方工具包中也有
很多没有满足OCP的例子。
2.然后我们从维护性的角度分析。假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个
产品类的时候,对工厂类的修改会变得相当麻烦。反而简单工厂没有这些麻烦,当多个产品类需要修改时,简单工厂模式仍然
仅仅需要修改唯一的工厂类
根据设计理念建议:工厂方法模式。但实际上,我们一般都用简单工厂模式

2.3.抽象工厂模式

(1)要点
①用来生产不同产品族的全部产品(对于增加新的产品,无能为力;支持增加产品族)
②抽象工厂模式是工厂模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的
解决方式。

(2)代码

//Engine接口
public interface Engine {
    void run();
    void start();
}
//LuxuryEngine类
class LuxuryEngine implements Engine {
    @Override
    public void run() {
        System.out.println("转的快");
    }
    @Override
    public void start() {
        System.out.println("启动快!可以自动启停");
    } }
   // LowEngine类
class LowerEngine implements Engine {
    @Override
    public void run() {
      System.out.println("转的慢");
    }
    @Override
    public void start() {
        System.out.println("启动慢!");
    } }
   //Seat接口
public interface Seat {
    void message();
}
//LuxurySeat类
class LuxurySeat implements Seat {
    @Override
    public void message() {
        System.out.println("可以自动按摩");
    } }
 //LowSeat类
class LowSeat implements Seat {
    @Override
    public void message() {
        System.out.println("不能自动按摩");
    } }
  
//Tyre接口
public interface Tyre {
    void revolve();
}
//LuxuryTyre类
class LuxuryTyre implements Tyre{
    @Override
    public void revolve() {
        System.out.println("旋转不磨损");
    } }
//LowTyre类
class LowTyre implements Tyre{
    @Override
    public void revolve() {
        System.out.println("容易磨损");
    } }

//CartFactory工厂接口
public interface CartFactory {
    Engine createEngine();
    Seat createSeat();
    Tyre createTyre();
}

//LuxuryFactory工厂实现类
public class LuxuryCarFactory implements CartFactory {
    @Override
    public Engine createEngine() {
        return new LuxuryEngine();
    }
    @Override
    public Seat createSeat() {
        return new LuxurySeat();
    }
    @Override
    public Tyre createTyre() {
        return new LuxuryTyre();
    } }

//LowCarFactory工厂实现类
public class LowCarFactory implements CartFactory{
    @Override
    public Engine createEngine() {
        return new LowerEngine();
    }
    @Override
    public Seat createSeat() {
        return new LowSeat();
    }
    @Override
    public Tyre createTyre() {
        return new LowTyre();
    } }
//测试
public class Client {
    public static void main(String[] args) {
        LuxuryCarFactory factory = new LuxuryCarFactory();
        Engine e = factory.createEngine();
        e.run();
        e.start();
    } }

3.建造者模式

3.1 概述

将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示
(1)分离了部件的构造(由Builder来构造)和装配(由Director装配)。 从而可以构造出复杂的对象。这个模式
适用于:某个对象的构建过程复杂的情况。
(2)由于实现了构建和装配的解耦。不同的构建器,相同的装配,可以做出不同的对象;相同的构建器,不同
的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
(3)建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对
象的类型就可以得到该对象,而无须知道其内部的具体构造细节。

3.2 结构

建造者(Builder)模式包含如下角色:
(1)产品类(Product):要创建的复杂对象。
(2)抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的
部件对象的创建。
(3)具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体
创建方法。在构造过程完成后,提供产品的实例。
(4)指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产
品的信息,只负责保证对象各部分完整创建或按某种顺序创建。

//1.产品类(自行车类)
public class Bike {
	private String frame;//车架
	private String seat;//车座
	//Getter,Setter方法
}
// 2.抽象建造者类
public abstract class Builder {
	//声明Bike类型的变量,并进行赋值
    protected Bike bike = new Bike();
    public abstract void buildFrame();
    public abstract void buildSeat();
    //构建自行车的方法
    public abstract Bike createBike();
} 
//3.1摩拜单车Builder类
public class MobikeBuilder extends Builder {
 public void buildFrame() {
        bike.setFrame("碳纤维车架");
    }
    public void buildSeat() {
        bike.setSeat("真皮车座");
    }
    public Bike createBike() {
        return bike;
    }
} 
//3.2 ofo单车Builder类
public class OfoBuilder extends Builder {
public void buildFrame() {
        bike.setFrame("铝合金车架");
    }
    public void buildSeat() {
        bike.setSeat("橡胶车座");
    }
    public Bike createBike() {
        return bike;
    }
} 
//4.指挥者类
public class Director {
	//声明builder类型的变量
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    //组装自行车的功能
    public Bike construct() {
        builder.buildFrame();
        builder.buildSeat();
        return builder.createBike();
    }
}  
//5.测试类
public class Client {
	public static void main(String[] args) {
		//创建指挥者对象
        Director director = new Director(new MobileBuilder());
        //让指挥者只会组装自行车
        Bike bike = director.construct();
        System.out.println(bike.getFrame());
        System.out.println(bike.getSeat());
    }
}

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

// 抽象 builder 类
public abstract class Builder {
	protected Bike mBike = new Bike();
	public abstract void buildFrame();
	public abstract void buildSeat();
	public abstract Bike createBike();
	public Bike construct() {
		this.buildFrame();
		this.BuildSeat();
		return this.createBike();
} }

说明:
这样做确实简化了系统结构,但同时也加重了抽象建造者类的职责,也不是太符合单一职责原则,如果
construct() 过于复杂,建议还是封装到 Director 中。

3.3 优缺点

(1)优点:
①建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一
般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在指挥者类中对整体而言可以取
得比较好的稳定性。
②在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得
相同的创建过程可以创建不同的产品对象。
③可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过
程更加清晰,也更方便使用程序来控制创建过程。
④建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不
用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。符合开闭原则。
(2)缺点:
造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适
合使用建造者模式,因此其使用范围受到一定的限制。

3.4 使用场景

建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合
在一起的算法却相对稳定,所以它通常在以下场合使用。
(1)创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
(2)创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表
示是独立的。
(3)开发中的应用场景
①StringBuilder类的append方法
②SQL中的PreparedStatement
③JDOM中,DemoBuilder、SAXBuilder

4.原型模式

(1)基本概述
①通过new产生一个对象需要非常繁琐的数据准备或访问权限,我们可以使用原型模式
②Java中的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点
优势有:效率高(直接克隆,避免了重新执行构造过程步骤)
③克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全
和原型对象相同。并且克隆出的新对象改变后不会影响原型对象。然后,再修改克隆对象的值。
(2)原型模式的实现
①实现Cloneable接口,调用Object中的clone方法
②Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了clone()方法替我们做了大部
分事情。
注意用词:克隆和拷贝一回事
(3)代码示例

//①浅克隆
public class Sheep implements Cloneable {//克隆羊
    private String name;
    private Date birthday;
 //		Getter/Setter方法,构造器
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();//直接调用Object的clone方法
        return obj;
    } }
   
//原型模式测试(浅克隆)
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Date date = new Date(2022L);
        Sheep s1 = new Sheep("少利",date);
        System.out.println(s1);
        System.out.println(s1.getName());
        System.out.println(s1.getBirthday());
        Sheep s2 = (Sheep) s1.clone();
        s2.setName("多利");
        System.out.println(s2);
        System.out.println(s2.getName());
        System.out.println(s2.getBirthday());
    } }

// ②深克隆(把属性也进行克隆)
public class Sheep2 implements Cloneable {
    private String name;
    private Date birthday;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();//自己调用object对象的clone()方法
//        添加如下代码实现深克隆
        Sheep2 s = (Sheep2) obj;
        s.birthday = (Date) this.birthday.clone();//把属性也进行克隆
        return obj;
    }
  // Getter/Setter方法,构造器
//原型模式测试(深客隆)
public class Client2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Date date = new Date(2023L);
        Sheep2 s1 = new Sheep2("小利", date);
        Sheep2 s2 = (Sheep2) s1.clone();
        System.out.println(s1);
        System.out.println(s1.getName());
        System.out.println("s1的date修改前:"+s1.getBirthday());
        date.setTime(20383L);//修改s1的date,不会影响s2
        System.out.println("s1的date修改后:"+s1.getBirthday());
        s2.setName("大利");
        System.out.println(s2);
        System.out.println(s2.getName());
        System.out.println("s2的date:"+s2.getBirthday());
    } }

public class Sheep implements Serializable {//克隆羊
    private String name;
    private Date birthday;
 // Getter/Setter方法,构造方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();//直接调用Object的clone方法
        return obj;
    } }


//③原型模式测试(使用序列化和反序列化深克隆)
public class Sheep implements Serializable { //实现Serializable接口
    private String name;
    private Date birthday;
  // 构造器,Getter/Setter方法
}

// 使用序列化和反序列化实现深克隆
public class Client3 {
    public static void main(String[] args) throws Exception {
        Date date = new Date(8888L);
        Sheep s1 = new Sheep("小羊", date);
        System.out.println(s1.getBirthday());
//       使用序列化和反序列化实现深克隆
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(s1);
        byte[] bytes = bos.toByteArray();

        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        Sheep s2 = (Sheep) ois.readObject();//克隆好的对象
        date.setTime(9999L);
        System.out.println(s2.getBirthday());
    } }

(4)应用场景【通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式】
原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者
spring中bean的创建实际就是两种:单例模式和原型模式(当然,原型模式需要和工厂模式搭建起来)

二.结构型模式

结构性模式核心作用:
是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题

1.适配器模式

(1)什么是适配器模式?
将一个类的接口转换成客户希望的另一个接口。Adaptor模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作
(2)模式中的角色
①目标接口(Target):客户端所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
②需要适配的类(Adaptee):需要适配的类或适配者类
③适配器(Adaptor):通过包装一个需要适配的对象,把原接口转换成目标接口
(3)生活中的场景
例如:以前的键盘使用ps/2接口,现在使用为USB接口。转接口(适配器)把键盘(需要适配的类)的ps/2接口
转换为 USB接口(目标接口)

//(1)被适配的类,相当于例子中的PS/2接口键盘
public class Adaptee {
    public void request(){
        System.out.println("可以完成ps/2接口的功能!");
    } }
   
//(2)目标接口
public interface Target {
   public void handleReq();
}
//(3)适配器
//①类适配器方式
public class Adaptor extends Adaptee implements Target {
    @Override
    public void handleReq() {
        super.request();
    } }
  
//②适配器(使用了组合方式)
public class Adaptor2 implements Target {
    private Adaptee adaptee;
    @Override
    public void handleReq() {
        adaptee.request();   }
    public Adaptor2(Adaptee adaptee) {
        super();
        this.adaptee = adaptee;
    } }

//客户端(相当于例子中的笔记本,只有USB接口)
public class Client {
    public void test1(Target t) {
        t.handleReq();
    }

    public static void main(String[] args) {
        Client c = new Client();
        Adaptee a = new Adaptee();
//        Target t = new Adaptor();
        Target t = new Adaptor2(a);
        c.test1(t);
    } }

(4)工作中的场景:经常用来做旧系统改造和升级
(5)Java中的场景
①java.io.InputStreamReader(InputStream)
②java.io.OutputStreamWriter(OutputStream)
// 是字节流通向字符流的桥梁,通过适配器把字节流转化为字符流

2.代理模式(Proxy pattern)

2.1 概述

(1)由于某些原因需要给目标对象提供一个代理以控制对该目标对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
(2)核心作用:通过代理,控制对对象的访问,可以详细控制访问某个类/对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理(注意:AOP[面向切面编程]的核心机制就是代理模式)
(3)Java中的代理按照代理类生成时机的不同分为静态代理和动态代理。静态代理代理类在编译期就生成了,而动态代理代理类则是Java运行时动态生成;动态代理又分为JDK代理和CGLib代理两种。

2.2 结构

(1)代理(Proxy)模式分为三种角色:
①抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
②真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
③代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能

(3)应用场景
①安全代理:屏蔽对真实角色的直接访问
②远程代理:通过代理类处理远程方法调用
③延迟加载:先加载轻量级的代理对象,真正需要加载时再加载真实对象
例如:当你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时不可能将所有
的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
(4)开发框架中的应用场景
①struts2中拦截器的实现
②数据库连接池关闭处理
③Hiberate中延时加载的实现
④mybatis中实现拦截器插件
⑤Aspect的实现
⑥spring在AOP的实现(日志拦截,声明式事务处理)
⑦web service
⑧RMI远程方法调用

2.1 静态代理

(1)抽象主题类

// 卖火车票的接口
public interface SellTickets {
    void sell();
}

(2)真实主题类

// 火车站类
public class TrainStation implements SellTickets {
    public void sell() {
        System.out.println("火车站卖票");
    } }

(3)代理类

// 代售点类
public class ProxyPoint implements SellTickets {
    //声明火车站类对象
    private TrainStation trainStation  = new TrainStation();
    public void sell() {
        System.out.println("代售点收取一些服务费用");
        trainStation.sell();
    } }

(4)客户端测试

public class Client {
    public static void main(String[] args) {
        //创建代售点类对象
        ProxyPoint proxyPoint = new ProxyPoint();
        //调用方法进行买票
        proxyPoint.sell();
    } }

从上面代码中可以看出测试类直接访问的是ProxyPoint类对象,也就是说ProxyPoint作为访问对象
和目标对象的中介。同时也对sell方法进行了增强(代理点收取一些服务费用)。

2.2 动态代理

2.2.1 分类
(1)JDK自带的动态代理
1)java.lang.reflect.Proxy(作用:动态生成代理类和对象)
2)java.lang.reflect.InvocationHandler(处理器接口)
①可以通过invoke方法实现对真实角色的代理访问
②每次通过Proxy生成代理类对象时都要指定对应的代理器对象
(2)JavaScript字节码操作库实现
(3)CGLIB
(4)ASM(底层使用指令,可维护性较差)
2.2.2 优点
抽象角色(接口)声明的方法都被转移到调用处理器中一个集中的方法去处理。这样,我们就可以更加灵活和统一
的处理众多的方法

2.2.1 动态代理类Proxy

(1)抽象主题类

// 卖火车票的接口
public interface SellTickets {
    void sell();
}

(2)真实主题类

// 火车站类
public class TrainStation implements SellTickets {
    public void sell() {
        System.out.println("火车站卖票");
    } }

(3)获取代理对象的工厂类


/**
 * @Description: 获取代理对象的工厂类
 *    代理类也实现了对应的接口
 */
public class ProxyFactory {

    //声明目标对象
    private TrainStation station = new TrainStation();

    //获取代理对象的方法
    public SellTickets getProxyObject() {
        //返回代理对象
        /*
            ClassLoader loader : 类加载器,用于加载代理类。可以通过目标对象获取类加载器
            Class<?>[] interfaces : 代理类实现的接口的字节码对象
            InvocationHandler h : 代理对象的调用处理程序
         */
        SellTickets proxyObject = (SellTickets)Proxy.newProxyInstance(
                station.getClass().getClassLoader(),
                station.getClass().getInterfaces(),
                new InvocationHandler() {

                    /*
                        Object proxy : 代理对象。和proxyObject对象是同一个对象,在invoke方法中基本不用
                        Method method : 对接口中的方法进行封装的method对象
                        Object[] args : 调用方法的实际参数

                        返回值: 方法的返回值。
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("代售点收取一定的服务费用(jdk动态代理)");
                        //执行目标对象的方法
                        Object obj = method.invoke(station, args);
                        return obj;
                    }
                }
        );
        return proxyObject;
    }
}

(4)客户端测试

public class Client {
    public static void main(String[] args) {
        //获取代理对象
        //1,创建代理工厂对象
        ProxyFactory factory = new ProxyFactory();
        //2,使用factory对象的方法获取代理对象
        SellTickets proxyObject = factory.getProxyObject();
        //3,调用卖调用的方法
        proxyObject.sell();
        System.out.println(proxyObject.getClass());
    } }

3.桥接模式

4.装饰模式

5.组合模式

6.外观模式

7.享元模式

行为型模式

更多推荐

Java常用设计模式