博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式学习笔记
阅读量:3960 次
发布时间:2019-05-24

本文共 24655 字,大约阅读时间需要 82 分钟。

设计模式概述

什么是设计模式

  • 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
  • 1995年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称
    [GoF设计模式」。

学习设计模式的意义

  • 设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
  • 正确使用设计模式具有以下优点:
    • 可以提高程序员的思维能力、编程能力和设计能力。
    • 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
    • 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

23种设计模式

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

OOP七大原则

  • 开闭原则:对扩展开放,对修改关闭
  • 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
  • 依赖倒置原则:要面向接口编程,不要面向实现编程。
  • 单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。
  • 接口隔离原则:要为各个类建立它们需要的专用接口
  • 迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话。
  • 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

单例模式

饿汉式和懒汉式都必须是要会手写的!

核心作用:

  • 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

常用场景:

  • Windows的任务管理器
  • Windows的回收站
  • 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
  • 网站的计数器一般也会采用单例模式,可以保证同步
  • 数据库连接池的设计一般也是单例模式
  • 在Servlet编程中,每个Servlet也是单例的
  • 在Spring中,每个Bean默认就是单例的

1.饿汉式

就是一上来就把类中的所有数据对象都给new出来了。所以饿汉式可能会存在内存浪费的情况!

package com.codeyu.designPattern.single;import java.security.PublicKey;//饿汉式单例public class Hungry {
//饿汉式一上来就会把这些加载进内存。所以可能会导致内存的浪费 private Byte[] bytes1 = new Byte[1024*1024]; private Byte[] bytes2 = new Byte[1024*1024]; private Byte[] bytes3 = new Byte[1024*1024]; private Byte[] bytes4 = new Byte[1024*1024]; //构造器私有 private Hungry() {
} private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance() {
return HUNGRY; }}

2.DCL懒汉式

DCL懒汉式中使用到了volatile关键字,是为了避免指令重排!

解决了懒汉式内存浪费的缺点。

package com.codeyu.designPattern.single;public class LazyMan {
//单例模式一定要构造器私有 private LazyMan() {
} private volatile static LazyMan lazyMan; //下述方法在单线程下确实单例OK的,但是在多线程下就不保证了! public static LazyMan getInstance() {
if(lazyMan == null) {
lazyMan = new LazyMan(); } return lazyMan; } //适用于多线程的单例模式 //双重检测锁模式的懒汉式单例:DCL懒汉式 public static LazyMan getInstance() {
if(lazyMan == null) {
synchronized (LazyMan.class) {
if(lazyMan == null) {
lazyMan = new LazyMan(); } } } return lazyMan; } /** 上述DCL懒汉式也不是完全绝对安全的。因为lazyMan = new LazyMan();这行代码不是原子性操作! * 它经历的过程是: * 1.分配内存空间 * 2.执行构造操作,初始化对象 * 3.将引用指向初始化后的对象内存 * 因为是三步操作,所以底层可能会经历指令重排。比如我们期望的是执行123,真实情况可能是执行132 * * 当走132时,假设A线程先执行完1和3,但是对象还没有初始化,此时线程B进来发现内存空间已经分配了,故 * 线程B会认为lazyMan!=null,线程B会直接返回lazyMan,而此时A线程还未初始化lazyMan,如果 * 此时外部代码使用该对象就会会产生问题了! * 所以为了避免指令重排,还需要在引用变量前加上一个volatile关键字。以保证其不会被指令重排 */}

但是单例模式也是可能会被破环的,可以被反射破坏:

public static void main(String[] args) throws Exception {
LazyMan instance1 = LazyMan.getInstance(); Class
clazz = LazyMan.class; Constructor
declaredConstructor = clazz.getDeclaredConstructor(); declaredConstructor.setAccessible(true); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode());}//命令行打印输出:1915910607284720968//两个对象的hashcode不一样,说明单例模式被破坏

针对上述问题,仍然可以进一步改进,在私有构造函数中添加代码如下:

