【HarmonyOS学习日志(6)】设计模式之原型模式和代理模式
首先,你需要定义一个接口,该接口声明了你想要代理的方法。
目录
原型模式
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
模式结构
-
抽象原型类:规定了具体原型对象必须实现的clone()方法。
-
具体原型类(Realizetype):实现抽象原型类的clone()方法,它是可被复制的对象。
-
访问类:使用具体原型类中的clone()方法来复制新的对象。
实现方法
原型模式的克隆分为浅克隆和深克隆。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。、
Java中的Object类中提供了clone()方法来实现浅克隆
具体原型类:
public class Realizetype implements Cloneable {
@Override
public Realizetype() {
System.out.println(“具体的原型对象创建完成!”)
}
@Override
protected Realizetype clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!")
return (Realizetype) super.clone();
}
测试访问类:
public class PrototypeTest {
public static void main(String[] args) throws cloneNotSupportedException {
Realizetype r1 = new Realizetype();
Realizetype r2 = ri.clone();
System.out.println(“对象r1和r2是同一个对象?” + (r1 == r2))
}
}
实例
三好学生奖状(浅克隆)
lic class Citation implements Cloneable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return (this.name);
}
public void show() {
System.out.println(name + "同学,在2024学年第一学期中表现优秀,被评为三好学生,特发此状!");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
//测试访问类
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建原型对象
Citation c1 = new Citation();
//克隆奖状对象
Citation c2 = citation.clone();
c1.setName("张三");
c2.setName("李四");
//调用 Show()方法展示
c1.show();
c2.show();
}
}
浅克隆的使用场景
-
对象的创建非常复杂,可以使用原型模式快捷的创建对象。
-
性能和安全要求比较高。
实例扩展(深克隆)
将上面的"三好学生"奖状中Citation类的name属性修改为Student类型的属性
public class Student {
//学生的姓名
private String name;
public String getName() {
return name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}'
}
}
public class Citation implements Cloneable {
private Student stu;
public Student getStu() {
return stu;
}
public void setStu(Student stu) {
this.stu = stu
}
public void show() {
System.out.println(stu.getName() + "同学,在2024学年第一学期中表现优秀,被评为三好学生,特发此状!");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建原型对象
Citation c1 = new Citation();
//创建张三学生对象
Student stu = new Student();
stu.setName("张三");
citation.setStu(stu);
//克隆奖状对象
Citation citation1 = citation.clone();
Student stu1 = citation1.getStu();
stu1.setName("李四");
//调用 Show()方法展示
citation.show();
citation1.show();
}
}
stu对象和stu1对象是同一个对象,就会产生将stu1对象中name属性值改为"李四",两个Citation(奖状)对象中显示的都是李四。这就是浅克隆的效果,对具体原型类(Citation)中的引用类型的属性进行引用的复制。这种情况需要使用深克隆,而进行深克隆需要使用对象流。
public class CitationTest {
public static void main(String[] args) throws Exception {
//创建原型对象
Citation citation = new Citation();
//创建张三学生对象
Student stu = new Student();
stu.setName("张三");
citation.setStu(stu);
//创建对象输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/robin/a.txt"));
//写对象
oos.writeObject(citation);
//释放资源
oos.close();
//创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/robin/a.txt"));
//读取对象
Citation citation1 = (Citation) ois.readObject();
//释放数据
ois.close();
Student stu1 = citation2.getStu();
stu1.setName("李四");
citation.show();
citation1.show();
}
}
浅克隆与深克隆的区别
-
浅克隆:克隆出来的新对象与原始对象共享引用类型的属性。也就是说,新对象中的引用类型属性指向的是原始对象中相同的引用类型属性。如果修改了新对象中的引用类型属性,原始对象中的相应属性也会被修改。在Java中,可以通过实现Cloneable接口和重写clone()方法来实现浅克隆。
-
深克隆:克隆出来的新对象与原始对象不共享引用类型的属性。也就是说,新对象中的引用类型属性指向的是新的对象,而不是原始对象中相同的引用类型属性。如果修改了新对象中的引用类型属性,原始对象中的相应属性不会被修改。
结构型模式
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
结构型模式分为:代理模式,适配器模式,装饰者模式,桥接模式,外观模式,组合模式,享元模式
代理模式
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。
模式结构
-
抽象主题类(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
-
真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
-
代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
静态代理
火车票售卖模型
//买火车票的接口
public interface SellTickets {
void sell();
}
//火车站类
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
//代售点类
public class ProxyPoint implements SellTickets {
//声明火车站类对象
private TrainStation trainStation = new TrainStation();
public void sell() {
System.out.println("代理点收取一些服务费用");
trainStatino.sell();
}
}
public class Client {
public static void main(String[] args) {
//创建代售点对象
ProxyPoint proxyPoint = new ProxyPoint();
//调用方法进行买票
proxyPoint.sell();
}
}
JDK动态代理(Java动态代理)
Java动态代理是Java提供的一种机制,它允许程序在运行时创建代理类,并在不改变代理类代码的情况下为方法调用添加额外的功能,从实现自定义类加载器到构建灵活高效的框架。动态代理的核心是 java.lang.reflect 包中的Proxy类和 InvocationHandler 接口。
动态代理的基本概念
动态代理是一种在运行时创建代理对象的技术,可以在不修改原始对象代码的情况下为方法调用添加额外的行为。Java提供了两种代理方式:
-
接口代理(基于java.lang.reflect.Proxy):用于代理实现了接口的类。
-
类代理(基于第三方库如CGLIB):用于代理没有实现接口的类。
接口代理
实现Java动态代理需要以下几个步骤:
-
定义一个接口:首先,你需要定义一个接口,该接口声明了你想要代理的方法。
-
实现InvocationHandler接口:创建一个实现了InvocationHandler接口的类,并重写invoke方法。在invoke方法中,你可以定义在调用目标对象的方法前后需要执行的逻辑。
-
创建动态代理类:使用Proxy类的newProxyInstance方法在运行时动态创建代理对象。这个方法需要三个参数:使用的ClassLoader,需要实现的接口数组,以及用来处理接口方法调用的InvocationHandler实例。
-
使用代理对象:通过代理对象调用方法时,实际会委托给InvocationHandler的invoke方法处理。
InvocationHandler接口
InvocationHandler是动态代理的核心接口。它定义了invoke方法,代理对象的每个方法调用都会被重定向到这个invoke方法。
public interface InvocationHandler {
Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数:
-
proxy:是调用该方法的代理实例。
-
method:是在代理实例上调用的接口方法对应的Method实例。
-
args:一个Object数组,是在代理实例上的方法调用中传递的参数值。如果接口方法为无参,则该值为null.
其返回值为,调用代理实例上的方法的返回值。
Proxy类
Proxy类提供了创建动态代理类及其实例的静态方法,该类也是动态代理类的超类。
代理类具有以下属性:
-
代理类的名称以"$Proxy"开头,后面跟着一个数字序号。
-
代理类继承了Proxy类。
-
代理类实现了创建时指定的接口(JDK动态代理是面向接口的)。
-
每个代理类都有一个公共构造函数,它接受一个参数,即接口InvacationHandler的实现,用于设置代理实例的调用处理器。
Proxy提供了两个静态方法,用于获取代理对象。
getProxyClass
用于获取代理类的Class对象,再通过调用构造函数创建代理实例。
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>...interfances)
throws IllegalArgumentException
参数:
-
loader:为类加载器。
-
interfaces:为接口Class对象数组。
返回值为动态代理类的Class对象。
newProxyInstance
用于创建一个代理实例。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfances,
InvocationHandler h)
throws IllegalArgumentException
参数
-
loader:为类加载器。
-
interfaces:为接口的Class对象数组。
-
h:指定的调用处理器。
返回值为指定接口的代理类的实例
Proxy类主要用来获取动态代理对象,InvocationHandler接口主要用于方法调用的约束与增强。
代码示例
创建目标接口及实现类
public interface Foo {
String ping(String name);
}
//Foo接口的实现类RealFoo
public class RealFoo implements Foo {
@Override
public String ping(String name) {
System.out.println("ping");
return "pong";
}
}
创建一个InvocationHandler
创建一个InvocationHandler接口的实现类MyInvocationHandler.该类的构造方法参数为要代理的目标对象。
public class MyInvocationHandler implements InvocationHandler {
// 目标对象
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy - " + proxy.getClass());
System.out.println("method - " + method);
System.out.println("args - " + Arrays.toString(args));
return method.invoke(target, args);
}
}
综合案例
将上述SellTickets类TrainStation类放入jdk_proxy文件夹内

