在日常开发中,我们往往忽视了设计模式的重要性。这可能是因为项目时间紧迫,或者对设计模式理解不深。其实,很多时候我们可能在不经意间已经使用了某些模式。重要的是要有意识地学习和应用,让代码更加优雅和高效。也许是时候重新审视我们的编程实践,将设计模式融入其中了。今天由浅入深,重学【代理模式】,让我们一起“重学设计模式”。
一、代理模式
代理模式是一种结构型设计模式,通过为另外一个对象提供一个替代或占位符,以控制对该对象的访问。代理模式在客户端与目标对象之间引入一个代理对象,可以在不改变目标对象的情况下,增加额外的功能,如权限控制、懒加载、日志记录等。
1、代理模式的结构
Subject(抽象主题):定义目标对象和代理对象的共同接口。
RealSubject(真实主题):实现Subject接口,是真正执行任务的对象。
Proxy(代理):实现Subject接口,持有一个RealSubject的引用,并控制对RealSubject的访问。
2、代理模式简要代码
// 抽象主题
interface Subject {
void request();
}
// 真实主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 堆代码 duidaima.com
// 代理
class ProxySubject implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
System.out.println("ProxySubject: Logging before request.");
realSubject.request();
System.out.println("ProxySubject: Logging after request.");
}
}
假设开发一个图像查看器应用,需要加载和显示大量图像。直接加载所有图像会占用大量内存和时间。通过代理模式,可以在需要显示图像时才加载实际的图像对象。
二、不用代理模式
public class Image {
private String filename;
public Image(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
System.out.println("Loading image: " + filename);
}
public void display() {
System.out.println("Displaying image: " + filename);
}
}
public class Client {
public static void main(String[] args) {
Image image1 = new Image("image1.jpg");
Image image2 = new Image("image2.jpg");
image1.display();
image2.display();
}
}
三、使用代理模式
1、抽象主题
/**
* 代理模式
*
* 抽象主题
*/
public interface Image {
void display();
}
2、真实主题
/**
* 真实主题
*/
public class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
System.out.println("Loading image: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
3、代理
public class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
4、客户端
public class ProxyClient {
public static void main(String[] args) {
Image image1 = new ProxyImage("image1.jpg");
Image image2 = new ProxyImage("image2.jpg");
// 图像将在显示时加载
image1.display();
image2.display();
}
}
5、使用代理模式有哪些改进和优化?
(1)延迟加载
图像只有在需要显示时才加载,节省资源。
(2)降低内存使用
避免一次性加载所有图像,适用于大量对象场景。
(3)增强控制
代理可以在加载前后添加额外逻辑,如缓存、权限检查。
四、哪些场景可以使用代理模式优化?
虚拟代理:延迟对象的创建和初始化。
保护代理:控制对对象的访问权限。
远程代理:为远程对象提供本地代表。
智能代理:在访问对象时执行附加操作,如引用计数、日志记录。
Java代码举例:远程代理
// 抽象主题
interface Image {
void display();
}
// 真实主题
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromServer();
}
private void loadImageFromServer() {
System.out.println("Loading image from server: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// 代理
class RemoteImageProxy implements Image {
private RealImage realImage;
private String filename;
public RemoteImageProxy(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// 客户端代码
public class RemoteProxyClient {
public static void main(String[] args) {
Image image = new RemoteImageProxy("remoteImage.jpg");
image.display();
}
}
五、在JDK源码中,哪些地方应用了代理模式
1、java.lang.reflect.Proxy 类
使用动态代理机制,为接口创建代理实例,允许在运行时动态处理方法调用。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 接口
interface Service {
void perform();
}
// 真实主题
class ServiceImpl implements Service {
@Override
public void perform() {
System.out.println("ServiceImpl: Performing service.");
}
}
// 代理处理器
class ServiceInvocationHandler implements InvocationHandler {
private Service service;
public ServiceInvocationHandler(Service service) {
this.service = service;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy: Before method " + method.getName());
Object result = method.invoke(service, args);
System.out.println("Proxy: After method " + method.getName());
return result;
}
}
// 客户端代码
public class ProxyExample {
public static void main(String[] args) {
Service realService = new ServiceImpl();
Service proxyService = (Service) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new ServiceInvocationHandler(realService)
);
proxyService.perform();
}
}
2、java.io.BufferedInputStream 类
通过代理模式包装原始的输入流,增加缓冲功能,提高读写效率。
BufferedInputStream 作为代理,封装了 FileInputStream,提供了缓冲功能,优化了文件读取操作。
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class BufferedInputStreamExample {
public static void main(String[] args) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.txt"))) {
int data;
while ((data = bis.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
六、总结
代理模式通过在客户端与真实对象之间引入代理对象,控制和管理对真实对象的访问,实现功能扩展和优化。其结构包括抽象主题、真实主题和代理三个部分,能够在不修改目标对象的情况下,添加如懒加载、权限控制、日志记录等额外功能。代理模式的主要优点在于增强控制、提高灵活性和优化资源使用,但也可能增加系统的复杂性。常见应用场景包括虚拟代理、保护代理、远程代理和智能代理。
在Java中,java.lang.reflect.Proxy 和 java.io.BufferedInputStream 是代理模式的典型应用,展示了其在实际开发中的重要作用。通过合理运用代理模式,开发者可以有效地管理对象访问,提升系统的可维护性和性能。