//单例模式一定要构造器私有private LazyMan() {
synchronized (LazyMan.class){
if (lazyMan != null){
throw new RuntimeException("不要试图使用反射破坏异常!"); } }}//此时命令行会抛出异常:java.lang.RuntimeException: 不要试图使用反射破坏异常!

当然,还可以进一步又使单例模式出现问题,然后进一步也有相应的影对方法!

枚举学习

直接代码测试:写了一个枚举类+一个Test类。通过枚举的获得一个枚举对象,然后通过反射的方式获得一个枚举对象,看看会抱什么错?

package com.codeyu.designPattern.single;import java.lang.reflect.Constructor;public enum  EnumSingle {
INSTANCE; public EnumSingle getInstance() {
return INSTANCE; }}class Test{
public static void main(String[] args) throws Exception{
EnumSingle instance1 = EnumSingle.INSTANCE; Class
enumClazz = EnumSingle.class; Constructor
constructor = enumClazz.getDeclaredConstructor(); constructor.setAccessible(true); EnumSingle instance2 = constructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); }}//运行上述代码,报错如下所示:就是所枚举中没有无参构造方法。Exception in thread "main" java.lang.NoSuchMethodException: com.codeyu.designPattern.single.EnumSingle.
()

很明显,上述错误不是我们所期望的错误。因为我们看Constructor类的newInstance()方法如下所示:

在这里插入图片描述

在这里插入图片描述

那么按照上述的NoSuchMethodException报错去查找原因:

先去Idea中out目录下看字节码.class文件:

在这里插入图片描述

发现确实存在一个私有构造啊,为什么运行时idea会报错NoSuchMethodException呢。既然这样那就去反编译以下看看:

在这里插入图片描述

#反编译命令行指令,在.class文件所在文件夹运行下述指令:javap -p 字节码文件全名#或javap -c 字节码文件全名

idea和反编译后的文件都提示说存在私有构造,但运行代码时却提示NoSuchMethodException。可能是idea和官方提供的反编译软件骗了我们,接下来采用更专业的反编译软件。编译后发现确实不是无参构造了,而是一个继承自父类的有参构造:

在这里插入图片描述

修改反射获取的构造函数,然后再次测试:

class Test{
public static void main(String[] args) throws Exception{
EnumSingle instance1 = EnumSingle.INSTANCE; Class
enumClazz = EnumSingle.class; Constructor
constructor = enumClazz.getDeclaredConstructor(String.class, int.class); constructor.setAccessible(true); EnumSingle instance2 = constructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); }}//运行代码后,这一期报错:Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects

的确是所期望的异常!所以Enum类的单例模式不能通过反射破坏,因为反射的newInstance()方法中的源码明确指定了不能通过反射创建枚举!

工厂模式

作用:

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

OOP七大原则

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

工厂模式的核心本质:

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

三种模式:

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

简单工厂模式(也称:静态工厂模式)

简单工厂模式其内部的方法都是静态的。若要扩展,则需要去修改源代码。

package com.codeyu.designPattern.factory;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 class Consumer {
public static void main(String[] args) {
//工厂方法获取对象 Car car = CarFactory.getCar("五菱"); }}

简单工厂模式缺点:

如果想要添加新的产品,则必须去修改源代码!

即简单工厂模式不满足OOP中的开闭原则!

工厂方法模式

即每一个汽车品牌都有一个自己的工厂,即将车工厂抽象成接口。而简单工厂模式就相当于所有的车均由同一个工厂生产。

工厂方法模式相比于简单工厂模式,它需要创建多个工厂,需要定义的类变多了。

此时当需要添加一个新车“大众”时,我们只需要添加两个新类即可,即一个车类,一个车工厂类,而不需要去修改原来的代码,即此时满足了开闭原则,可以随时的进行动态扩展:

//车类public class DaZhong implements Car {
@Override public void name() {
System.out.println("大众"); }}//车工厂类public class DaZhongFactory implements CarFactory {
@Override public Car getCar() {
return new DaZhong(); }}

