1. 装饰者设计模式
<1>解决的问题:在不改变原有类方法源代码的情况下给方法进行增强,在实现的方法中调用原有对象的对应方法,也可对原有方法进行增强。
<2>要求:要实现接口的所有方法。
<3>弊端:要重写的方法太多了,写起来麻烦。
<4>代码演示:
需求:增强close方法
Inter接口:
//装饰设计模式
//在不改变现有对象原有结构的情况下,
//动态地给该对象增加一些职责(即增加其额外功能)
public interface Inter {
public abstract void init();
public abstract void close();
}
InterImpl1实现类:
public class InterImpl1 implements Inter{
@Override
public void init() {
System.out.println("初始化连接...");
}
@Override
public void close() {
System.out.println("关闭连接...");
}
}
重定义的InterImpl2实现类:
//1.定义一个类,实现和被装饰类相同的接口
public class InterImpl2 implements Inter {
//2.定义一个原有对象的成员变量
private Inter inter;
//3.通过有参构造方法为其赋值}
public InterImpl2(Inter inter) {
this.inter = inter;
}
//4.重写方法:不想改进的方法继续调用原有对象的功能。想改进的方法自己重写
@Override
public void init() {
inter.init();
}
@Override
public void close() {
System.out.println("归还连接");
}
}
InterTest测试类:
//需求,再不改变现有对象原有结构的情况下;
// 将关闭连接改成归还连接
public class InterTest {
public static void main(String[] args) {
InterImpl1 i1 = new InterImpl1();
i1.init();
i1.close();
System.out.println("-----------------");
InterImpl2 i2 = new InterImpl2(i1);
i2.init();
i2.close();
}
}
打印结果:
---------------------------------------------------------------------------------------------------------------------
初始化连接...
关闭连接...
-----------------
初始化连接...
归还连接
2. 适配器模式
<1>解决的问题:解决重写的方法太多的问题,如果没有这样的模版类(适配器类)就需要自己写。
<2>要求:需要提前定义一个类(适配器类/模版类)实现接口,重写所有方法,在重写的方法中调用原有对象的对应方法,不做增强。然后我们的类只需要继承该适配器类,重写需要增强的方法即可.
<3>弊端:如果没有这样的模版类(适配器类)就需要自己写
<4>代码演示:
需求:增强method1方法
Inter接口:
//归还连接-适配器设计模式
//解决接口和实现类之间的矛盾问题。
public interface Inter {
public abstract void method1();
public abstract void method2();
public abstract void method3();
public abstract void method4();
public abstract void method5();
}
MyAdapter适配器类【为了避免其他类来创建适配器类的对象,该适配器类需要定义为抽象类!】:
//1.定义一个适配器类,实现对应的接口。
//2重写所有的抽象方法。
//3.为了避免其他类来创建适配器类的对象,该适配器类需要定义为抽象类!
public abstract class MyAdapter implements Inter {
/*@Override
public void method1() {
}*/
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
}
InterImpl实现类:
//4.让功能类继承适配器类,重写自己需要的方法。
public class InterImpl extends MyAdapter{
@Override
public void method1() {
System.out.println("只用method01方法...");
}
}
Test测试类:
public class Test {
public static void main(String[] args) {
InterImpl ii = new InterImpl();
ii.method1();
}
}
打印结果:
---------------------------------------------------------------------------------------------------------------------
只用method01方法...
3. 动态代理
<1>解决的问题:在不改变原始对象方法源代码的情况下对原始方法进行增强,可以彻底解决装饰模式以及适配器模式要重写的方法过多问题。
<2>要求:代理对象和目标/真实对象要实现相同的接口,实现了相同的接口就意味着有相同的功能
<3>三个角色:目标/原始/真实对象、接口、代理对象(在内存中动态生成的对象)
<4>具体做法:使用Proxy.newProxyInstance(…)方法动态生成并返回一个代理对象
<5>代码演示:
需求:增强study方法
StudentInterface接口:
public interface StudentInterface {
void eat(String name);
void study();
}
Student实现类:
public class Student implements StudentInterface{
@Override
public void eat(String name){
System.out.println("学生吃" +name);
}
@Override
public void study(){
System.out.println("学生在教室学习");
}
}
StudentTest测试类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class StudentTest {
public static void main(String[] args) {
Student stu = new Student();
stu.eat("米饭");
stu.study();
/*要求;在不改动Student类中任何的代码的前提下,
通过study方法输出一句话:学生在家自己学习*/
//动态代理∶在不改变目标对象方法的情况下对方法进行增强组成
//组成:
// 被代理对象︰真实的对象
// 代理对象︰内存中的一个对象要求
//要求:
//代理对象必须和被代理对象实现相同的接口
//实现[Proxy类]
System.out.println("-------------------------------------------");
//Proxy.newProxyInstance()
//参数1-类加载器:和被代理对象使用相同的类加裁器
//参数2-接口类型的Class数组:和被代理对象使用相同接口
//参数3-代理规则:完成代理培强的功能[匿名内部类]
//因为传递了一个接口类型的Class数组则可以强转
StudentInterface proxyStu = (StudentInterface)Proxy.newProxyInstance(stu.getClass().getClassLoader(), new Class[]{StudentInterface.class}, new InvocationHandler() {
/*
* 执行Student类中所有的方法都会经过invoke方法
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//对method方法进行判断,如果是study,则增强
//如果不是,还是调用学生对象原有的功能即可
if (method.getName().equals("study")) {
System.out.println("学生在家自己学习");
return null;
} else {
return method.invoke(stu, args);
}
}
});
proxyStu.eat("米饭");
proxyStu.study();
}
}
打印结果:
---------------------------------------------------------------------------------------------------------------------
学生吃米饭
学生在教室学习
-------------------------------------------
学生吃米饭
学生在家自己学习
注:
<1>System.out.println(proxy); 不要打印,因为打印proxy相当于调用了proxy.toString(),形成了死递归【Exception in thread “main” java.lang.StackOverflowError】
<2>使用代理对象时,调用的其他方法【非增强方法】都是原对象中的方法
<3>Proxy.newProxyInstance( , ,)中的三个参数
stu.getClass().getClassLoader():借用stu获取对应的学生对象,再得到学生对象的类加载器
new Class[]{StudentInterface.class}:先new Class[ ]得到通过.class得到接口类[多态]的对象【该对象类型为StudentInterface】
new InvocationHandler():InvocationHandler是一个接口,通过匿名内部类来定义代理规则