对象的功能扩展方式(继承、装饰者设计模式、动态代理设计模式)

【继承】、【装饰者模式】、【动态代理模式】

● 这三种都是给对象扩展功能的。

下面我们通过同一个需求来对三种模式的讲解:


1.【继承】(extends) 来实现

继承就不用多讲了,我们直接上代码

    1.1 解决问题

 需求:有一个Winter(侍者),它有server(服务)功能,我们需要这个Winter对象增加两个功能[ welcome(欢迎)  goodBye(再见)]

/**
 * 侍者
 */
class Winter{
	public void server(){
		System.out.println("服务...");
	}
}

/**
 * 男侍者  继承侍者类
 */
class ManWinter extends Winter{
	//增加功能
    public void welcome(){
        System.out.println("欢迎光临...");
    }

    public void goodBye(){
        System.out.println("再见...");
    }
}

/**
 * 测试
 */
public class Test{
	public static void main(String[] args) {
		ManWinter winter = new ManWinter();  //创建一个男侍者对象
        winter.welcome();    
		winter.server();
        winter.goodBye();
	}
}

运行结果:

       欢迎光临...

       服务...

       再见...

 

可见达到了扩展功能的目的。

    1.2 【继承】(extends)总结

缺点

            1)被扩展功能的类(Winter类)是不能修改的(写死的)。

            2)  扩展功能的类(ManWinter类)是不能修改的(写死的)。

这两个限制,使我们用起来十分的不方便(都不能修改,就不够灵活)。

     1.3  知识延伸

这时候我们为了提供更方便的开发模式,就要想,能不能将被扩展功能的类(Winter类)变成可以改变的?

答案当然是能,这就出现了下面的装饰者模式。


2.【装饰者模式】 来实现

装饰者模式:字面意思,对某东西进行装饰。  这里的意思是对被扩展功能类进行装饰(增加功能)

   2.1  装饰者模式实例

FileInputStream fis = new FileInputStream("F:/a.jpg");    //创建一个文件输出流对象
BufferedInputStream b = new BufferedInputStream(fis);    //给流添加缓冲区功能
ObjectInputStream o = new ObjectInputStream(b);        //给流添加对象序列化功能


        上面就是通过装饰者模式来完成的给文件输出流对象添加功能(缓冲区功能,对象序列化功能)的操作。

   2.2  装饰者模式设计

下面我们通过装饰者模式来自己设计:

需求: 有一个Winter(侍者),它有server(服务)功能,我们需要这个Winter对象增加两个功能[ welcome(欢迎)  goodBye(再见)]

装饰者有一个小口诀:是你(is a)还有你(has a),一切拜托你(user a)

/**
 * 侍者
 */
interface Winter{
	public void server(); //侍者原有方法
}

/**
 * 男侍者   (这里以男侍者为例,凡是Winter的子类对象都可以被MyDecorate装饰)
 */
class ManWinter implements Winter{
	@Override
	public void server() {
		System.out.println("服务");  
	}
}

/**
 * 侍者装饰类        是你还有你,一切拜托你
 */
class MyDecorate implements Winter{  //是你(MyDecorate本身是一个Winter的子类对象)
	//这是底层对象,被增强的对象
	private Winter winter;	//还有你(MyDecorate中有Winter的实例化对象) 

	public MyDecorate(Winter winter){ //通过构造器传递底层对象!
		this.winter = winter;
	}

	//一切拜托你(不是增强的地方,就让winter来完成)
	@Override
	public void server() {
		winter.server();
	}
	
	
	/*
	 * 增强点 (这里就是要增强的功能)
	 */
	public void welcome(){
		System.out.println("欢迎...");
	}
	public void goodBye(){
		System.out.println("再见...");
	}
	
}

/**
 * 测试
 */
public class Test{
	public static void main(String[] args) {
		ManWinter manWinter = new ManWinter();	//创建男侍者对象
		
		/*
		 * 创建一个装饰者对象,将男侍者传递给装饰者对象
		 *   (要执行【一切拜托你】的操作)
		 *   
		 *   现在这个装饰者对象就增加了两个方法(welcome()和goodBye())
		 *   达到了增强功能的目的
		 */
		//所有Winter的子类对象都可以被MyDecorate所装饰
		MyDecorate myDecorate = new MyDecorate(manWinter);	
		myDecorate.welcome();	
		myDecorate.server();
		myDecorate.goodBye();
	}
}

执行结果:

       欢迎光临...

       服务...

       再见...

整理一下整体思路:

    1)  创建一个Winter接口

    2)  创建一个实现Winter接口了的ManWinter类

    3)  创建一个装饰类MyDecorate

    4)测试:

            ● 创建一个被增强对象(Winter的子类对象) 

            ● 创建一个装饰类对象(MyDecorate)

            ● 将Winter的子类对象(manWinter)传递给装饰类(MyDecorate)构造器

            ● 最终得到了一个装饰对象,这个对象就有了增强的方法welcome()和goodBye()

看到了这里,你可以再看一下上面的缓冲区装饰文件输入流的代码,应该可以发现具体步骤是一样的。对比一下:

//给文件输入流对象增加缓冲区功能
FileInputStream fis = new FileInputStream("f:/1.txt");
BufferedInputStream buf = new BufferedInputStream(fis);
       
//给manWinter增加功能 
ManWinter manWinter = new ManWinter();
MyDecorate myDecorate = new MyDecorate(manWinter);

    2.3 【装饰者模式】总结:

        优点:被扩展功能的类是可以修改的(想要装饰哪个对象,就将这个对象传入装饰类的构造方法中,得到装饰对象,这个装饰对象就有需要增强的功能  注意:只能传入与装饰类实现了同一接口的对象)。

         缺点:扩展功能的类(MyDecorate类)是不能修改的(也就是要增强的功能,是写死的)。

