在上一篇文章Fun论设计模式之2:代理模式(Proxy Pattern)中,我们知道了代理模式本质上就是对类的访问过程做同样类型的控制。
那里有提到,把不同模块的API也放到代理类中,并给这些API做相同的前置处理,就可以减少日后的工作量。这个处理,也涉及到一个设计模式——外观模式。
搬运runoob的描述:
意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
何时使用: 1、客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可。 2、定义系统的入口。
如何解决:客户端不与系统耦合,外观类与系统耦合。
关键代码:在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。
外观模式在Java web的Spring系统之中有很大体现。Spring使用maven理清类包依赖关系和调用顺序,还隐藏了相当复杂的依赖注入等过程,开发者只需调用过滤器(拦截器),写好MVC三层结构,没有繁杂的初始化过程。这就是外观模式。
华为云API调用类这里,也是把代理过程和具体的初始化过程给封装了,外部类只需拿到VPCUtil执行业务,或者被封装的业务函数本身。
在上一节,我们把虚拟私有云的API类封装了一遍,如果每个业务的API类都要封装,那么执行过程是这样的:
图1. 用0.2版本TokenProxy代理之后,华为云API模块操作流程图
实际上还可以再优化一下,把4种业务的API封装到一起,因为这几种业务使用的token是一样的,都是从华为云身份验证服务拿到的(何时使用)。
为了减少代理类的代码量,我们先声明一个SuperUtil类,然后把这4种业务API类都作为子类继承(关键代码):
1 public abstract class SuperUtil { 2 protected String username; 3 protected String password; 4 protected String token; 5 protected String projectId; 6 protected long overDate; 7 }业务API父类
这些属性,业务类就无需再次声明了。
1 package zhyx_cloud; 2 3 import java.lang.reflect.Method; 4 5 import org.springframework.cglib.proxy.Enhancer; 6 import org.springframework.cglib.proxy.MethodInterceptor; 7 import org.springframework.cglib.proxy.MethodProxy; 8 9 public class TokenProxy { 10 11 private VPCUtil VPCProxy = null; 12 13 private ECSUtil ECSProxy = null; 14 15 private SecurityGroupUtil SecurityGroupProxy = null; 16 17 private PublicIPUtil PublicIPProxy = null; 18 19 private MethodInterceptor interceptorFactory(String username, String password){ 20 return new MethodInterceptor() { 21 @Override 22 public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 23 SuperUtil superUtil = (SuperUtil)sub; 24 if(superUtil.token == null || System.currentTimeMillis() > superUtil.overDate) { 25 TokenAndProject tap = new IAMUtil().getToken(username,password); 26 VPCUtil myproxy = (VPCUtil)sub; 27 myproxy.token = tap.getToken(); 28 myproxy.projectId = tap.getProjectId(); 29 myproxy.overDate = System.currentTimeMillis() + 23*60*60*1000; 30 } 31 Object res = methodProxy.invokeSuper(sub, objects); 32 return res; 33 } 34 }; 35 } 36 37 public TokenProxy(String username, String password) { 38 Enhancer enhancer = new Enhancer(); 39 enhancer.setSuperclass(VPCUtil.class); 40 enhancer.setCallback(this.interceptorFactory(username, password)); 41 this.VPCProxy= (VPCUtil)enhancer.create(); 42 enhancer = new Enhancer(); 43 enhancer.setSuperclass(ECSUtil.class); 44 enhancer.setCallback(this.interceptorFactory(username, password)); 45 this.ECSProxy = (ECSUtil)enhancer.create(); 46 enhancer = new Enhancer(); 47 enhancer.setSuperclass(SecurityGroupUtil.class); 48 enhancer.setCallback(this.interceptorFactory(username, password)); 49 this.SecurityGroupProxy = (SecurityGroupUtil)enhancer.create(); 50 enhancer = new Enhancer(); 51 enhancer.setSuperclass(PublicIPUtil.class); 52 enhancer.setCallback(this.interceptorFactory(username, password)); 53 this.PublicIPProxy = (PublicIPUtil)enhancer.create(); 54 } 55 56 public VPCUtil getVPCProxy() { 57 return this.VPCProxy; 58 } 59 60 public ECSUtil getECSProxy() { 61 return ECSProxy; 62 } 63 64 public SecurityGroupUtil getSecurityGroupProxy() { 65 return SecurityGroupProxy; 66 } 67 68 public PublicIPUtil getPublicIPProxy() { 69 return PublicIPProxy; 70 } 71 72 }TokenProxy v0.3
把这些工作(关键代码)都处理完之后,外部类调用这个类,就可以只通过这个类就拿到4个业务模块的函数,并且这些函数无需重复验证了(主要解决)。
图2. 用0.3版本TokenProxy代理之后,华为云API模块操作流程图
优点: 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。(如果还有其他类型的业务API,代理类的初始化过程代码会更加繁杂)
使用场景: 1、为复杂的模块或子系统提供外界访问的模块。 2、子系统相对独立。 3、预防低水平人员带来的风险。
注意事项:在层次化结构中,可以使用外观模式定义系统中每一层的入口。