代理模式(Proxy Pattern)是一种结构型设计模式,用于为其他对象提供一种代理或占位符,以控制对该对象的访问。
代理模式常用于在不影响客户端代码的情况下,对目标对象进行访问控制、增加额外操作或延迟加载等。
下面是一个简单的演示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 代理模式_简单原理示例 { class Program { static void Main(string[] args) { Proxy proxy = new Proxy(); proxy.request(); Console.ReadKey(); } } public interface Subject { void request(); } public class RealSubject : Subject { public void request() { Console.WriteLine("真实对象的请求"); } } public class Proxy : Subject { Subject sub; public Proxy() { this.sub = new RealSubject(); } public void request() { Console.WriteLine("代理正在请求...."); sub.request(); } } }
例子:加载图片并显示
假设我们有一个Image类,用于加载和显示图片。由于加载图片可能是一个耗时操作,我们希望在图片加载完成之前显示一个占位符。为了实现这个功能,
我们可以创建一个ImageProxy类作为Image类的代理,它先显示占位符,然后在后台异步加载图片。
首先,定义Image接口和RealImage类:
using System; using System.Threading.Tasks; namespace ProxyPatternExample { public interface IImage { void Display(); } public class RealImage : IImage { private string _filename; public RealImage(string filename) { _filename = filename; LoadFromDisk(_filename); // 模拟从磁盘加载图片 } private void LoadFromDisk(string filename) { Console.WriteLine($"Loading {_filename} from disk..."); // 模拟耗时操作,比如加载大图片 Task.Delay(2000).Wait(); Console.WriteLine($"{_filename} loaded."); } public void Display() { Console.WriteLine($"Displaying {_filename}..."); } } }
接下来,定义ImageProxy
类实现代理模式:
using System; using System.Threading.Tasks; namespace ProxyPatternExample { public class ImageProxy : IImage { private RealImage _realImage; private string _filename; private bool _loaded; public ImageProxy(string filename) { _filename = filename; _loaded = false; // 图片尚未加载完成 } public void Display() { if (!_loaded) // 如果图片尚未加载完成,则显示占位符并异步加载图片 { Console.WriteLine("Displaying placeholder image..."); // 显示占位符图片的代码可以在这里实现 LoadImageAsync(); // 异步加载图片 } else // 如果图片已经加载完成,则直接显示图片 { _realImage.Display(); // 调用实际图片对象的 Display() 方法显示图片 } } private async void LoadImageAsync() // 异步加载图片的方法,使用 async/await 实现异步操作 { await Task.Run(() => LoadImage()); // 在后台线程中执行 LoadImage() 方法,避免阻塞 UI 线程 // 图片加载完成后设置 _loaded 标志为 true,表示图片已经加载完成并且可以显示了。 //请注意这个标志必须在 UI 线程中设置,否则可能引发线程安全问题。 //这里为了简化示例,直接在 LoadImage() 方法中设置了 _loaded 标志。 //在实际项目中可能需要考虑线程同步问题。 _loaded = true; //图片加载完成后可以在这里添加一些其他操作,比如更新 UI、触发事件等。 Console.WriteLine("Image loaded."); } private void LoadImage() { //在后台线程中输出一条消息表示正在加载图片。 //请注意这个消息可能不是在 UI 线程中输出的,因此可能无法实时显示在 UI 上。 //如果需要实时显示消息或其他 UI 更新操作,请在 LoadImageAsync() 方法中处理。 //创建实际图片对象并加载图片数据。这里只是简单地创建了一个 RealImage 对象并没有实际加载图片数据。 //在实际项目中需要根据具体需求实现图片的加载和缓存等操作。 //这里省略了其他成员的定义和实现,比如构造函数、属性等。 //这些成员可以根据具体需求进行定义和实现。 //在实际项目中,代理类可能需要处理更多的逻辑, //比如图片的缓存、异常处理等。 //代理模式可以让我们在不修改客户端代码的情况下对目标对象进行扩展和增强, //提高代码的灵活性和可维护性。 _realImage = new RealImage(_filename); Console.WriteLine($"Loading {_filename} from disk..."); } } }
下面是如何使用ImageProxy
的示例:
using System; namespace ProxyPatternExample { class Program { static void Main(string[] args) { IImage image = new ImageProxy("example.jpg"); // 调用 ImageProxy 对象的 Display() 方法显示图片。由于图片尚未加载完成,因此会先显示占位符并开始异步加载图片。 image.Display(); //显示一条消息表示已经请求显示图片。 //在实际项目中,这里可能是一些其他的操作,比如更新 UI、等待用户输入等。 Console.WriteLine("Image display requested."); // 这里可以添加一些其他的逻辑,比如等待图片加载完成、处理用户输入等。 //请注意,由于图片加载是异步进行的,因此不能直接在 Display() 方法调用后立即访问实际图片对象。 //否则可能会引发空引用异常或其他错误。 // 一种可能的解决方案是使用事件或回调函数来通知客户端图片已经加载完成, //并传递实际图片对象。客户端可以在收到通知后再对实际图片对象进行操作。 Console.ReadLine(); } } }
在这个示例中,我们创建了一个ImageProxy
对象并指定要加载的图片文件名。
然后调用ImageProxy
对象的Display()
方法显示图片。
由于图片尚未加载完成,因此会先显示占位符并开始异步加载图片。
在图片加载完成后,我们可以对实际图片对象进行操作,比如显示图片、更新UI等。
请注意,由于图片加载是异步进行的,因此不能直接在Display()
方法调用后立即访问实际图片对象。
否则可能会引发空引用异常或其他错误。
一种可能的解决方案是使用事件或回调函数来通知客户端图片已经加载完成,
并传递实际图片对象。
客户端可以在收到通知后再对实际图片对象进行操作。