设计模式是一个软件设计师必须掌握的技能,笔者在目前的项目开发过程中并没有过多的使用,深感惋惜,借此文章总结下Java中常用设计模式相关知识点,为今后的项目开发预先铺路。
设计模式可以说不是新的知识,是对开发流程等相关过程的简化,类似于Spring等框架,是为开发者省力的,系统越大作用越明显。
Java中的设计模式通常分为三类:
- 创建型,用于通过特定方式生成对象实例,例如单例模式、建造者模式、工厂模式、抽象工厂模式等;
- 结构型,用于将多个对象组织成更大的结构,例如代理模式、适配器模式、门面模式、桥接模式等;
- 行为型,用于帮助系统间各对象的通信或者控制复杂系统中的流程,例如观察者模式、命令模式、中介者模式等;
本文依次介绍如下设计模式:
- 单例模式;
- 工厂模式;
- 抽象工厂模式;
- 代理模式;
- 命令模式;
- 策略模式;
- 门面模式;
- 桥接模式;
- 观察者模式;
- 建造者模式;
- 拦截器模式;
- 责任链模式;
1.单例模式
意义:在开发过程中有些类创建多个实例是没有意义的,例如数据源的SessionFactory,相反创建多个实例还会加大系统的额外开销。
实现(程序模拟实现一个SessionFactory单例对象):
package com.lsm1998.test01;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:19:15
* 说明:单例模式Demo
*/
public class MySessionFactory
{
private static MySessionFactory sessionFactory;
private MySessionFactory()
{
}
public static MySessionFactory getInstance()
{
if (sessionFactory == null)
{
sessionFactory = new MySessionFactory();
}
return sessionFactory;
}
public static void main(String[] args)
{
MySessionFactory sessionFactory1 = MySessionFactory.getInstance();
MySessionFactory sessionFactory2 = MySessionFactory.getInstance();
// 返回true
System.out.println(sessionFactory1==sessionFactory2);
}
}
2.工厂模式
意义:笔者认为工厂模式的意义有两个,如下
- 增强创建对象的灵活性,相对于new关键字来说,每次创建一次不同的对象需要使用一次new,灵活性较强;
- 降低对象之间调用关系的耦合程度,例如A类调用B实例的方法,使用传统的new关键字需要在代码中使用new类名来获取对象,当后期出现一个C类来取代B类时则需要修改所有的B类,而使用工厂模式则可以绕过类名来创建对象;
实现(程序实现一个汽车工厂类,根据创建对象传入的参数创建对应的实例):
package com.lsm1998.test02;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:19:15
* 说明:工厂模式 顶层接口
*/
public interface Car
{
String getCarName();
}
********************分割线********************
package com.lsm1998.test02;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:19:23
* 说明:工厂模式 底层实现类
*/
public class Benz implements Car
{
@Override
public String getCarName()
{
return "奔驰";
}
}
********************分割线********************
package com.lsm1998.test02;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:19:22
* 说明:工厂模式 底层实现类
*/
public class BWM implements Car
{
@Override
public String getCarName()
{
return "宝马";
}
}
********************分割线********************
package com.lsm1998.test02;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:19:50
* 说明:模拟拥有一辆车的人
*/
public class People
{
private Car car;
public People(Car car)
{
this.car = car;
}
public void showCar()
{
/**
* 不使用工厂模式的代码
* 拥有宝马车的情况
* car=new BWM();
* 拥有奔驰车的情况
* car=new Benz();
*/
System.out.println("我的车是:" + car.getCarName());
}
}
********************分割线********************
package com.lsm1998.test02;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:19:24
* 说明:汽车工厂
*/
public class CarFactory
{
/**
* 根据传入的汽车名称生产对应的汽车对象
*
* @param carName
* @return
*/
public Car getCar(String carName) throws Exception
{
if ("BWM".equalsIgnoreCase(carName))
{
return new BWM();
} else if ("Benz".equalsIgnoreCase(carName))
{
return new Benz();
} else
{
throw new Exception("没有名为:" + carName + "的汽车");
}
}
public static void main(String[] args) throws Exception
{
CarFactory factory=new CarFactory();
Car car1=factory.getCar("bwm");
System.out.println(car1.getCarName());
Car car2=factory.getCar("benz");
System.out.println(car2.getCarName());
People people1=new People(car1);
people1.showCar();
People people2=new People(car2);
people2.showCar();
// 模拟对象生产异常
Car car3=factory.getCar("xxx");
System.out.println(car3.getCarName());
}
}
3.抽象工厂
意义:简单工厂模式直接生产对象,而抽象工厂模式不会直接生产对象,只是提供一个接口,具体的工厂再实现这个接口。
简而言之,工厂模式直接生产对象,而抽象工厂根据生产的目标对象先匹配对应的对象工厂,再由匹配的对象工厂生产目标对象。
实现(BWM和Benz类、Car接口与工厂模式相同,此处省略):
package com.lsm1998.test03;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:20:41
* 说明:抽象工厂接口
*/
public interface AbstractFactory
{
Car createCar(String carName);
}
************分割线************
package com.lsm1998.test03;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:20:46
* 说明:负责生产奔驰的工厂
*/
public class BenzFactory implements AbstractFactory
{
@Override
public Car createCar(String carName)
{
return new Benz();
}
}
************分割线************
package com.lsm1998.test03;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:20:45
* 说明:负责生产宝马的工厂
*/
public class BWMFactory implements AbstractFactory
{
@Override
public Car createCar(String carName)
{
return new BWM();
}
}
************分割线************
package com.lsm1998.test03;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:20:48
* 说明:Base工厂,负责目标对象与实例工厂的匹配工作
*/
public class BaseFactory implements AbstractFactory
{
@Override
public Car createCar(String carName)
{
AbstractFactory factory = null;
if ("BWM".equalsIgnoreCase(carName))
{
factory = new BWMFactory();
} else if ("Benz".equalsIgnoreCase(carName))
{
factory = new BenzFactory();
}
if (factory != null)
{
return factory.createCar(carName);
}
return null;
}
}
************分割线************
package com.lsm1998.test03;
/**
* 作者:刘时明
* 日期:2018/11/25
* 时间:20:55
* 说明:抽象工厂测试
*/
public class Test
{
public static void main(String[] args)
{
BaseFactory factory=new BaseFactory();
Car car1=factory.createCar("bwm");
System.out.println(car1.getCarName());
Car car2=factory.createCar("benz");
System.out.println(car2.getCarName());
}
}
4.代理模式
意义:笔者认为代理模式最大的作用在于提供一个中介者,例如创建一个系统开销很大、耗时较长的对象,客户端在发出创建改对象的请求后又迫切需要使用该对象,此时就可以额外创建一个代理对象先返回给客户端,避免了无谓的等待,代理模式还有一个很重要的使用是AOP代理,可以在执行一个方法前后插入一些通用的操作,例如JDBC事务。
代理模式的实现:JDK动态代理或者CGLIB动态代理都可以实现,二者的区别在于前者依赖于接口,不依赖第三方jar包,而后者不依赖于接口,但依赖于第三方jar包(笔者认为CGLIB动态代理更为强大、适用)
实现(程序通过JDK动态代理和CGLIB动态代理分别实现方法执行的事务切入):
package com.lsm1998.test04;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:13:29
* 说明:模拟事务的类
*/
public class TxMath
{
public void beginTx()
{
System.out.println("---开始事务---");
}
public void endTx()
{
System.out.println("---结束事务---");
}
}
************分割线************
package com.lsm1998.test04;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:12:42
* 说明:Math顶层接口
*/
public interface MyMath
{
int add(int a, int b);
int sub(int a, int b);
void log();
}
************分割线************
package com.lsm1998.test04;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:13:29
* 说明:MyMath接口实现类
*/
public class MyMathImpl implements MyMath
{
@Override
public int add(int a, int b)
{
return a + b;
}
@Override
public int sub(int a, int b)
{
return a - b;
}
@Override
public void log()
{
System.out.println("log日志方法执行");
}
}
************分割线************
package com.lsm1998.test04;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:13:31
* 说明:基于JDK内置功能的动态代理的处理类
*/
public class MyInvocationHandler implements InvocationHandler
{
// 需要被代理的对象
private Object object;
public void setObj(Object object)
{
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
TxMath txMath = new TxMath();
txMath.beginTx();
Object result = method.invoke(object, args);
System.out.println("result=" + result);
txMath.endTx();
return result;
}
}
************分割线************
package com.lsm1998.test04;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:15:54
* 说明:不依赖于任何接口的Math类
*/
public class DifferentMath
{
public int add(int a, int b)
{
return a + b;
}
}
************分割线************
package com.lsm1998.test04;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:15:44
* 说明:基于Cglib jar包的动态代理处理类
*/
public class CglibInvocationHandler implements MethodInterceptor
{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
{
TxMath math = new TxMath();
math.beginTx();
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("result=" + result);
math.endTx();
return result;
}
}
************分割线************
package com.lsm1998.test04;
import net.sf.cglib.proxy.Enhancer;
import java.lang.reflect.Proxy;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:15:17
* 说明:动态代理对象工厂,传入一个实例,获取其对应的代理对象
*/
public class MyProxyFactory
{
public static Object getProxy(Object target)
{
MyInvocationHandler handler = new MyInvocationHandler();
// 设置代理对象
handler.setObj(target);
// 传入的参数分别是:当前类加载器,target类的父接口Class对象数组,JDK动态代理的处理类
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
}
public static Object getCglibtProxy(Object target)
{
CglibInvocationHandler handler = new CglibInvocationHandler();
// 获取CGLIB enhancer增强类对象
Enhancer enhancer = new Enhancer();
// 设置代理对象类型
enhancer.setSuperclass(target.getClass());
// 定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
enhancer.setCallback(handler);
// 生成并返回代理对象
return enhancer.create();
}
}
************分割线************
package com.lsm1998.test04;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:14:12
* 说明:测试类
*/
public class Test
{
public static void main(String[] args)
{
try
{
// JDK动态代理
MyMath proxy=new MyMathImpl();
MyMath math=(MyMath)MyProxyFactory.getProxy(proxy);
System.out.println(math.add(2,4));
math.log();
// CGLIB动态代理
DifferentMath proxy2=new DifferentMath();
DifferentMath differentMath=(DifferentMath)MyProxyFactory.getCglibtProxy(proxy2);
System.out.println(differentMath.add(2,4));
}catch (Exception e)
{
System.err.println("生成代理对象出现错误");
}
}
}
5.命令模式
意义:可以把一次处理行为作为参数传递给一个方法,一次处理行为就可以看作一个命令,命令模式最直观的好处在于它的灵活性和解耦性,只需要修改少量的命令就可以替换系统中全部应用该命令的操作。
笔者看来,命令模式的实现就是运用匿名内部类,将一次命令封装到一个接口中,再到处理方法将该接口作为参数传入,处理方法只需执行命令接口方法即可。
实现(程序实现一个通过命令模式自由指定处理行为的数组处理类):
package com.lsm1998.test05;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:16:36
* 说明:命令接口
*/
public interface Command<E>
{
E resultMethod(int[] target);
}
************分割线************
package com.lsm1998.test05;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:16:35
* 说明:需要使用命令处理的类
*/
public class TargetArray
{
public <E> E arrMethod(int[] target, Command<E> cmd)
{
// 直接调用命令接口的方法返回
return cmd.resultMethod(target);
}
}
************分割线************
package com.lsm1998.test05;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:16:37
* 说明:测试类
*/
public class Test
{
public static void main(String[] args)
{
TargetArray pa = new TargetArray();
int[] target = {5, 8, 9, 2, 4, 7};
// 通过命令模式完成求和操作
int result1 = pa.arrMethod(target, t ->
{
int sum = 0;
for (int temp : t)
{
sum += temp;
}
return sum;
});
// 通过命令模式完成求积操作
int result2 = pa.arrMethod(target, t ->
{
int product = 1;
for (int temp : t)
{
product *= temp;
}
return product;
});
System.out.println("result1=" + result1);
System.out.println("result2=" + result2);
}
}
6.策略模式
意义:策略模式用于封装系列的算法,客户端程序可以自由匹配一种算法,达到算法的自由切换。
实现(模拟一个商品折扣策略,对于不同的用户会有不同的折扣策略,使用策略模式可以灵活切换折扣算法):
package com.lsm1998.test06;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:20:48
* 说明:折扣策略接口
*/
public interface DiscountStrategy
{
double getDisCount(double originPrice);
}
************分割线************
package com.lsm1998.test06;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:20:50
* 说明:默认策略类
*/
public class DefaultDiscount implements DiscountStrategy
{
@Override
public double getDisCount(double originPrice)
{
return originPrice * 0.7;
}
}
************分割线************
package com.lsm1998.test06;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:20:50
* 说明:VIP策略类
*/
public class VipDiscount implements DiscountStrategy
{
@Override
public double getDisCount(double originPrice)
{
return originPrice * 0.5;
}
}
************分割线************
package com.lsm1998.test06;
/**
* 作者:刘时明
* 日期:2018/11/26 0026
* 时间:20:51
* 说明:折扣策略的匹配类
*/
public class DiscountContext
{
private DiscountStrategy strategy;
/**
* 定制的折扣策略
* @param strategy
* @return
*/
public DiscountContext(DiscountStrategy strategy)
{
this.strategy = strategy;
}
/**
* 默认的折扣策略
*/
public DiscountContext()
{
this.strategy = new DefaultDiscount();
}
public double getDiscountPrice(double price)
{
return this.strategy.getDisCount(price);
}
public void chageDiscount(DiscountStrategy strategy)
{
this.strategy = strategy;
}
}
************分割线************
package com.lsm1998.test06;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:20:55
* 说明:测试类
*/
public class Test
{
public static void main(String[] args)
{
int num=100;
// 使用默认的折扣策略
DiscountContext context=new DiscountContext();
System.out.println(context.getDiscountPrice(num));
// 使用指定的VIP折扣策略
DiscountContext context2=new DiscountContext(new VipDiscount());
System.out.println(context2.getDiscountPrice(num));
// 切换到默认的折扣策略
context2.chageDiscount(new DefaultDiscount());
System.out.println(context2.getDiscountPrice(num));
}
}
7.门面模式
意义:门面模式可以为一些类提供一个简化的接口,从而简化访问这些类的复杂性,只需要调用门面类的业务方法就可以实现以往一连串的业务调用。
实现(程序模拟实现一个点餐的业务逻辑,需要有支付、烹饪、送餐三个阶段):
package com.lsm1998.test07;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:21:50
* 说明:支付接口
*/
public interface Payment
{
String pay();
}
************分割线************
package com.lsm1998.test07;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:21:54
* 说明:支付接口实现类
*/
public class PaymentImpl implements Payment
{
@Override
public String pay()
{
String food="快餐";
System.out.println("你已经向收银员支付了费用,你购买的食物是:"+food);
return food;
}
}
************分割线************
package com.lsm1998.test07;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:21:50
* 说明:烹饪接口
*/
public interface Cook
{
String cook(String food);
}
************分割线************
package com.lsm1998.test07;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:21:57
* 说明:烹饪接口实现类
*/
public class CookImpl implements Cook
{
@Override
public String cook(String food)
{
System.out.println("厨师正在烹饪:"+food);
return food;
}
}
************分割线************
package com.lsm1998.test07;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:22:00
* 说明:服务员送餐接口
*/
public interface Waiter
{
void service(String food);
}
************分割线************
package com.lsm1998.test07;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:22:01
* 说明:服务员送餐接口实现类
*/
public class WaiterImpl implements Waiter
{
@Override
public void service(String food)
{
System.out.println("服务员已经将:" + food + "端过来了,请慢用");
}
}
************分割线************
package com.lsm1998.test07;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:22:10
* 说明:提供点餐服务的门面类
*/
public class Facade
{
// 封装提供业务逻辑的三个类
private Payment payment;
private Cook cook;
private Waiter waiter;
public Facade()
{
this.payment = new PaymentImpl();
this.cook = new CookImpl();
this.waiter = new WaiterImpl();
}
/**
* 封装点餐业务方法
*/
public void serveFood()
{
String food=payment.pay();
cook.cook(food);
waiter.service(food);
}
}
************分割线************
package com.lsm1998.test07;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:22:05
* 说明:测试类
*/
public class Test
{
public static void main(String[] args)
{
// 创建三个对象
Payment payment=new PaymentImpl();
Cook cook=new CookImpl();
Waiter waiter=new WaiterImpl();
// 依次调用三个方法实现一次点餐业务
String food=payment.pay();
cook.cook(food);
waiter.service(food);
// 使用门面模式
Facade facade=new Facade();
facade.serveFood();
}
}
8.桥接模式
意义:是一种结构型模式,可以将类的多维度变化分离,降低类的多维度变化带来的额外开销,减少类的设计中的臃肿程度。
例如现实生活中的面馆菜单,有牛肉面、猪肉面,顾客可以根据口味选择辣味和不辣,那么就有四个对应的实例对象,分别是辣味牛肉面、不辣牛肉面、辣味猪肉面、不辣猪肉面,此时使用桥接模式可以分离辣味风格和面的品种。
实现:
package com.lsm1998.test08;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:23:48
* 说明:辣味风格接口
*/
public interface Peppery
{
String style();
}
************分割线************
package com.lsm1998.test08;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:23:49
* 说明:加辣风格
*/
public class PepperyStyle implements Peppery
{
@Override
public String style()
{
return "辣味很重,很过瘾";
}
}
************分割线************
package com.lsm1998.test08;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:23:50
* 说明:不加辣风格
*/
public class PlainStyle implements Peppery
{
@Override
public String style()
{
return "味道清淡,有点痿,吃不了辣";
}
}
************分割线************
package com.lsm1998.test08;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:23:52
* 说明:辣味风格+面条品种的组合抽象类
*/
public abstract class AbstractNodele
{
protected Peppery style;
public AbstractNodele(Peppery style)
{
this.style=style;
}
public abstract void eat();
}
************分割线************
package com.lsm1998.test08;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:23:55
* 说明:牛肉面对象与辣味风格的组合类
*/
public class BeefNoodle extends AbstractNodele
{
public BeefNoodle(Peppery style)
{
super(style);
}
@Override
public void eat()
{
System.out.println("牛肉面条,辣味风格:" + super.style.style());
}
}
************分割线************
package com.lsm1998.test08;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:23:54
* 说明:猪肉面对象与辣味风格的组合类
*/
public class ProkyNoodle extends AbstractNodele
{
public ProkyNoodle(Peppery style)
{
super(style);
}
@Override
public void eat()
{
System.out.println("猪肉面条,辣味风格:" + super.style.style());
}
}
************分割线************
package com.lsm1998.test08;
/**
* 作者:刘时明
* 日期:2018/11/26
* 时间:23:56
* 说明:测试类
*/
public class Test
{
public static void main(String[] args)
{
// 辣味牛肉面
AbstractNodele nodele1=new BeefNoodle(new PepperyStyle());
nodele1.eat();
// 不辣牛肉面
AbstractNodele nodele2=new BeefNoodle(new PlainStyle());
nodele2.eat();
// 辣味猪肉面
AbstractNodele nodele3=new BeefNoodle(new PepperyStyle());
nodele3.eat();
// 不辣猪肉面
AbstractNodele nodele4=new BeefNoodle(new PlainStyle());
nodele4.eat();
}
}
9.观察者模式
意义:当对象之间存在一对多依赖关系时,使用观察者模式可以让一个或者多个对象观察一个主题对象,当主题对象的状态发生变化时,系统能通知所有依赖于此对象的观察者对象,从而达到自动更新的目的,典型实现是发布/订阅的JMS机制。
例如我有一部手机打算发布到二手交易市场低价出售,而交易市场是多种多样的,此时我每到一个交易市场发布出售信息都需要自己跑一趟去告知,当手机在某个市场完成交易后有需要去其他市场删除出售信息,而使用观察者模式则可以让多个交易市场观察我这台二手手机对象,我只需要发布一次出售信息或者出售完毕信息,观察的交易市场会将我的交易信息自动更新上去。
实现:
package com.lsm1998.test09;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:10:58
* 说明:观察者接口
*/
public interface MyObserver
{
void update(Observable o,Object arg);
}
************分割线************
package com.lsm1998.test09;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:11:07
* 说明:二手交易市场 1
*/
public class Market1 implements MyObserver
{
@Override
public void update(Observable o, Object arg)
{
System.out.println("这里是交易市场1,刚刚有位买家发布了一个信息:" + arg);
}
}
************分割线************
package com.lsm1998.test09;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:11:07
* 说明:二手交易市场 2
*/
public class Market2 implements MyObserver
{
@Override
public void update(Observable o, Object arg)
{
System.out.println("这里是交易市场2,刚刚有位先生发布了一个交易信息:" + arg);
}
}
************分割线************
package com.lsm1998.test09;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:11:08
* 说明:二手交易市场 3
*/
public class Market3 implements MyObserver
{
@Override
public void update(Observable o, Object arg)
{
System.out.println("这里是交易市场3,刚刚有位帅哥发布了一个信息:" + arg);
}
}
************分割线************
package com.lsm1998.test09;
import java.util.ArrayList;
import java.util.List;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:11:02
* 说明:被观察者的抽象基类
*/
public abstract class Observable
{
// 存储对应的观察者
List<MyObserver> observerList = new ArrayList<>();
// 注册一个观察者
public void registObserver(MyObserver o)
{
observerList.add(o);
}
// 删除一个观察者
public void removeObserver(MyObserver o)
{
observerList.remove(o);
}
// 被观察者对其对应观察者的一次更新通知
public void notifyObservers(Object value)
{
for (MyObserver o : observerList)
{
o.update(this, value);
}
}
}
************分割线************
package com.lsm1998.test09;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:11:09
* 说明:准备出售的二手手机
*/
public class MyPhone extends Observable
{
public void release()
{
notifyObservers("发布了出售信息,欢迎大家购买");
}
public void remove()
{
notifyObservers("手机已经卖出了,谢谢大家支持");
}
}
************分割线************
package com.lsm1998.test09;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:11:18
* 说明:测试类
*/
public class Test
{
public static void main(String[] args)
{
// 创建我的二手手机对象,它是被观察者
MyPhone phone=new MyPhone();
// 创建所有交易市场对象,他们是观察者
Market1 m1=new Market1();
Market2 m2=new Market2();
Market3 m3=new Market3();
// 注册观察者
phone.registObserver(m1);
phone.registObserver(m2);
phone.registObserver(m3);
// 发布出售信息
phone.release();
// 假设此时手机已经在市场2被抢购了,此时需要在市场1、3发布信息
System.out.println("--------------------");
phone.removeObserver(m2);
phone.remove();
phone.registObserver(m2);
}
}
10.建造者模式
意义:建造者模式的作用是用来分步创建复杂的对象,典型的应用是Mybatis的SqlSessionFactory对象的生成,有基础的读者可以发现SqlSessionFactoryBuilder类的build方法参数列表略显多样,既可以传入代表配置文件的Configuration对象,也可以传入输入流等等,Mybatis框架就是为其专门创建一个构造器SqlSessionFactoryBuilder,将SqlSessionFactory的构建过程和表示分开,隐藏了复杂对象的生成过程,降低了开发复杂度。
实现(程序模拟实现一个门票对象通过建造者模式的生成):
package com.lsm1998.test10;
import java.util.ArrayList;
import java.util.List;
/**
* 作者:刘时明
* 日期:2018/10/22
* 时间:23:29
* 说明:门票对象
*/
public class Ticket
{
// 门票类型
private List<String> types;
// 门票价格
private double price;
public Ticket()
{
types=new ArrayList<>();
}
public double getPrice()
{
return price;
}
public void setPrice(double price)
{
this.price = price;
}
public List<String> getTypes()
{
return types;
}
public void setTypes(List<String> types)
{
this.types = types;
}
@Override
public String toString()
{
return "Ticket{" +
"types=" + types +
", price=" + price +
'}';
}
}
*************分割线*************
package com.lsm1998.test10;
/**
* 作者:刘时明
* 日期:2018/10/22
* 时间:23:20
* 说明:门票构建帮助类
*/
public class TicketHelper
{
private Ticket ticket;
public TicketHelper()
{
this(100.0);
}
public TicketHelper(double price)
{
this.ticket = new Ticket();
this.ticket.setPrice(price);
}
public void buildAdult(boolean adult)
{
if (adult)
{
this.ticket.getTypes().add("成人");
} else
{
this.ticket.getTypes().add("儿童");
this.ticket.setPrice(ticket.getPrice() * 0.5);
}
}
public void buildForSeat(boolean forSeat)
{
if (forSeat)
{
this.ticket.getTypes().add("有座");
} else
{
this.ticket.getTypes().add("无座");
this.ticket.setPrice(ticket.getPrice() * 0.75);
}
}
public void buildSoldier(boolean soldier)
{
if (soldier)
{
this.ticket.getTypes().add("军人及其家属");
this.ticket.setPrice(ticket.getPrice() * 0.75);
} else
{
this.ticket.getTypes().add("非军人及其家属");
}
}
public Ticket getTicket()
{
return ticket;
}
}
*************分割线*************
package com.lsm1998.test10;
/**
* 作者:刘时明
* 日期:2018/10/22
* 时间:23:29
* 说明:门票构建对象
*/
public class TicketBuilder
{
public static Ticket builder(TicketHelper helper)
{
return helper.getTicket();
}
}
*************分割线*************
package com.lsm1998.test10;
/**
* 作者:刘时明
* 日期:2018/10/22
* 时间:23:22
* 说明:测试类
*/
public class Test
{
public static void main(String[] args)
{
// 创建门票构建帮助类
TicketHelper helper=new TicketHelper();
// 设置门票对象属性,可以封装成一个配置类
helper.buildAdult(true);
helper.buildSoldier(true);
helper.buildForSeat(true);
// 通过构建对象获取实例
Ticket ticket=TicketBuilder.builder(helper);
System.out.println(ticket);
}
}
11.拦截器模式
意义:拦截器模式与代理模式是十分类似的,二者的实现过程也是基本一致,都是通过JDK动态代理(当然基于Cglib的动态代理除外),相对不同的是,责任链模式需要绑定一个拦截器接口,该接口提供拦截过程中的逻辑处理方法,拦截器模式的作用不仅限于此,可以通过链式绑定拦截器实现一个责任链,详情请看‘12责任链模式’。
实现(程序模拟实现一个事务切入):
package com.lsm1998.test11;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:23:44
* 说明:模拟Dao接口
*/
public interface Dao
{
void save(Object o);
void delete(Long id);
}
************分割线************
package com.lsm1998.test11;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:23:45
* 说明:Dao接口实现类
*/
public class DaoImpl implements Dao
{
@Override
public void save(Object o)
{
System.out.println("保存一个对象:" + o);
}
@Override
public void delete(Long id)
{
System.out.println("删除一个对象,id=" + id);
}
}
************分割线************
package com.lsm1998.test11;
import java.lang.reflect.Method;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:23:37
* 说明:自定义拦截器接口
*/
public interface Interceptor
{
// 前置处理方法
void before(Object proxy, Object target, Method method, Object[] args);
// 后置处理方法
void after(Object proxy, Object target, Method method, Object[] args);
}
************分割线************
package com.lsm1998.test11;
import java.lang.reflect.Method;
/**
* 作者:刘时明
* 日期:2018/11/28
* 时间:0:05
* 说明:自定义拦截器实现类
*/
public class MyInterceptor implements Interceptor
{
@Override
public void before(Object proxy, Object target, Method method, Object[] args)
{
System.out.println("开启事务");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args)
{
System.out.println("提交事务");
}
}
************分割线************
package com.lsm1998.test11;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:23:39
* 说明:通过JDK动态代理实现拦截功能
*/
public class InterceptorJdkProxy implements InvocationHandler
{
// 真实对象
private Object target;
// 拦截器全限定名(包名+类名)
private String interceptorClass;
public InterceptorJdkProxy(Object target, String interceptorClass)
{
this.target = target;
this.interceptorClass = interceptorClass;
}
/**
* 绑定委托对象并返回一个【代理占位】
*
* @param target 真实对象名
* @param interceptorClass 拦截器全限定名
* @return 代理对象【代理占位】
*/
public static Object bind(Object target, String interceptorClass)
{
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InterceptorJdkProxy(target, interceptorClass));
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
if (interceptorClass == null)
{
// 没有设置拦截器则直接反射原有方法
return method.invoke(target, args);
}
Object result;
// 通过反射生成拦截器
Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).getConstructor().newInstance();
// 设置前置方法
interceptor.before(proxy, target, method, args);
// 反射原有对象方法
result = method.invoke(target, args);
// 调用后置方法
interceptor.after(proxy, target, method, args);
return result;
}
}
************分割线************
package com.lsm1998.test11;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:23:43
* 说明:测试
*/
public class Test
{
public static void main(String[] args)
{
Dao proxy=(Dao) InterceptorJdkProxy.bind(new DaoImpl(),"com.lsm1998.test11.MyInterceptor");
// 模拟保存持久化一个对象
proxy.save(new Object());
// 模拟删除持久化一个对象
proxy.delete(1l);
}
}
责任链模式
意义:责任链模式就是在拦截器模式中多个拦截器链式绑定,类似于A拦截,而B拦截C,C拦截D,此时D对象就会被C、B、A三个对象拦截,由此,责任链模式拥有比代理模式更加强大的前置、后置处理功能,责任链模式比较典型的应用是Struts框架,Struts中配置功能、传参数、设置参数等大部分功能都是通过一系列拦截器完成的。
实现(Interceptor接口和InterceptorJdkProxy类沿用拦截器模式中的程序):
package com.lsm1998.test12;
import com.lsm1998.test11.Interceptor;
import java.lang.reflect.Method;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:23:54
* 说明:拦截器1
*/
public class Interceptor1 implements Interceptor
{
@Override
public void before(Object proxy, Object target, Method method, Object[] args)
{
System.out.println("拦截器1的前置方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args)
{
System.out.println("拦截器1的后置方法");
}
}
************分割线************
package com.lsm1998.test12;
import com.lsm1998.test11.Interceptor;
import java.lang.reflect.Method;
/**
* 作者:刘时明
* 日期:2018/11/28
* 时间:0:08
* 说明:拦截器2
*/
public class Interceptor2 implements Interceptor
{
@Override
public void before(Object proxy, Object target, Method method, Object[] args)
{
System.out.println("拦截器2的前置方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args)
{
System.out.println("拦截器2的后置方法");
}
}
************分割线************
package com.lsm1998.test12;
import com.lsm1998.test11.Interceptor;
import java.lang.reflect.Method;
/**
* 作者:刘时明
* 日期:2018/11/27
* 时间:23:54
* 说明:拦截器3
*/
public class Interceptor3 implements Interceptor
{
@Override
public void before(Object proxy, Object target, Method method, Object[] args)
{
System.out.println("拦截器3的前置方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args)
{
System.out.println("拦截器3的后置方法");
}
}
************分割线************
package com.lsm1998.test12;
import com.lsm1998.test11.Dao;
import com.lsm1998.test11.DaoImpl;
import com.lsm1998.test11.InterceptorJdkProxy;
/**
* 作者:刘时明
* 日期:2018/11/28
* 时间:0:08
* 说明:测试类
*/
public class Test
{
public static void main(String[] args)
{
Dao proxy1 = (Dao) InterceptorJdkProxy.bind(new DaoImpl(), "com.lsm1998.test12.Interceptor1");
Dao proxy2 = (Dao) InterceptorJdkProxy.bind(proxy1, "com.lsm1998.test12.Interceptor2");
Dao proxy3 = (Dao) InterceptorJdkProxy.bind(proxy2, "com.lsm1998.test12.Interceptor3");
proxy3.save(new String("一个实体类"));
}
}