简单工厂模式与工厂方法模式对比

结构复杂度方面:简单工厂模式更占优势

代码复杂度方面:简单工厂模式更占优势

编程复杂度方面:简单工厂模式更占优势

管理上的复杂度方面:简单工厂模式更占优势

一般而言:还是简单工厂模式更占优势!

但:

根据设计模式来看:更推荐用工厂方法模式!满足开闭原则!

根据实际业务来看:更推荐简单工厂模式!

小结:

  • 简单工厂模式(静态工厂模式)
    • 虽然某种程度上不符合设计原则,但实际使用最多!
  • 工厂方法模式
    • 不修改已有类的前提下,通过增加新的工厂类实现扩展。
  • 抽象工厂模式
    • 不可以增加产品,可以增加产品族!

应用场景:

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

抽象工厂模式

  • 定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
  • 适用场景:
    • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
    • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
    • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
  • 优点:
    • 具体产品在应用层的代码隔离,无需关心创建的细节
    • 将一个系列的产品统一到一起创建
  • 缺点:
    • 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难
    • 增加了系统的抽象性和理解难度

测试代码:

//=================下述为两个产品接口的定义package com.codeyu.designPattern.factory.abstract1;//手机产品接口public interface PhoneProduct {
void start(); void shutDown(); void callUp(); void sendSms();}package com.codeyu.designPattern.factory.abstract1;//路由器产品接口public interface RouterProduct {
void start(); void shutDown(); void openWifi(); void Setting();}//=================上述两种产品接口的实现类package com.codeyu.designPattern.factory.abstract1;//华为手机public class HuaWeiPhone implements PhoneProduct {
@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("华为发短信"); }}package com.codeyu.designPattern.factory.abstract1;//小米手机public class XiaoMiPhone implements PhoneProduct {
@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("小米发短信"); }}package com.codeyu.designPattern.factory.abstract1;//华为路由器public class HuaWeiRouter implements RouterProduct {
@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("华为路由器设置"); }}package com.codeyu.designPattern.factory.abstract1;//小米路由器public class XiaoMiRouter implements RouterProduct {
@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("小米路由器设置"); }}//=================下述定义抽象产品工厂package com.codeyu.designPattern.factory.abstract1;//抽象产品工厂public interface ProductFactory {
//生产手机 PhoneProduct phoneProduct(); //生产路由器 RouterProduct routerProduct();}//=================下述分别定义小米工厂和华为工厂package com.codeyu.designPattern.factory.abstract1;public class HuaWeiFactory implements ProductFactory {
@Override public PhoneProduct phoneProduct() {
return new HuaWeiPhone(); } @Override public RouterProduct routerProduct() {
return new HuaWeiRouter(); }}package com.codeyu.designPattern.factory.abstract1;public class XiaoMiFactory implements ProductFactory {
@Override public PhoneProduct phoneProduct() {
return new XiaoMiPhone(); } @Override public RouterProduct routerProduct() {
return new XiaoMiRouter(); }}//============下述为最后的测试代码:package com.codeyu.designPattern.factory.abstract1;public class Client {
public static void main(String[] args) {
System.out.println("==============小米产品测试=============="); XiaoMiFactory xiaoMiFactory = new XiaoMiFactory(); PhoneProduct xiaomiPhone = xiaoMiFactory.phoneProduct(); RouterProduct xiaomiRouter = xiaoMiFactory.routerProduct(); xiaomiPhone.callUp(); xiaomiRouter.openWifi(); }}//打印输出为:==============小米产品测试==============小米打电话小米路由器打开wifi

整个类图:

在这里插入图片描述

抽象图:

在这里插入图片描述

代理模式

为什么要学代理模式?因为这就是SpringAOP的底层。

而SpingAOP和SpringMVC是必问的面试考题。

代理模式分类:

  • 静态代理
  • 动态代理

在这里插入图片描述

静态代理

角色分析:

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

代码步骤:

1.接口

//租房接口public interface Rent {
void rent();}