//新建类
//获取代理对象的工厂类——代理类也实现了对应的接口
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() {
//jvm内存结构
/*
Object proxy : 代理对象,和proxyObject对象是同一个对象,在invoke方法中基本不用
Method method : 对接口中的方法进行封装的method对象。
Object[] args : 调用方法的实际参数
返回值 : 方法的返回值——调用sell()方法的返回值,没有则返回null,有则由invoke返回具体的值
*/
public Object invoke(Object proxy,Method method,Object[] args) throw Throwable{
System.out.println("invoke方法执行了");
System.out.println("代售点收取一定的服务费用(jdk动态代理)");
//执行目标对象(火车站)的方法
Object obj = method.invoke(station.args);
return obj;
}
}
);
return proxyObject;
}
}
//测试类
public class Client {
public static void main(String[] args) {
//获取代理对象
//1.创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//2.使用factory对象的方法获取代理对象
SellTickets proxyObject = factory.getProxyObject();
//3.调用卖调用的方法
proxyObject.sell();
}
}
动态代理的优势
-
代码复用:可以通过动态代理实现常见的横切关注点(如日志记录、事务管理、性能监控等),减少重复代码。
-
解耦:通过代理对象和目标对象分离,降低模块之间的耦合度。
-
灵活性:可以在运行时动态地改变代理行为,增强程序的灵活性和扩展性。
更多推荐

所有评论(0)