Public class ViewAction implements Action
{
public void doAction()
{
//做View的动作
System.out.println(“You could view the information……”);
……
}
}
代理的意思很好理解,它借鉴了我们日常所用的代理的意思:就是本来该自己亲自去做的某件事,由于某种原因不能直接做,而只能请人代替你做,这个被你请来做事的人就是代理。比如过春节要回家,由于你要上班,没时间去买票,就得票务中介代你购买,这就是一种代理模式。这个情景可以形象的描述如下:
class:火车站
{
卖票:
{……}
}
火车站是卖票的地方,我们假设只能在火车站买到票。卖票的动作实质是火车站类完成的。
Class:票务中介
{
卖票:
{
收中介费;
火车站.卖票;
}
}
顾客找票务中介买票的时候,调用票务中介.卖票。票务中介其实做了两件事,一是去火车站买票,二是不能白帮你卖票,肯定要收中介费。而你得到的好处是不用直接去火车站买票,节省了买票的时间用来上班。 以上我们简单模拟了代理模式的情景和为什么要使用代理模式,下面我们以一个例子来具体分析一下JAVA中的代理模式。 假设有一个信息管理系统,用些用户有浏览信息的权限,有些用户有浏览、添加和修改信息的权限,还有些用户有除了上述的权限,还有删除信息的权限,那么我们最容易想到的做法如下: public class ViewAction
{
//由userId计算权限
……
String permission = ……;
if(permission.equals(Constants.VIEW))
{
System.out.println(“You could view the information……”);
……
}
}
其他的动作都和浏览信息的动作差不多。我们来看这样的类,很容易看出它的一些缺点来:第一、它把权限计算和动作执行都放在一个类里,两者的功能相互混在一起,容易造成思路的混乱,而且修改维护和测试都不好;一句话来说, 它不满足单一职责原则 。第二是客户调用的时候 依赖具体的类,造成扩展和运行期内的调用的困难,不满足依赖颠倒原则 ( 依赖倒转原则(DIP)尽量依赖 于 抽象, 定义一个接口去实现它, 不要依赖 于 具体 ) 。 既然有这么多的问题,我们有必要对该类进行重新设计。其实大家早已想到,这个类应该使用代理模式。是啊,和我们买火车票的动作一样,动作类不能直接执行那个动作,而是要先检查权限,然后才能执行;先检查权限,后执行的那各类其实就是一个代理类,修改后的代码如下:
public interface Action
{
public void doAction();
}
首先是设计一个接口,用来满足依赖颠倒原则。
Public class ViewAction implements Action
{
public void doAction()
{
//做View的动作
System.out.println(“You could view the information……”);
……
}
}
这个类跟火车站一样,是动作的真实执行者。 <!---->Public class ProxyViewAction implements Action
{
private Action action = new ViewAction();
public void doAction()
{
//调用权限类的方法取得用户权限
if(Permission.getPermission(userId).equals(Constants.VIEW))
{
action.doAction();
}
}
}
这是代理类,很容易理解。在我们的ProxyViewAction类中,除了做了客户真正想要做的动作:doAction()以外,还进行了 额外 的动作检查用户的权限。而作核心动作doAction()是在一个 干干净净 的类:ViewAction中进行,这个类只做 核心动作 ,对其他的不关心,满足了单一职责原则。 客户端通过调用代理类来执行动作 ,而代理类一是将权限判断和动作的执行分离开来,满足了单一职责原则;二是实现了 同 一个接口,从而满足了依赖颠倒原则。比第一个思路好了很多。 代理又被称为 委派 ,说的是代理类 并不真正的执行那个核心动作 ,而是委派给另外一个类去执行,如ProxyView类中,ProxyView类并没有真正执行doAction()方法,而是交给ViewAction类去执行。 我们再来看代理类ProxyViewAction,可以看到它不仅依赖于接口Action,而且依赖于具体的实现ViewAction。这样对我们的系统扩展很不利,比如我们有Add动作、Delete动作、Modify动作等等,我们需要对每一个动作都写一个代理类,而这些代理类都做同样的事情,先进行权限判断,然后再委派。所以我们需要对这些代理再进行一次抽象, 让它只依赖接口Action,而不依赖于具体的实现 。 要实现这样的想法,我们需要将代理类中的具体实现提走,让代理的使用者在运行期提供具体的实现类,即所谓的 依赖注入 ,如下:
Public class ProxyAction implements Action
{
private Action action;
public ProxyAction(Action action)
{
this.action = action;
}
public void doAction()
{
//调用权限类的方法取得用户权限
if(Permission.getPermission(userId).equals(action.getClass().getName()))
{
action.doAction();
}
}
}
这样,我们就将所有实现了Action接口的实现使用一个代理类来代理它们。除了ViewAction类能用,以后扩展的AddAction、 ModifyAction、DeleteAction类等等,都可以使用一个代理类:ProxyAction。 而我们的客户端类似如下: Action action = ProxyAction(new ViewAction); Action.doAction(); 通过对代理类的 依赖注入 ,我们使得代理类初步有了一定扩展性。但是我们还要看到,这个代理类依赖于某一个确定的接口。这仍然不能满足我们的实际要求,如我们的系统的权限控制一般是整个系统级的,这样系统级的权限控制,我们很难在整个系统里抽象出一个统一的接口,可能会有多个接口,按照上面的代理模式,我们需要对每一个接口写一个代理类,同样,这些类的功能都是一样的。这显然不是一个好地解决办法。 基于上面的原因,我们需要解决一个系统在没有统一的接口的情况下,对一些零散的对象的某一些动作使用代理模式的问题。JAVA API为我们引入了动态代理或动态委派的技术。 动态代理的核心是 InvocationHandler接口 ,要使用动态代理就必须实现该接口。这个接口的委派任务是在invoke(Object proxy, Method m, Object[] args)方法里面实现的: //invoke是调用的意思 //在调用核心功能之前作一些动作 …… //调用核心功能 m.invoke(obj, args); //在调用核心功能以后做一些动作 …… 我们可以看到动态代理其实用的是 反射机制 来调用核心功能的:m.invoke(obj, args);正是这种反射机制的使用使得我们调用核心功能更加灵活,而 不用依赖于某一个具体的接口,而是依赖于Object对象 。 下面我们来具体看看动态代理或动态委派如何使用:
public class ProxyAction implements InvocationHandler {
private Object action;
//构造方法
public ProxyAction(Object action)
{
this.action = action;
}
public static Object getInstance(Object action)
{
return Proxy.newProxyInstance(action.getClass().getClassLoader(),
action.getClass().getInterfaces(),new ProxyAction(action));
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
Object result;
try {
//在委派之前作动作,如权限判断等
System.out.println("before method " + m.getName());
//进行委派
result = m.invoke(action, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: "
+ e.getMessage());
} finally {
//在委派之后做动作
System.out.println("after method " + m.getName());
}
return result;
}
}
这个代理类,首先是实现了InvocationHandler接口;然后在getInstance()方法里得到了代理类的实例;在invoke()方法里实现代理功能,也很简单。 下面我们来看客户端: Action action = (Action)ProxyAction.getInstance(new ViewAction()); Action.doAction(); 我们可以看到代理类对接口的依赖也转移到了客户端上,这样,代理类不依赖于某个接口。对于同样的代理类ProxyAction,我们也可以有如下的客户端调用: Engine engine = (Engine)ProxyAction.getInstance(new EngineImpl()); Engine.execute(); 只要engineImpl类实现了Engine接口,就可以像上面那样使用。 现在我们可以看到,动态代理的确是拥有相当的灵活性。但我们同时也看到了,这个代理类写起来比较麻烦,而且也差不多每次都写这样千篇一律的东西,只有委派前的动作和委派后的动作在不同的代理里有着不同,其他的东西都需要照写。如果这样的代理类写多了,也会有一些冗余代理。需要我们进一步优化,这里我们使用模板方法模式来对这个代理类进行优化,如下:
<!---->public abstract class BaseProxy implements InvocationHandler {
private Object obj;
protected BaseProxy(Object obj)
{
this.obj = obj;
}
public static Object getInstance(Object obj,InvocationHandler instance)
{
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),instance);
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
Object result;
try {
System.out.println("before method " + m.getName());
this.doBegin();
result = m.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: "
+ e.getMessage());
} finally {
System.out.println("after method " + m.getName());
this.doAfter();
}
return result;
}
public abstract void doBegin();
public abstract void doAfter();
}
这样,代理的实现类只需要关注实现委派前的动作和委派后的动作就行,如下
public class ProxyImpl extends BaseProxy {
protected ProxyImpl(Object o)
{
super(o);
}
public static Object getInstance(Object foo)
{
return getInstance(foo,new ProxyImpl(foo));
}
//委派前的动作
public void doBegin() {
// TODO Auto-generated method stub
System.out.println("begin doing....haha");
}
//委派后的动作
public void doAfter() {
// TODO Auto-generated method stub
System.out.println("after doing.....yeah");
}
}
从上面的代码,我们可以看出代理实现类的确是简单多了,只关注了委派前和委派后的动作,这是我们作为一个代理真正需要关心的。 至此,代理模式和动态代理已经告一段落。我们将动态代理引申一点说开去,来作为这篇文章的蛇足。 这个话题就是面向方面的编程,或者说AOP。我们看上面的ProxyImpl类,它的两个方法doBegin()和doAfter(),这是做核心动作之前和之后的两个截取段。正是这两个截取段,却是我们AOP的基础。在OOP里,doBegin(),核心动作,doAfter()这三个动作在多个类里始终在一起,但他们所要完成的逻辑却是不同的,如doBegin()可能做的是权限,在所有的类里它都做权限;而在每个类里核心动作却各不相同;doAfter()可能做的是日志,在所有的类里它都做日志。正是因为在所有的类里,doBegin()或doAfter()都做的是同样的逻辑,因此我们需要将它们提取出来,单独分析、设计和编码,这就是我们的AOP的思想。 这样说来,我们的动态代理就能作为实现AOP的基础了。好了,就说这么多,关于AOP技术,我们可以去关注关于这方面的知识。 <!----><!----><!----><!----><!----><!---->
分享到:
相关推荐
第一卷《Pattern-Oriented Software Architecture: A System of Patterns》主要介绍了模式的概念和应用,为读者提供了理解和使用设计模式的基础。书中详细阐述了如何通过模式来组织和构建软件系统,以提高软件的可...
- 第一章:设计模式基础 - 第二章:单例模式 - 第三章:工厂方法模式 - 第四章:抽象工厂模式 - 第五章:建造者模式 - 第六章:原型模式 - 第七章:适配器模式 - 第八章:桥接模式 - 第九章:组合模式 - ...
这份“设计模式PPT”由教师宋艳在大四第一学期作为课程材料使用,旨在帮助学生理解和掌握设计模式在实际编程中的应用。 设计模式分为三大类:创建型、结构型和行为型。每种类型包含多种具体的模式,让我们逐一探讨...
当然还有一些提示和鼓励:第一次阅读此书时你可能不会完全理解它,但不必着急,我们在起初编写这本书时也没有完全理解它们!请记住,这不是一本读完一遍就可以束之高阁的书。我们希望你在软件设计过程中反复参阅...
Python基础(第1周) - **课程目标**:掌握Python基础语法,为后续学习奠定基础。 - **主要内容**: - Python初识:理解Python的哲学和特性。 - Python语法基础:包括变量、数据类型、运算符等。 - Python控制...
在Java或C#等面向对象语言中,代理模式通常分为静态代理和动态代理两种类型。 静态代理是在编译时就确定了代理类和目标类的关系,代理类和目标类需要有相同的接口或者继承自相同的父类。这种方式的灵活性较低,因为...
- AOP(面向切面编程)的理解,包括通知类型和代理模式。 - Spring Boot的快速开发特性,如自动配置。 10. **数据库相关**: - SQL查询语言:掌握基本的SELECT、INSERT、UPDATE、DELETE语句。 - JDBC编程:如何...
- 代理模式:静态代理和动态代理(JDK动态代理与CGLIB)的实现。 - 适配器模式、装饰者模式、策略模式等常见设计模式的使用场景。 5. **框架** - Spring:理解IoC容器和AOP原理,掌握Spring Boot和Spring Cloud...
### 尚硅谷_Java面试题第一季(最新) #### 知识点概览 尚硅谷推出的《Java面试题第一季》是一系列针对企业面试高频技术问题的视频教程。该课程覆盖了JavaSE、SSM(Spring、SpringMVC、MyBatis)、框架高级应用...
- 结构型:适配器模式、装饰器模式、代理模式。 - 行为型:策略模式、观察者模式、模板方法模式。 ### 总结 软件工程作为一门综合性的学科,其核心是确保软件项目的成功实施。通过上述章节的介绍,我们可以了解...
Java是一种广泛使用的面向对象的编程语言,以其平台独立性、高效性和丰富的类库而闻名。在面试和笔试中,Java开发者经常会遇到各种各样的问题,这些题目涵盖了语言基础、内存管理、多线程、集合框架、IO流、网络编程...
【疯狂Java面试题】是针对Java开发者的一份重要的学习资源,源自《疯狂Java讲义》第四版中的原版面试题目。这份资料集涵盖了广泛的Java编程和相关技术的知识点,旨在帮助求职者准备Java开发岗位的面试,同时也适用于...
#### 第1章:软件工程概述 - **软件工程概念**: - 定义:软件工程是一门研究如何采用系统化、规范化、可度量的方式来进行软件的开发、运行及维护的学科。 - 目标:提高软件质量和开发效率,降低开发成本和维护...
- 动态代理:Java动态代理机制及其应用。 7. **设计模式**: - 单例模式:懒汉式、饿汉式、双重检查锁定等实现方式。 - 工厂模式:简单工厂、工厂方法、抽象工厂。 - 适配器模式:类适配器与对象适配器。 - ...
第1章 引言 1 1.1 什么是设计模式 2 1.2 Smalltalk MVC中的设计模式 3 1.3 描述设计模式 4 1.4 设计模式的编目 5 1.5 组织编目 7 1.6 设计模式怎样解决设计问题 8 1.6.1 寻找合适的对象 8 1.6.2 决定对象的粒度 9 ...
- **代理模式**:为其他对象提供一种代理以控制对这个对象的访问。 3. **实战经验**: - **代码重构**:讲解如何通过改进代码结构来提高代码质量。 - **错误处理与异常**:介绍如何有效地捕获和处理程序中的错误...
- 代理:学习动态代理和静态代理,理解AOP(面向切面编程)。 - 装箱/拆箱:了解自动装箱和拆箱的机制,减少类型转换的繁琐。 - 静态引用:了解静态导入和静态方法的使用。 3. **JavaEE技术** - XML基础:理解...
它们包括创建型模式(如工厂模式、抽象工厂模式、单例模式)、结构型模式(如代理模式、装饰器模式、适配器模式)以及行为型模式(如观察者模式、策略模式、责任链模式)。 在JavaScript中,单例模式是一种常见的...