2.真实角色

//房东public class Host implements Rent{
@Override public void rent() {
System.out.println("房东要出租房子!"); }}

3.代理角色

package com.codeyu.designPattern.proxy.static1;//中介代理,帮房东租房子//因为中介代理房东去租房子,所以它也要实现Rent接口public class Proxy implements Rent{
private Host host; public Proxy() {
} public Proxy(Host host) {
this.host = host; } @Override public void rent() {
seeHouse(); host.rent(); contract(); Charge(); } //看房,中介帮房东做的事情,且不必劳烦房东 public void seeHouse() {
System.out.println("中介带你看房!"); } //收中介费 public void Charge() {
System.out.println("收中介费!"); } //签租赁合同 public void contract() {
System.out.println("签租赁合同!"); }}

4.客户端访问代理角色

package com.codeyu.designPattern.proxy.static1;public class Client {
public static void main(String[] args) {
//没有代理模式的租房子,直接找房东 Host host = new Host(); host.rent(); //有代理模式的租房子,通过中介来租房 //代理,中介帮房东租房子,但是代理角色一般会有一些附属操作! Proxy proxy = new Proxy(host); //代理房东 proxy.rent(); }}

代理模式的好处:

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

缺点:

  • 一个真实角色就会产生一个代理角色;代码量会翻信

代理模式再理解

直接看代码:

//假设有一个UserSrevice接口,实现增删改查功能package com.codeyu.designPattern.proxy.static1.demo02;public interface UserService {
void add(); void delete(); void update(); void query();}//用一个实现类来实现UserService接口package com.codeyu.designPattern.proxy.static1.demo02;//真实对象public class UserServiceImpl implements UserService{
@Override public void add() {
System.out.println("增加了一个用户!"); } @Override public void delete() {
System.out.println("删除了一个用户!"); } @Override public void update() {
System.out.println("修改了一个用户!"); } @Override public void query() {
System.out.println("查询了一个用户!"); }}//Client类使用UserServiceImpl类来实现增删改查package com.codeyu.designPattern.proxy.static1.demo02;public class Client {
public static void main(String[] args) {
UserService userService = new UserServiceImpl(); userService.add(); }}//===================================//但是,假设现在想要给UserServiceImpl类中的每个方法添加日志功能//一种方式是直接去修改业务代码,将每一个方法中添加一句日志输出package com.codeyu.designPattern.proxy.static1.demo02;//真实对象public class UserServiceImpl implements UserService{
@Override public void add() {
//日志功能 System.out.println("使用了add()方法"); System.out.println("增加了一个用户!"); } @Override public void delete() {
//日志功能 System.out.println("使用了delete()方法"); System.out.println("删除了一个用户!"); } @Override public void update() {
//日志功能 System.out.println("使用了update()方法"); System.out.println("修改了一个用户!"); } @Override public void query() {
//日志功能 System.out.println("使用了query()方法"); System.out.println("查询了一个用户!"); }}

但是上述添加日志功能的实现修改了业务代码,我们所期望的是尽量少或者不要修改业务代码。这时,就可以通过代理的方式来实现:

//原来的业务代码只用执行业务;package com.codeyu.designPattern.proxy.static1.demo02;//真实对象public class UserServiceImpl implements UserService{
@Override public void add() {
System.out.println("增加了一个用户!"); } @Override public void delete() {
System.out.println("删除了一个用户!"); } @Override public void update() {
System.out.println("修改了一个用户!"); } @Override public void query() {
System.out.println("查询了一个用户!"); }}//通过代理方法来添加日志功能;package com.codeyu.designPattern.proxy.static1.demo02;public class UserServiceProxy implements UserService{
private UserServiceImpl userService; //通过set方法注入UserServiceImpl public void setUserService(UserServiceImpl userService) {
this.userService = userService; } //日志功能使用一个日志方法来实现 public void log(String str) {
System.out.println("[Debug] 使用了"+str+"方法"); } @Override public void add() {
log("add"); userService.add(); } @Override public void delete() {
log("delete"); userService.delete(); } @Override public void update() {
log("update"); userService.update(); } @Override public void query() {
log("query"); userService.query(); }}//Client演示使用方式:package com.codeyu.designPattern.proxy.static1.demo02;public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl(); //使用代理类 UserServiceProxy userServiceProxy = new UserServiceProxy(); userServiceProxy.setUserService(userService); userServiceProxy.add(); }}

