上一篇博客讲了一下单例和工厂模式,这两种设计模式,我个人感觉讲的还算是比较容易理解的,不过暂时不理解其实也没有啥问题,总归是需要个过程的。
初期想用的时候,其实直接按照例子套用模板公式就可以了,代码写的多了,有了一定的源码阅读量之后,相对来说,就是比较容易理解了,到时候也算是能够有自己一定的心得体会了。
好了,废话不多说,咱们先来说说今天要讲的第一个设计模式-模板方法模式。
##模板方法
假设现在有一批豪车的模型需要建造,一种是宝马,一种是奔驰,订购商要求呀,模型车在启动的时候,需要有点火、鸣笛、熄火三个步骤,按照程序构造,我们写出了如下的代码:
首先我们定义名车的接口类ICar:
/***
* @desc 汽车类接口
* @author Aby
* @date 2020/07/26
*/
public abstract class ICar {
/***
* 点火
*/
public abstract void start();
/***
* 熄火
*/
public abstract void stop();
/***
* 鸣笛
*/
public abstract void alarm();
/***
* 启动
*/
public abstract void run();
}
然后对应的奔驰实现类BenzCar:
/***
* @desc 奔驰
* @author Aby
* @date 2020/07/26
*/
public class BenzCar extends ICar {
@Override
public void start() {
System.out.println("奔驰车发动");
}
@Override
public void stop() {
System.out.println("奔驰车停止");
}
@Override
public void alarm() {
System.out.println("奔驰车鸣笛");
}
@Override
public void run() {
this.start();
this.alarm();
this.stop();
}
}
对应的宝马实现类BmvCar :
/***
* @desc 宝马
* @author Aby
* @date 2020/07/26
*/
public class BmvCar extends ICar {
@Override
public void start() {
System.out.println("宝马车发动");
}
@Override
public void stop() {
System.out.println("宝马车停止");
}
@Override
public void alarm() {
System.out.println("宝马车鸣笛");
}
@Override
public void run() {
this.start();
this.alarm();
this.stop();
}
}
然后是对应的测试类App:
public class App {
public static void main(String[] args) {
BenzCar benzCar = new BenzCar();
benzCar.run();
BmvCar bmvCar = new BmvCar();
bmvCar.run();
}
}
执行测试类,我们能够看到打印出了对应的发动、鸣笛、熄火的三个过程,但是仔细看看我们的宝马类和奔驰类,run
方法的代码,完全是重复的,所以这一点儿,我们完全可以将这块儿的代码提出来,所以改造如下:
名车的接口类ICar修改如下:
/***
* @desc 汽车类接口
* @author Aby
* @date 2020/07/26
*/
public abstract class ICar {
/***
* 发动
*/
public abstract void start();
/***
* 停止
*/
public abstract void stop();
/***
* 鸣叫
*/
public abstract void alarm();
/***
* 启动
*/
public final void run() {
this.start();
this.alarm();
this.stop();
}
}
奔驰实现类BenzCar修改如下:
/***
* @desc 奔驰
* @author Aby
* @date 2020/07/26
*/
public class BenzCar extends ICar {
@Override
public void start() {
System.out.println("奔驰车发动");
}
@Override
public void stop() {
System.out.println("奔驰车停止");
}
@Override
public void alarm() {
System.out.println("奔驰车鸣笛");
}
}
宝马实现类BmvCar修改如下 :
/***
* @desc 宝马
* @author Aby
* @date 2020/07/26
*/
public class BmvCar extends ICar {
@Override
public void start() {
System.out.println("宝马车发动");
}
@Override
public void stop() {
System.out.println("宝马车停止");
}
@Override
public void alarm() {
System.out.println("宝马车鸣笛");
}
}
对应的测试类App我们不做修改:
public class App {
public static void main(String[] args) {
BenzCar benzCar = new BenzCar();
benzCar.run();
BmvCar bmvCar = new BmvCar();
bmvCar.run();
}
}
重新执行对应的测试类,我们能够发现代码完全是没有问题的,依然能够得到我们想要的结果。
而这种抽出来共同点,交给具体子类来实现具体细节的方法,就是所谓的模板方法了,这种设计模式其实用的还是挺多的,在spring和mybatis我们常用的两种框架中运用的也是到处都是,这一点儿等到下次更新博文的时候,会进行详细的说明。
好了,模板方法讲完了,接下来我们就来说说代理模式了。
代理模式
生活在社会中,我们很多时候都是会遇到各种各样的问题,比如买房这种事情,现在房价越来越贵,想买一套房子基本上掏空三代人,甚至于一二三线城市,即便是掏空三代人说不定也付不起个首付,就说我吧,从毕业还没有开始就在南京混,现在工作了五年了,还是买不起南京的一套房,其中缘由自不必说,毕竟说多了咱怕和谐,言多必失还是很有道理的。
好了,不扯了,咱们就从买房这件事情来说吧,想要买房需要了解各种政策,而且真正买房之后,假如说是买套二手房,还需要校验一系列原房主的身份产权等等,再加上去银行贷款,各种杂七杂八的事情,一般人想搞懂也不容易,所以这个时候,我们可以找一个中介,让中介来搞定中间的一系列环节,到时候想买房只需要签个字就可以了。而中介的做的事情,其实就是代理的一种过程。
静态代理
好了,我们按照上面的思路,来进行代码的编写,咱们先看不用代理的时候的代码:
首先定义一个人购买房子的接口IPerson:
/***
* @desc 买房接口
* @author Aby
* @date 2020/07/26
*/
public interface IPerson {
/***
* 买房的方法
* @param userName 用户
*/
void buyHouse();
}
具体的实现类Person:
/***
* @desc 具体买房类
* @author Aby
* @date 2020/07/26
*/
public class Person implements IPerson {
private String userName;
public Person(String userName) {
this.userName = userName;
}
@Override
public void buyHouse() {
System.out.println(this.userName + "买房");
}
}
测试类App:
public class App {
public static void main(String[] args) {
IPerson person = new Person("张三");
person.buyHouse();
}
}
执行对应的测试类,我们能够看到,打印出了张三买房的过程。
上面的实现,是没有使用代理的,那么我们现在使用下代理的模式进行代码开发:
首先以上的接口和实现类,都不改变,我们新增代理类ProxyPerson:
/***
* @desc 代理类
* @author Aby
* @date 2020/07/26
*/
public class ProxyPerson implements IPerson {
private IPerson iPerson = null;
public ProxyPerson(IPerson _iPerson) {
this.iPerson = _iPerson;
}
@Override
public void buyHouse() {
this.iPerson.buyHouse();
}
}
然后我们修改下对应的App测试类:
public class App {
public static void main(String[] args) {
IPerson person = new Person("张三");
ProxyPerson proxyPerson = new ProxyPerson(person);
proxyPerson.buyHouse();
}
}
运行测试类,我们能够看到,打印出的与之前是一样的,而这就是代理,代理的意义就是不用自己亲力亲为,只需要指定对应的人去处理即可,在我们的生活场景中,其实处处是代理,买房、代练游戏、搬运,各种杂七杂八的不一而足,不过我们之前的代码,实现的只是最简单的一种代理模式,这种模式常常被称为静态代理。
有静态代理,那么自然是有动态代理,我们下面就来说说动态代理。
动态代理
动态代理实现的方式以我所知的,就是两种,一种是实现InvocationHandler接口,一种是cglib实现,cglib的这种我们就不说了,我们这次主要是来说说实现InvocationHandler接口的代理。
如同上面的静态代理,如果说我们有很多的接口,都需要产生代理类的话,那么对应的代理类就是很多,这导致代码量不断增加,但是做的事情其实都是差不多的,所以这个时候动态代理自然是孕育而生。
首先新增动态代理类PersonInvocationHandler:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/***
* @desc 动态代理类
* @author Aby
* @date 2020/07/26
*/
public class PersonInvocationHandler implements InvocationHandler {
//被代理者
Class cls = null;
//被代理的实例
Object obj = null;
//需要代理的
public PersonInvocationHandler(Object _object) {
this.obj = _object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj, args);
return result;
}
}
改造对应的App测试类:
import java.lang.reflect.Proxy;
public class App {
public static void main(String[] args) {
IPerson person = new Person("张三");
PersonInvocationHandler invocationHandler = new PersonInvocationHandler(person);
//获得类的class loader
ClassLoader cl = person.getClass().getClassLoader();
//动态产生一个代理者
IPerson proxy = (IPerson) Proxy.newProxyInstance(cl, person.getClass().getInterfaces(), invocationHandler);
proxy.buyHouse();
}
}
执行以上的代码,我们能够看到,同样的能够实现我们想要的方法,而且上面的代理类,不仅仅能够代理买房这个接口,还能够代理其他的各种接口。
以上就是InvocationHandler实现的动态代理了,cglib咱们这里就不说了,不过最基本的差异点我们要了解,就是InvocationHandler代理的是接口,cglib是不需要的,而且InvocationHandler和cglib代理的方式也不一样,这个以后有机会再说。
好了,这就是今天说的模板方法模式和代理模式了,其实设计模式不需要太过刻意去用,其实有句话说的好:“看山是山;看山不是山;看山还是山。”真正的高手是运用起设计模式并不是刻意去用的,而是写出来代码,自然而然的就成为设计模式了,说实话这种牛人我倒是认识一个,可惜人家不认识我哈。
好了,这一篇博文到这里就结束了,下一讲我们就来看看,这两篇讲的设计模式在一些流行框架中的使用。