设计模式代理模式-模板方法模式-命令模式

设计模式代理模式-模板方法模式-命令模式

  • 代理模式
    • 基本介绍:
      • 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
      • 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的独享
      • 代理模式有不同的三种形式:静态代理、动态代理(jdk代理、接口代理)、Cglib代理(可以在内存中动态的创建代理而不需要实现接口,它是属于动态代理的范畴)
    • 静态代理
      • 基本介绍:静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者继承相同的父类。

      • 代码

          public interface ITeacherDao {
          	public void teach();
          }
          public class TeacherDao implements ITeacherDao {
          	public void teach() {
          		System.out.println("老师授课中。。。");
          	}
          }
          public class TeacherDaoProxy implements ITeacherDao{
          	private ITeacherDao target;//目标对象通过接口来聚合
          	public TeacherDaoProxy(ITeacherDao target) {
          		this.target = target;
          	}
          	@Override
          	public void teach() {
          		System.out.println("代理开始");
          		target.teach();
          		System.out.println("代理结束");
          	}
          }
          public class Client {
          	public static void main(String[] args) {
          		new TeacherDaoProxy(new TeacherDao()).teach();
          	}
          }
        
      • 静态代理的优缺点

        • 优点:在不修改目标对象的功能前提下,通过代理对象对目标功能扩展
        • 缺点:因为代理目标需要与目标对象实现一样的接口,所以会有很多代理类
        • 一旦接口增加方法,目标对象与代理对象都要维护。
    • 动态代理
      • 基本介绍

        • 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
        • 代理对象的生成,是利用jdk的api,动态的在内存中构建对象
        • 动态代理也叫做:jdk代理、接口代理
      • 代码

          public interface ITeacherDao {
          	public void teach();
          
          }
          public class TeacherDao implements ITeacherDao {
          	public void teach() {
          		System.out.println("老师授课中。。。");
          	}
          }
          import java.lang.reflect.InvocationHandler;
          public class ProxyFactory {
          	public Object target;
          	//构建构造器的时候对目标对象进行初始化
          	public ProxyFactory(Object target) {
          		this.target = target;
          	}
          	//get目标对象生成一个代理对象
          	public  Object getProxyInstance() {
          		/*
          		 * loader 指定当前目标对象使用的类加载器
          		 * interfaces 目标对象实现的接口类型使用泛型的方式确认类型
          		 * h 事件处理执行目标对象的方法时,会触发事件处理器方法
          		 */
          		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
          				target.getClass().getInterfaces(),
          				new InvocationHandler() {
          			@Override
          			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          				System.out.println("JDK代理开始");
          				//通过反射机制调用目标对象的代理方法
          				Object returnVal = method.invoke(target, args);
          				System.out.println("JDK代理提交");
          				return returnVal;
          			}
          		});
          
          	}
          }
          public class Client {
          	public static void main(String[] args) {
          		ITeacherDao object=(ITeacherDao) new ProxyFactory(new TeacherDao()).getProxyInstance();
          		//内存中生成了代理对象
          		System.out.println(object.getClass());
          		object.teach();
          	}
          }
        
    • Cglib代理
      • 基本介绍

        • 静态代理和jdk代理模式都要求目标对象是实现一个接口,但是目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理。
        • Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理叫做动态代理。
        • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口,它广泛的许多aop框架使用,例如spring aop,实现方法拦截。
        • 在aop编程中如何选择代理模式:
          1. 目标对象需要实现接口,用jdk代理
          2. 目标对象不需要实现接口,用Cglib代理
        • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
      • 代码

          public class TeacherDao {
          	public void teach() {
          		System.out.println("老师授课中。。。我是cglib代理不需要实现接口");
          	}
          }
          import java.lang.reflect.Method;
          import net.sf.cglib.proxy.Enhancer;
          import net.sf.cglib.proxy.MethodInterceptor;
          import net.sf.cglib.proxy.MethodProxy;
          
          public class ProxyFactory implements MethodInterceptor{
          	private Object target;
          	public ProxyFactory(Object target) {
          		this.target = target;
          	}
          	//返回一个代理对象是target对象的代理对象
          	public Object getProxyInstance() {
          		//1、创建一个工具类
          		Enhancer enhancer=new Enhancer();
          		//2、设置父类
          		enhancer.setSuperclass(target.getClass());
          		//3、设置回调函数
          		enhancer.setCallback(this);
          		//4、创建子类对象即代理对象
          		return enhancer.create();
          	}
          	
          	//会调用目标对象的方法
          	@Override
          	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
          		System.out.println("cglib代理开始");
          		Object interVal = method.invoke(target, args);
          		System.out.println("cglib代理结束");
          		return interVal;
          	}
          }
          public class Client {
          	public static void main(String[] args) {
          		TeacherDao object=(TeacherDao) new ProxyFactory(new TeacherDao()).getProxyInstance();
          		object.teach();
          	}
          }
        
      • 代理模式的几种变体

        • 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
        • 缓存代理:比如:当请求图片文件资源时,先到缓存代理取,如果取到资源则ok,如果娶不到资源,再到公网或者数据库取,然后缓存。
        • 远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息
        • 同步代理:主要使用在多线程编程中,完成多线程间同步工作
  • 模板方法模式
    • 基本介绍:

      • 模板方法模式:又叫模板模式,在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
      • 简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。
      • 这中类型的设计模式属于行为型模式。
    • 代码

        //抽象类表示豆浆
        public abstract class SoyaMilk {
        	//模板方法做成final不让子类去覆盖
        	public final void make() {
        		select();
        		addCondiments();
        		soka();
        		beat();
        	}
        	//选材料
        	private void select() {
        		System.out.println("第一步选择好的黄豆");
        	}
        	//添加不同的材料,抽象方法子类具体实现
        	protected abstract void addCondiments();
        	
        	private void soka() {
        		System.out.println("第三步黄豆和配料开始浸泡");
        	}
        	private void beat() {
        		System.out.println("第四步黄豆和配料放到豆浆机去打碎");
        	}
        }
        public class RedbeanSoyaMilk extends SoyaMilk {
        	@Override
        	protected void addCondiments() {
        		System.out.println("第二步添加红豆配料");
        	}
        }
        public class PeanutSoyaMilk extends SoyaMilk {
        
        	@Override
        	protected void addCondiments() {
        		System.out.println("第二步添加花生配料");
        	}
        }
        public class Client {
        	public static void main(String[] args) {
        		System.out.println("制作红豆豆浆");
        		SoyaMilk readBeansoyaMilk =new RedbeanSoyaMilk();
        		readBeansoyaMilk.make();
        		System.out.println("制作花生豆浆");
        		SoyaMilk peanutSoyaMilk =new PeanutSoyaMilk();
        		peanutSoyaMilk.make();
        	}
        }
      
    • 模板方法模式的钩子方法

      • 在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为钩子。
    • 代码

        对上述的SoyaMilk进行改造
        public abstract class SoyaMilk {
        	//模板方法做成final不让子类去覆盖
        	public final void make() {
        		select();
        		if(customerWantCondiments()) {
        			addCondiments();
        		}
        		soka();
        		beat();
        	}
        	//选材料
        	private void select() {
        		System.out.println("第一步选择好的黄豆");
        	}
        	//添加不同的材料,抽象方法子类具体实现
        	protected abstract void addCondiments();
        
        	private void soka() {
        		System.out.println("第三步黄豆和配料开始浸泡");
        	}
        	private void beat() {
        		System.out.println("第四步黄豆和配料放到豆浆机去打碎");
        	}
        	//钩子方法决定是否需要添加配料
        	boolean customerWantCondiments() {
        		return true;
        	}
        }
        public class PureSoyaMilk extends SoyaMilk {
        	@Override
        	protected void addCondiments() {
        		System.out.println("不添加任何配料");
        	}
        	@Override
        	boolean customerWantCondiments() {
        		return false;
        	}
        }
        public class Client {
        	public static void main(String[] args) {
        		System.out.println("制作纯豆浆");
        		SoyaMilk readBeansoyaMilk =new PureSoyaMilk();
        		readBeansoyaMilk.make();
        	}
        }
      
    • 模板方法的注意事项和细节

      • 基本思想:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
      • 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
      • 既统一了算法,也提供了很大的灵性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
      • 该模式的不足之处:每一个不同的实现需要一个子类实现,导致类的个数增加,使得系统更加庞大。
      • 一般模板方法加上final关键字,防止子类重写模板方法。
      • 模板方法的使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理。
  • 命令模式
    • 基本介绍:

      • 命令模式:在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体请求操作者即可,此时,可以使用命令模式来进行设计。
      • 命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
      • 在命令模式中会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持撤销的操作。
      • 通俗易通的理解:将军发布命令,士兵去执行。其中有几个角色:将军(发布命令者)、士兵(命令执行者)、命令(连接将军和士兵)Invoker是调用者(将军),Receiver是被调用者(士兵)、 MyCommand是命令,实现了Command接口,持有接受对象。
    • 代码

        //创建命令接口
        public interface Command {
        	//执行动作
        	public void excute();
        	//撤销动作
        	public void undo();
        }
        /**
         * 它没有任何命令即空执行,用于初始化每个按钮,当调用
         * 空命令时对象什么都不做,这也是一种设计模式,这可以省
         * 掉空的判断。
         * @author Administrator
         *
         */
        public class NoCommand implements Command{
        	@Override
        	public void excute() {
        	}
        	@Override
        	public void undo() {
        	}
        }
        public class LightOnCommand implements Command{
        	private LightReceiver light;
        	public LightOnCommand(LightReceiver light) {
        		this.light = light;
        	}
        	@Override
        	public void excute() {
        		//调用接收者的方法
        		light.on();
        	}
        	@Override
        	public void undo() {
        		light.off();
        	}
        }
        public class LightOffCommand implements Command{
        	private LightReceiver light;
        	public LightOffCommand(LightReceiver light) {
        		this.light = light;
        	}
        	@Override
        	public void excute() {
        		//调用接收者的方法
        		light.off();
        	}
        	@Override
        	public void undo() {
        		light.on();
        	}
        }
        public class LightReceiver {
        	public void on() {
        		System.out.println("电灯打开");
        	}
        	public void off() {
        		System.out.println("电灯关闭");
        	}
        }
        public class RemoteController {
        	//开按钮的命令数组
        	private Command[] onCommands;
        	//关按钮的命令数组
        	private Command[] offCommands;
        	Command undoCommand;
        	public RemoteController() {
        		onCommands=new Command[5];
        		offCommands=new Command[5];
        		for (int i = 0; i < 5; i++) {
        			onCommands[i]=new NoCommand();
        			offCommands[i]=new NoCommand();
        		}
        	}
        	//给我们的按钮设置需要的命令
        	public void setCommand(int no,Command onCommand,Command offCommand) {
        		onCommands[no]=onCommand;
        		offCommands[no]=offCommand;
        	}
        	//按下开的按钮
        	public void onButtonWasPushed(int no) {
        		onCommands[no].excute();
        		undoCommand=onCommands[no];
        	}
        	//按下关的按钮
        	public void offButtonWasPushed(int no) {
        		offCommands[no].excute();
        		undoCommand=offCommands[no];
        	}
        	//按下撤销的按钮
        	public void undoButtonWasPushed() {
        		undoCommand.undo();
        	}
        }
        public class Client {
        	public static void main(String[] args) {
        		LightReceiver lightReceiver = new LightReceiver();
        		LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
        		LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
        		RemoteController remoteController = new RemoteController();
        		remoteController.setCommand(0, lightOnCommand, lightOffCommand);
        		System.out.println("按下电灯打开的按钮");
        		remoteController.onButtonWasPushed(0);
        		System.out.println("按下电灯关闭的按钮");
        		remoteController.offButtonWasPushed(0);
        		System.out.println("按下撤销的按钮");
        		remoteController.undoButtonWasPushed();
        	}
        }
      
    • 命令模式注意事项和细节

      • 将发起请求的对象和执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的工作,也就是说:“请求发起者”和请求执行者“之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
      • 容易设计一个命令队列。只要命令对象放到列队,就可以多线程的执行命令。
      • 容易实现对请求的撤销和重做。
      • 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在使用的时候要注意。
      • 空命令也是一种设计模式,它为我们省去了判空的操作,在上面的实例中,如果没有空命令,我们没按下一个按键都要判空,这给我们编码带来了一定的麻烦。
      • 命令模式经典的应用场景:界面的一个按钮就是一个命令、模拟cmd(dos命令)、订单的撤销和恢复、触发-反馈机制。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值