上述使用代理方法的好处就是业务代码只用去实现业务相关的功能,日志功能的使用由代理类来完成,让代码整体的耦合度更低!

在这里插入图片描述

动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的,也就是给它一个模板,然后根据模板自动生成!
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口:JDK动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javasist

要知道两个类:Proxy和InvocationHandler

Proxy是位于java.lang.reflect包下的类,InvocationHandler是位于java.lang.reflect包下的接口。

Proxy

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类!

Proxy作用:

  • Proxy是用于创建动态代理实例的
  • 它是由它所创建的动态代理类的超类

InvocationHandler

作用:调用处理程序,并返回一个结果的!

接下来看一个测试用例:

//=======1.首先有一个租房接口=======package com.codeyu.designPattern.proxy.dynamic1.demo03;//租房接口public interface Rent {
void rent();}//======2.然后有一个Host类实现了Rent接口=======package com.codeyu.designPattern.proxy.dynamic1.demo03;//房东public class Host implements Rent {
@Override public void rent() {
System.out.println("房东要出租房子!"); }}//=======3.然后我们去写一个实现了InvocationHandler接口的类=======package com.codeyu.designPattern.proxy.dynamic1.demo03;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;//=========4.等会儿我们会用这个类自动生成代理类========public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口 private Rent rent; public void setRent(Rent rent) {
this.rent = rent; } //生成得到代理对象 public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } @Override //处理代理实例,并放回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制实现! Object result = method.invoke(rent, args); return result; }}//==========5.Client的测试用例=========package com.codeyu.designPattern.proxy.dynamic1.demo03;public class Client {
public static void main(String[] args) {
//真实角色 Host host = new Host(); //代理角色:现在没有 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //通过调用程序处理角色来处理我们要调用的接口对象! pih.setRent(host); Rent proxy = (Rent)pih.getProxy(); proxy.rent(); }}//=====6.命令行打印输出:======房东要出租房子!

通过上述代码可以看出,我们的动态代理代码中并没有真实的代理解决,我们只定义了一个实现了InvocationHandler接口的处理类:ProxyInvocationHandler,该处理类就做两件事情,一个是invoke():即执行它真正要执行的方法;另一个是得到代理类的方法,即getProxy()方法,改方法会返回一个代理实例。

在Client类的测试代码中,怎么去得到代理类实例并设定要代理的角色呢?

答:首先通过:

pih.setRent(host);

设定要代理的角色。

然后通过:

Rent proxy = (Rent)pih.getProxy();

**获得动态代理对象!**即动态代理实例!

然后通过代理实例来实现租房:

proxy.rent();

实际上,上述方法:proxy.rent();实际上是调用的ProxyInvocationHandler类中的invoke()方法:

@Override//处理代理实例,并放回结果public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制实现! Object result = method.invoke(rent, args); return result;}

只不过该invoke()方法的内部是通过method.invoke(rent, args);只是去调用了Rent接口的rent()方法,所以如果我们要实现代码的横切,即添加上:seeHouse()、fare()等方法的话,只用在上述方法中添加接口,如下:

@Override//处理代理实例,并放回结果public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//看房 seeHouse(); //动态代理的本质就是使用反射机制实现! Object result = method.invoke(rent, args); return result; //收费 fare();}public void seeHouse() {
System.out.println("中介带看房子!");}public void fare() {
System.out.println("收中介费!");}//此时仍然通过Client类进行测试:package com.codeyu.designPattern.proxy.dynamic1.demo03;public class Client {
public static void main(String[] args) {
//真实角色 Host host = new Host(); //代理角色:现在没有 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //通过调用程序处理角色来处理我们要调用的接口对象! pih.setRent(host); //这里的porxy就是动态生成的 Rent proxy = (Rent)pih.getProxy(); proxy.rent(); }}//=====6.命令行打印输出:======中介带看房子!房东要出租房子!收中介费!

实际上核心全在ProxyInvocationHandler类中,它指明了代理谁;生成代理类对象;以及调用代理程序。在下方代码中用123标记出来:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;//=========public class ProxyInvocationHandler implements InvocationHandler {
//1.被代理的接口(代理谁) private Rent rent; public void setRent(Rent rent) {
this.rent = rent; } //2.生成得到代理类对象 public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } @Override //3.调用代理程序 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制实现! Object result = method.invoke(rent, args); return result; }}

