JAVA SCRIPT设计模式是本人根据GOF的设计模式写的博客记录。使用JAVA SCRIPT语言来实现主体功能,所以不可能像C++,JAVA等面向对象语言一样严谨,大部分程序都附上了JAVA SCRIPT代码,代码只是实现了设计模式的主体功能,不代表全部的正确,特此声明。若读者需要了解设计模式目录、原则、设计变化方向,环境相关等信息请查看设计模式开篇。
一、UML类图
参与者:
1.1 Proxy(ImageProxy)
- 保存一个引用使得代理可以访问实体。若RealSubject和Subject的接口相同,Proxy会引用Subject。
- 提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。
- 控制对实体的存取,并可能负责创建和删除它。
1.2 Subject(Graphic)
- 定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。。
1.3 RealSubject(Image)
- 定义Proxy所代表的实体。
二、意图
为其他对象提供一种代理以控制对这个对象的访问。
三、适用性
- 远程代理(Remote Proxy)为一个对象在不同的地址空间提供局部代表。
- 虚代理(Virtual Proxy)根据需要创建开销很大的对象。
- 保护代理(ProtectionProxy)控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
- 智能指引(SmartReference)取代了简单的指针,它在访问对象时执行一些附加操作。它的典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它。
- 当第一次引用一个持久对象时,将它装入内存。
- 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
四、示例代码
4.1 动机
对一个对象进行访问控制的一个原因是为了只有在我们确实需要这个对象时才对它进行创建和初始化。我们考虑一个可以在文档中嵌入图形对象的文档编辑器。有些图形对象的创建开销很大。但是打开文档必须很迅速,因此我们在打开文档时应避免 一次性创建所有开销很大的对象。因为并非所有这些对象在文档中都同时可见,所以也没有必要同时创建这些对象。
这一限制条件意味着,对于每一个开销很大的对象,应该根据需要进行创建,当一个图像变为可见时会产生这样的需要。但是在文档中我们用什么来代替这个图像呢?我们又如何才能隐藏根据需要创建图像这一事实,从而不会使得编辑器的实现复杂化呢?例如,这种优化不应影响绘制和格式化的代码。
问题的解决方案是使用另一个对象,即图像 Proxy,替代那个真正的图像。 Proxy可以代替一个图像对象,并且在需要时负责实例化这个图像对象。
只有当文档编辑器激活图像代理的Draw操作以显示这个图像的时候,图像Proxy才创建真 正的图像。Proxy直接将随后的请求转发给这个图像对象。
4.2 示例UML
目录结构:
4.2 Proxy(ImageProxy)
import Graphic from '../Graphic.js';
import MyImage from './MyImage.js';
export default class ImageProxy extends Graphic {
fileName;
extent;
image;//被代理的js Image对象
constructor(ctx,fileName,extent) {
super(ctx );
this.fileName=fileName;
this.extent=extent;
}
Draw() {
if(this.image==null)
{
console.log(` 图片Draw代理第一次被实际调用 `);
this.LoadImage();
}else{
console.log(` 图片Draw已经被初始化后调用 `);
return this.image.Draw();
}
}
GetExtent()
{
if(this.image==null)
{
console.log(` 获取图片的大小 ,用于计算站位空间`);
return this.extent;
}
return this.image.GetExtent();
}
LoadImage(){
this.image=new MyImage(this.ctx,this.fileName);
this.image.Load();
}
}
4.3 Subject(Graphic)
- 定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
export default class Graphic {
ctx;
constructor(ctx) {
this.ctx=ctx;
}
Draw() {
}
GetExtent()
{
}
Store()
{
}
Load()
{
}
}
4.4 RealSubject(Image)
- 定义Proxy所代表的实体。
import Graphic from '../Graphic.js';
export default class MyImage extends Graphic {
fileName;
image;//被代理的js Image对象
constructor(ctx,fileName) {
super(ctx );
this.fileName=fileName;
}
Draw() {
console.log(` 图片被画到画布上 `);
this.ctx.drawImage(this.image,0,0);
}
GetExtent()
{
return {height:this.image.height,width:this.image.width} ;
}
async Load(){
console.log(` 图片加载。。。 `);
this.image =new Image();
this.image.src = this.fileName;
this.image.onload=() => {
this.Draw();
}
}
}
4.6 Client
import ImageProxy from './Graphic/impl/ImageProxy.js';
export default class Client{
main(ctx){
let imageProxy =new ImageProxy(ctx,'./hb.jpg',{height:950,width:1200});
/**从代理获取图片大小**/
console.log(` 第一次调用: 图片Draw `);
imageProxy.GetExtent();
imageProxy.Draw();
/**模拟等待三秒后,实际调用**/
setTimeout(() => {
console.log(` 第二次调用:图片Draw `);
imageProxy.GetExtent();
imageProxy.Draw();
} , 3000 )
}
}
4.7 测试HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="module" >
import Client from './Client.js';
var x=document.getElementById("mycanvas")
var ctx=x.getContext("2d") //create 2d object
let cl=new Client();
cl.main(ctx)
</script>
</head>
<body>
<canvas id="mycanvas" width=900px height=900px></canvas>
</body>
</html>
测试结果:
Client.js:7 第一次调用: 图片Draw
ImageProxy.js:26 获取图片的大小 ,用于计算站位空间
ImageProxy.js:15 图片Draw代理第一次被实际调用
MyImage.js:21 图片加载。。。
MyImage.js:12 图片被画到画布上
Client.js:13 第二次调用:图片Draw
ImageProxy.js:18 图片Draw已经被初始化后调用
MyImage.js:12 图片被画到画布上
五、源代码下载
下载链接:https://pan.baidu.com/s/1XuPqp84cccBNVkbnMY3sKw
提取码:q2ut