装饰者模式什么时候用呢?  在不知道被增强对象的具体类型时,就可以使用装饰者模式!

     2.4 知识延伸

那么问题来了,能不能将扩展功能的类也变为可修改的(或者说是可选择的)呢?答案肯定是:可以的    这就有了下面的动态代理模式。


3.【动态代理模式】(Proxy)来实现

    3.1 使用的类介绍 

         3.1.1  java.lang.reflect.Proxy (与动态设置相关的类)

获取此类对象的方法:static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

参数介绍:详细可查阅JDK API

           ● ClassLoader loader  :类加载器,负责加载类的对象。

           ● Class[] interfaces  :接口数组,里面存放的都是目标对象实现的接口们的Class

           ● InvocationHandler h   :java.lang.reflect.InvocationHandler是代理实例的调用处理程序 实现的接口。

       3.1.2 java.lang.reflect.InvocationHandler (是代理实例的调用处理程序 实现的接口)

此接口下的唯一抽象方法:Object invoke(Object proxy , Method method , Object[] args)

参数介绍:详细可查阅JDK API

           ● Object proxy   :目标对象(需要被增强的对象)

           ● Method method  :对应于在代理实例上调用的接口方法的Method实例

           ● Object[] args   :包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为null

这里的类和参数介绍如果看不懂没关系,我们直接上代码,看完代码再返回这里更深入的理解。

    3.2  解决问题

需求: 有一个Winter(侍者),它有server(服务)功能,我们需要这个Winter对象增加两个功能[ welcome(欢迎) 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 侍者
 */
interface Winter{
	public void server();
}

/**
 * 前置增强
 */
interface Before{
	public void before();
}

/**
 * 后置增强
 */
interface After{
	public void after();
}

/**
 * 动态代理工厂
 */
class ProxyFactory{
	private Object targetObject;  //目标对象  被增强对象
	private Before before;	//前置对象
	private After after;	//后置对象
	
	/**
	 * 创建动态代理对象
	 * @return 动态代理对象
	 */
	public Object createProxy(){
		/*
		 * 1.给出三大参数
		 */
		ClassLoader loader = this.getClass().getClassLoader();	//得到类加载器
		Class[] interfaces = targetObject.getClass().getInterfaces();  //得到目标对象实现的接口们
		InvocationHandler h = new InvocationHandler() { 
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				if(before != null) //如果前置对象不为空
					before.before();	//调用前置对象的方法
				Object result = method.invoke(targetObject, args); //调用目标对象的方法
				if(after != null)	//如果后置对象不为空
					after.after();	//调用后置对象的方法
				return result;	//返回调用目标对象的方法得到的结果
			}
		};
		
		/*
		 * 2.通过三大参数创建动态代理对象
		 */
		Object proxyObject = Proxy.newProxyInstance(loader, interfaces, h);
		return proxyObject;
	}
	
	public Object getTargetObject() {
		return targetObject;
	}
	public void setTargetObject(Object targetObject) {
		this.targetObject = targetObject;
	}
	public Before getBefore() {
		return before;
	}
	public void setBefore(Before before) {
		this.before = before;
	}
	public After getAfter() {
		return after;
	}
	public void setAfter(After after) {
		this.after = after;
	}
}

/**
 * 测试
 */
public class Test{
	public static void main(String[] args) {
		//1.创建动态代理工厂对象
		ProxyFactory factory = new ProxyFactory();
		
		//2.设置目标对象
		Winter targetObject = new Winter(){ //创建目标对象
			@Override
			public void server() {
				System.out.println("服务...");
			}};
		factory.setTargetObject(targetObject);
		
		//3.设置前置增强对象
		Before before = new Before() {
			@Override
			public void before() {
				System.out.println("欢迎光临...");
			}
		};
		factory.setBefore(before);
		
		//4.设置后置增强对象
		After after = new After() {
			@Override
			public void after() {
				System.out.println("再见...");
			}
		};
		factory.setAfter(after);
		
		//5.得到动态代理对象
		Object proxyObject = factory.createProxy();
		
		//6.将动态代理对象强转为目标对象类型
		Winter winterObject = (Winter) proxyObject;
		
		//7.调用目标对象方法
		winterObject.server();
	}
}

代码执行结果:

         欢迎光临...

         服务...

         再见...

整理一下思路:

       1)创建一个目标对象所实现的接口

       2)创建一个前置增强对象所实现的接口

       3)创建一个后置增强对象所实现的接口

       4)创建一个动态对象工厂类:

              ● 创建 目标对象、前置增强对象、后置增强对象

              ● 得到三大参数(Proxy.newProxyInstance()所需要的参数)  现在可以到上面去再看一下类与参数介绍

              ● 创建动态代理对象

       5)测试:

              ● 创建动态代理工厂对象

              ● 创建并设置 目标对象、前置增强对象、后置增强对象

              ● 通过动态代理工厂的createProxy()方法获得最终的动态代理对象

    3.3【动态代理模式】总结:

优点

            1)被扩展功能的类可以变化。

            2)  扩展功能的类也可以变化。

通俗讲就是给x增加y功能,x和y都可以动态的设置,灵活性大大的提高了。

这些可变的东西还可以用配置文件来代替,操作将更为灵活。


4.【继承】、【装饰者模式】、【动态代理模式】之间的比较

  继承 装饰者模式 动态代理模式
作用 增加功能(给x添加y功能) 增加功能(给x添加y功能) 增加功能(给x添加y功能)
优点   x可变 x和y都可变
缺点 x和y都不可变 y不可变  
灵活性

 

 

 

你可能感兴趣的:(设计模式)