再看一个测试代码:

//======1.已知UserService接口======package com.codeyu.designPattern.proxy.static1.demo02;public interface UserService {
void add(); void delete(); void update(); void query();}//======2.UserService接口的实现类======package com.codeyu.designPattern.proxy.static1.demo02;//真实对象public class UserServiceImpl implements UserService{
@Override public void add() {
System.out.println("增加了一个用户!"); } @Override public void delete() {
System.out.println("删除了一个用户!"); } @Override public void update() {
System.out.println("修改了一个用户!"); } @Override public void query() {
System.out.println("查询了一个用户!"); }}//======3.可以把下方的ProxyInvocationHandler类当作一个模板,用以处理以后需要用到代理的地方======package com.codeyu.designPattern.proxy.dynamic1.demo04;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;//万能的InvocationHandler实现类模板public class ProxyInvocationHandler implements InvocationHandler {
//1.代理谁 private Object target; public void setTarget(Object target) {
this.target = target; } //2.生成得到代理类对象 public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override //3.处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制实现! Object result = method.invoke(target, args); return result; }}//======4.Client类进行测试======package com.codeyu.designPattern.proxy.dynamic1.demo04;import com.codeyu.designPattern.proxy.static1.demo02.UserService;import com.codeyu.designPattern.proxy.static1.demo02.UserServiceImpl;import java.lang.reflect.InvocationHandler;public class Client {
public static void main(String[] args) {
//真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理角色:通过ProxyInvocationHandler类中的方法来获取 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设定要代理的对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserService)pih.getProxy(); //使用代理来完成业务 proxy.add(); }}//======5.命令行打印输出======增加了一个用户!

可以将上述的ProxyInvocationHandler类当作一个工具类,用以初一其他的需要用到代理的地方!

接下来还是实现之前关于UserServiceImpl类中的方法所说到的一个功能,即调用每一个方法时,输出一个日志,那么可以按照下述方式进行:

//万能的InvocationHandler实现类模板public class ProxyInvocationHandler implements InvocationHandler {
//1.代理谁 private Object target; public void setTarget(Object target) {
this.target = target; } //2.生成得到代理类对象 public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } @Override //3.处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName()); //动态代理的本质就是使用反射机制实现! Object result = method.invoke(target, args); return result; } public void log(String msg) {
System.out.println("调用了"+msg+"方法"); }}//执行下述方法;public class Client {
public static void main(String[] args) {
//真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理角色:通过ProxyInvocationHandler类中的方法来获取 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设定要代理的对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserService)pih.getProxy(); //使用代理来完成业务 proxy.add(); }}//打印如数日志:调用了add方法增加了一个用户! //执行下述方法;public class Client {
public static void main(String[] args) {
//真实角色 UserServiceImpl userService = new UserServiceImpl(); //代理角色:通过ProxyInvocationHandler类中的方法来获取 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设定要代理的对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserService)pih.getProxy(); //使用代理来完成业务 proxy.delete(); }}//打印如数日志:调用了delete方法增加了一个用户!

上述也是通过反射的方式去获得的方法名!

动态代理的好处

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

转载地址:http://dqozi.baihongyu.com/

你可能感兴趣的文章