一、工厂模式 BeanFactory
1、简单工厂模型,是指由一个工厂对象决定创建哪一种产品类的实例,工厂类负责创建的对象较少,客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心
优点:
只需传入一个正确的参数,就可以获取你所需要的对象,无须知道其创建的细节
缺点:
工厂类的职责相对过重,增加新的产品时需要修改工厂类的判断逻辑,违背开闭原则,不易于扩展过于复杂的产品结构
/**
* @author : lssffy
* @Description : 家庭
* @date : 2023/12/18 23:36
*/
public interface IFamily {
/** 吃饭 */
public void eatingRice();
}
/**
* @author : lssffy
* @Description : joyous
* @date : 2023/12/18 23:37
*/
public class JoyousFamily implements IFamily{
public void eatingRice() {
System.out.println("Joyous family eating rice");
}
}
/**
* @author : lssffy
* @Description : tom
* @date : 2023/12/18 23:40
*/
public class TomFamily implements IFamily{
public void eatingRice() {
System.out.println("tom family eating rice");
}
}
/**
* @author : lssffy
* @Description : 测试类
* @date : 2023/12/18 23:38
*/
public class IFamilyTest {
public static void main(String[] args) {
IFamily family = new JoyousFamily();
family.eatingRice();
}
}
2、工厂方法模式(Fatory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。在工厂方法模式中用户只需要关心所需产品对应的工厂,无须关心创建细节,而且加入新的产品符合开闭原则
适用场景:
创建对象需要大量重复的代码,客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。一个类通过其子类来指定创建哪个对象。
优点:
用户只需关心所需产品对应的工厂,无须关心创建细节。
加入新产品符合开闭原则,提高了系统的可扩展性
缺点:
类的个数容易过多,增加了代码结构的复杂度。
增加了系统的抽象性和理解难度。
/**
* @author : lssffy
* @Description : 工厂类
* @date : 2023/12/18 23:41
*/
public class FamilyFactory {
public IFamily create(Class<? extends IFamily> clazz) {
// if("joyous".equals(name)){
// return new JoyousFamily();
// }else if("tom".equals(name)){
// return new TomFamily();
// }
// try {
// if (!(null == name || "".equals(name))) {
// return (IFamily) Class.forName(name).newInstance();
// }
// }catch(Exception e){
// e.printStackTrace();
// }
try{
if(null !=clazz){
return clazz.newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
3、抽象工厂模式(Abastract Factory Pattern)是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。属于创建型设计模式
适用场景:
客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。
强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。
提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
优点:
具体产品在应用层代码隔离,无须关心创建细节
将一个系列的产品族统一到一起创建
缺点:
规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
增加了系统的抽象性和理解难度
/**
* @author : lssffy
* @Description : 抽象工厂
* @date : 2023/12/19 17:16
*/
public interface FamilyFactory {
Bridge createBridge();
WatchTV createWatchTV();
}
/**
* @author : lssffy
* @Description : 冰箱
* @date : 2023/12/19 17:14
*/
public interface Bridge {
void open();
}
/**
* @author : lssffy
* @Description : 电视
* @date : 2023/12/19 17:15
*/
public interface WatchTV {
void watch();
}
/**
* @author : lssffy
* @Description : A品牌
* @date : 2023/12/19 17:17
*/
public class JavaBridge implements Bridge{
public void open() {
System.out.println("JavaBridge");
}
}
/**
* @author : lssffy
* @Description : A品牌
* @date : 2023/12/19 17:18
*/
public class JavaWatchTV implements WatchTV{
public void watch() {
System.out.println("Java Watching");
}
}
/**
* @author : lssffy
* @Description : A品牌工厂
* @date : 2023/12/19 17:19
*/
public class JavaFamilyFactory implements FamilyFactory{
public Bridge createBridge() {
return new JavaBridge();
}
public WatchTV createWatchTV() {
return new JavaWatchTV();
}
}
/**
* @author : lssffy
* @Description : 测试类
* @date : 2023/12/22 9:36
*/
public class Test {
public static void main(String[] args) {
FamilyFactory factory = new JavaFamilyFactory();
factory.createWatchTV().watch();
factory.createBridge().open();
}
}
二、装饰器模式 BeanWrapper
三、代理模式 AopProxy
在生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、事务代理、非侵入式日志监听等,这些都是代理模式的实际体现。代理模式(ProxyPattern)的定义也非常简单,是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客服端和目标对象之间起到中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一保护目标对象,二增强目标对象。代理模式属于结构型模式,有静态代理和动态代理。
Java中的代理分为三种角色:
代理类(ProxySubject)
委托类(RealSubject)
接口(Subject)
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。
静态代理:
Java中的静态代理要求代理类(ProxySubject)和委托类(RealSubject)都实现同一个接口(Subject)。静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一单需要修改接口,代理类和委托类都需要修改。
动态代理:
Java中的动态代理依靠反射来实现,代理类和委托类不需要实现同一个接口。委托类需要实现接口,否则无法创建动态代理。代理类在JVM运行时动态生成,而不是编译期就能确定。Java动态代理主要涉及到两个类:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。代理类需要实现InvocationHandler接口或者创建匿名内部类,而Proxy用于创建动态动态。
Proxy用来动态创建一个代理对象的类,它提供了许多的方法,但是用的最多的就是newProxyInstance 这个方法。一般情况下,使用下面的newProxyInstance方法。
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
//loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
//interfaces: 一个Interface对象的数组,表示的是将要给需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样就能调用这组接口中的方法了
//h: 一个InvocationHandler对象,表示的是当这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
InvocationHandler每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。 在调用代理对象中的每一个方法时,在代码内部,都是直接调用了InvocationHandler的invoke方法,而invoke方法根据代理类传递给自己的method参数来区分是什么方法。
//在代理实例上处理方法调用并返回结果
Object invoke(Object proxy,Method method,Object[] args)
//proxy:指代所代理的那个真实对象
//method:指代的是所要调用真实对象的某个方法的Method对象
//args:指代的是调用真实对象某个方法时接受的参数
/**
* @author : lssffy
* @Description : 接口类
* @date : 2023/12/21 14:02
*/
public interface HelloService {
void sayHello();
}
/**
* @author : lssffy
* @Description : 实现
* @date : 2023/12/21 14:03
*/
public class HelloServiceImpl implements HelloService {
public void sayHello() {
System.out.println("Hello");
}
}
/**
* @author : lssffy
* @Description : 代理类
* @date : 2023/12/21 14:05
*/
public class HelloServiceProxy {
private HelloService helloService;
public HelloServiceProxy(HelloService helloService) {
this.helloService = helloService;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before hello service");
Object obj = method.invoke(helloService, args);
System.out.println("after hello service");
return obj;
}
});
}
}
/**
* @author : lssffy
* @Description :
* @date : 2023/12/21 10:21
*/
public class Test {
public static void main(String[] args) {
try{
HelloService helloService = new HelloServiceImpl();
HelloService dynamic = (HelloService) new HelloServiceProxy(helloService).getProxyInstance();
dynamic.sayHello();
}catch (Exception e){
e.printStackTrace();
}
}
}
CGLIB动态代理类的机制,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。” cglib 创建某个类A的动态代理类的模式 - 查找A上的所有非final的public类型的方法定义; - 将这些方法的定义转换成字节码; - 将组成的字节码转换成相应的代理的class对象; - 实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)
/**
* @author : lssffy
* @Description : 代理类
* @date : 2023/12/21 14:42
*/
public class CGlibClass {
public void cglibProxy(){
System.out.println("cglibProxy");
}
}
/**
* @author : lssffy
* @Description : 代理实现类
* @date : 2023/12/21 14:44
*/
public class CGLIBHandler implements MethodInterceptor{
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before execution");
methodProxy.invokeSuper(o,objects);
System.out.println("after execution");
return null;
}
}
/**
* @author : lssffy
* @Description :
* @date : 2023/12/21 10:21
*/
public class Test {
public static void main(String[] args) {
CGLIBHandler cglibHandler = new CGLIBHandler();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CGlibClass.class);
enhancer.setCallback(cglibHandler);
CGlibClass proxy =(CGlibClass) enhancer.create();
proxy.cglibProxy();
}
}
JDK Proxy 生成对象的步骤如下:
1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。
2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口。
3、动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)。
4、编译新生成的 Java 代码.class。
5、再重新加载到 JVM 中运行
CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,CGLib 执行效率更高。
静态代理实现较简单,代理类在编译期生成,效率高。缺点是会生成大量的代理类。
JDK动态代理不要求代理类和委托类实现同一个接口,但是委托类需要实现接口,代理类需要实现InvocationHandler接口。
动态代理要求代理类InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步
新增,违背开闭原则。
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开
闭原则。
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,
无需修改代理类的代码。
优点:
1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强
缺点:
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度
四、单例模式 ApplicationContext
单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛。例如,国家主席、公司 CEO、部门经理等。在 J2EE 标准中,ServletContext、ServletContextConfig 等;在 Spring 框架应用中 ApplicationContext;数据库的连接池也都是单例形式。
饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题。
优点:
没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。
缺点:
类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能占着茅坑不拉屎
/**
* @author : lssffy
* @Description : 饿汉式单例类
* @date : 2023/12/22 9:55
*/
public class HungrySingleton {
private HungrySingleton(){}
// private static final HungrySingleton httpSingleton = new HungrySingleton();
//静态代码块的方式
private static final HungrySingleton httpSingleton;
static {
httpSingleton = new HungrySingleton();
}
public static HungrySingleton getInstance(){
return httpSingleton;
}
}
懒汉式单例是被外部类调用的时候内部类才会加载
/**
* @author : lssffy
* @Description : 懒汉式单例类
* @date : 2023/12/22 10:06
*/
public class LazySimpleSingleton {
private LazySimpleSingleton(){}
private static LazySimpleSingleton lazy = null;
public static LazySimpleSingleton getInstance(){
if(lazy == null){
synchronized (LazySimpleSingleton.class){
if(lazy == null) {
lazy = new LazySimpleSingleton();
}
}
}
return lazy;
}
}
/**
* @author : lssffy
* @Description : 静态内部类单例
* @date : 2023/12/22 10:11
*/
//这种形式兼顾饿汉式的内存浪费,也兼顾 synchronized 性能问题
//完美地屏蔽了这两个缺
public class LazyInnerClassSingleton {
//默认使用 LazyInnerClassGeneral 的时候,会先初始化内部类
//如果没使用的话,内部类是不加载的
private LazyInnerClassSingleton() {
//针对反射破坏单例,增加判断条件用于控制
if(LazyHandler.lazy!=null){
throw new RuntimeException("不允许创建多个实例");
}
}
//每一个关键字都不是多余的
//static 是为了使单例的空间共享
//保证这个方法不会被重写,重载
public static final LazyInnerClassSingleton getInstance(){
return LazyHandler.lazy;
}
//默认不加载
public static class LazyHandler{
private static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton();
}
}
序列化破坏单例
/**
* @author : lssffy
* @Description : 序列化破坏单例
* @date : 2023/12/22 10:19
*/
public class SerializableSingleton implements Serializable {
//序列化就是说把内存中的状态通过转换成字节码的形式
//从而转换一个 IO 流,写入到其他地方(可以是磁盘、网络 IO)
//内存中状态给永久保存下来了
//反序列化
//讲已经持久化的字节码内容,转换为 IO 流
//通过 IO 流的读取,进而将读取的内容转换为 Java 对象
//在转换过程中会重新创建对象 new
private static final SerializableSingleton INSTANCE = new SerializableSingleton();
private SerializableSingleton(){}
public static SerializableSingleton getInstance(){
return INSTANCE;
}
//加上这个方法可以防止序列化破坏单例
private Object readResolve(){
return INSTANCE;
}
}
/**
* @author : lssffy
* @Description :测试类
* @date : 2023/12/22 10:22
*/
public class SerializableSingletonTest {
public static void main(String[] args) {
SerializableSingleton s1 = null;
SerializableSingleton s2 = SerializableSingleton.getInstance();
FileOutputStream fos = null;
try{
fos = new FileOutputStream("SerializableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SerializableSingleton) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
}catch(Exception e){
e.printStackTrace();
}
}
}
注册式单例为登记式单例,就是将每一个实例都登记到某一个地方,使用唯一的标识获取实例。注册式单例有两种写法:一种为容器缓存,一种为枚举登记。
/**
* @author : lssffy
* @Description : 注册式单例
* @date : 2023/12/22 10:37
*/
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
/**
* @author : lssffy
* @Description : 测试类
* @date : 2023/12/22 10:39
*/
public class EnumSingletonTest {
public static void main(String[] args) {
try {
EnumSingleton enumSingleton1 = null;
EnumSingleton enumSingleton2 = EnumSingleton.getInstance();
enumSingleton2.setData(new Object());
FileOutputStream fos = new FileOutputStream("EnumSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(enumSingleton2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("EnumSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
enumSingleton1 = (EnumSingleton) ois.readObject();
ois.close();
System.out.println(enumSingleton1.getData());
System.out.println(enumSingleton2.getData());
System.out.println(enumSingleton1.getData()==enumSingleton2.getData());
}catch(Exception e){
e.printStackTrace();
}
}
}
容器式单例
/**
* @author : lssffy
* @Description : 容器式单例
* @date : 2023/12/22 10:46
*/
public class ContainerSingleton {
private ContainerSingleton(){}
private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
public static Object getBean(String className){
synchronized (ioc){
if(!ioc.containsKey(className)){
Object obj = null;
try{
obj = Class.forName(className).newInstance();
ioc.put(className, obj);
}catch(Exception e){
e.printStackTrace();
}
return obj;
}else{
return ioc.get(className);
}
}
}
}
/**
* @author : lssffy
* @Description : 测试类
* @date : 2023/12/22 10:50
*/
public class ContainerSingletonTest {
public static void main(String[] args) {
Object obj1 = ContainerSingleton.getBean("com.gupaoedu.vip.pattern.singleton.ContainerSingleton") ;
Object obj2 = ContainerSingleton.getBean("com.gupaoedu.vip.pattern.singleton.ContainerSingleton") ;
System.out.println(obj1);
System.out.println(obj2);
System.out.println(obj1==obj2);
}
}
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
使用场景:
类初始化消耗资源较多
new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
构造函数比较复杂
循环体中生产大量对象时
浅拷贝:如果原型对象的成员变量是值类型,将复制一份给克隆对象,也就是说在堆中拥有独立的空间;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。换句话说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
/**
* @author : lssffy
* @Description :
* @date : 2023/12/20 21:53
*/
public class Car {
public int id;
public String name;
}
/**
* @author : lssffy
* @Description :
* @date : 2023/12/20 21:53
*/
public class Bus extends Car implements Cloneable, Serializable {
public Oil oil;
public Bus(){
this.id = 11;
this.name = "xiaoqiche";
this.oil = new Oil();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* @author : lssffy
* @Description :
* @date : 2023/12/20 21:54
*/
public class Oil implements Serializable {
public int right;
public int left;
public void big(){
this.left = 2;
this.right = 2;
}
public void small(){
this.left /= 2;
this.right /= 2;
}
}
深拷贝:一种完全拷贝,无论是值类型还是引用类型都会完完全全的拷贝一份,在内存中生成一个新的对象,深拷贝有两种方式,一种是跟浅拷贝一样实现 Cloneable 接口,另一种是实现 Serializable 接口,用序列化的方式来实现深拷贝
/**
* @author : lssffy
* @Description :
* @date : 2023/12/20 23:11
*/
@Data
public class Monkey implements Serializable {
private int height;
private int width;
}
/**
* @author : lssffy
* @Description :
* @date : 2023/12/20 23:12
*/
@Data
public class Qtds implements Serializable {
private int height;
private int width;
private Monkey monkey;
public Object deepClone() throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Qtds qtds = (Qtds) ois.readObject();
return qtds;
}
}
五、委派模式 DispatcherServlet
委派模式(Delegate Pattern)的基本作用是负责任务的调用和分配任务,跟代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是代理模式注重过程,而委派模式注重结果。委派模式在 Spring 中应用非常多,大家常用的 DispatcherServlet 其实就是用到了委派模式。现实生活中也常有委派的场景发生,
/**
* @author : lssffy
* @Description : 员工接口
* @date : 2023/12/22 11:07
*/
public interface IEmployee {
public void doing(String command);
}
/**
* @author : lssffy
* @Description : 员工A
* @date : 2023/12/22 11:09
*/
public class EmployeeA implements IEmployee{
public void doing(String command) {
System.out.println("I am employeeA,I can ready working with the" + command);
}
}
/**
* @author : lssffy
* @Description : 员工B
* @date : 2023/12/22 11:10
*/
public class EmployeeB implements IEmployee{
public void doing(String command) {
System.out.println("I am employeeB,I can ready working with the" + command);
}
}
/**
* @author : lssffy
* @Description : 项目经理
* @date : 2023/12/22 11:26
*/
public class Leader implements IEmployee{
private Map<String,IEmployee> target = new HashMap<String,IEmployee>();
public Leader(){
target.put("加密",new EmployeeA());
target.put("登录",new EmployeeB());
}
public void doing(String command) {
target.get(command).doing(command);
}
}
/**
* @author : lssffy
* @Description : boss
* @date : 2023/12/22 11:29
*/
public class Boss {
public void common(String common,Leader leader){
leader.doing(common);
}
}
/**
* @author : lssffy
* @Description :
* @date : 2023/12/22 11:30
*/
public class DelegateTest {
public static void main(String[] args) {
//客户请求(Boss)、委派者(Leader)、被被委派者(Target)
//委派者要持有被委派者的引用
//代理模式注重的是过程, 委派模式注重的是结果
//策略模式注重是可扩展(外部扩展),委派模式注重内部的灵活和复用
//委派的核心:就是分发、调度、派遣
//委派模式:就是静态代理和策略模式一种特殊的组合
new Boss().common("登录",new Leader());
new Boss().common("加密",new Leader());
}
}
SpringMVC的DispatcherServlet实现委派模式
/**
* @author : lssffy
* @Description : 业务类
* @date : 2023/12/22 13:50
*/
public class MemberController {
public void getMemberById(String mid){
}
}
/**
* @author : lssffy
* @Description : 业务类
* @date : 2023/12/22 13:51
*/
public class OrderController {
public void getOrderById(String mid){
}
}
/**
* @author : lssffy
* @Description : 业务类
* @date : 2023/12/22 13:52
*/
public class SystemController {
public void logout(){
}
}
/**
* @author : lssffy
* @Description : 委派类
* @date : 2023/12/22 13:52
*/
public class DispatcherServlet extends HttpServlet {
private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception{
String url = request.getRequestURI();
String mid = request.getParameter("mid");
if("getMemberById".equals(url)){
new MemberController().getMemberById(mid);
}else if(("getOrderById").equals(url)){
new OrderController().getOrderById(mid);
}else if("logout".equals(url)){
new SystemController().logout();
}else{
response.getWriter().write("404 Not Found");
}
}
protected void service(HttpServletRequest request, HttpServletResponse response) {
try{
doDispatch(request, response);
}catch(Exception e){
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Web Application</display-name>
<servlet>
<servlet-name>delegateServlet</servlet-name>
<servlet-class>com.gupaoedu.vip.pattern.delegate.mvc.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>delegateServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
六、策略模式 HandlerMapping
策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。
策略模式的应用场景
1、假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
2、一个系统需要动态地在几种算法中选择一种
实现案例
/**
* @author : lssffy
* @Description : 抽象类
* @date : 2023/12/22 15:44
*/
public interface PromotionStrategy {
void doPromotion();
}
/**
* @author : lssffy
* @Description : 策略类
* @date : 2023/12/22 15:44
*/
public class CouponStrategy implements PromotionStrategy{
public void doPromotion() {
System.out.println("优惠券,直接抵扣");
}
}
/**
* @author : lssffy
* @Description : 策略类
* @date : 2023/12/22 15:46
*/
public class CashbackStrategy implements PromotionStrategy{
public void doPromotion() {
System.out.println("返现促销");
}
}
/**
* @author : lssffy
* @Description : 策略类
* @date : 2023/12/22 15:47
*/
public class GroupBuyStrategy implements PromotionStrategy{
public void doPromotion() {
System.out.println("拼团,享受团价");
}
}
/**
* @author : lssffy
* @Description : 策略类
* @date : 2023/12/22 15:48
*/
public class EmptyStrategy implements PromotionStrategy{
public void doPromotion() {
System.out.println("无优惠");
}
}
/**
* @author : lssffy
* @Description : 活动
* @date : 2023/12/22 15:49
*/
public class PromotionActivity {
private PromotionStrategy promotionStrategy;
public PromotionActivity(PromotionStrategy promotionStrategy){
this.promotionStrategy = promotionStrategy;
}
public void execute(){
promotionStrategy.doPromotion();
}
}
/**
* @author : lssffy
* @Description : 测试类
* @date : 2023/12/22 15:50
*/
public class PromotionTest {
public static void main(String[] args) {
PromotionActivity p1 = new PromotionActivity(new CouponStrategy());
PromotionActivity p2 = new PromotionActivity(new CashbackStrategy());
p1.execute();
p2.execute();
}
}
如果把上面这段测试代码放到实际的业务场景其实并不实用。
因为我们做活动时候往往是要根据不同的需求对促销策略进行动态选择的,并不会一次
性执行多种优惠。所以,我们的代码通常会这样写
/**
* @author : lssffy
* @Description : 测试类
* @date : 2023/12/22 15:50
*/
public class PromotionTest {
public static void main(String[] args) {
PromotionActivity promotionActivity = null;
String promotion = "COUPON";
if(promotion.equals("COUPON")){
promotionActivity = new PromotionActivity(new CouponStrategy());
}else if(promotion.equals("CASHBACK")){
promotionActivity = new PromotionActivity(new CashbackStrategy());
}//......
promotionActivity.execute();
}
}
这样改造之后,满足了业务需求,客户可根据自己的需求选择不同的优惠策略了。但是,经过一段时间的业务积累,我们的促销活动会越来越多,我们是不需要思考代码是不是应该重构了,我们可以结合单例模式和工厂模式
/**
* @author : lssffy
* @Description : 工厂类
* @date : 2023/12/22 15:59
*/
public class PromotionStrategyFactory {
private static final Map<String,PromotionStrategy> PROMOTION_STRATEGY_MAP = new ConcurrentHashMap<String, PromotionStrategy>();
static {
PROMOTION_STRATEGY_MAP.put(PromotionKey.COUPON,new CouponStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.CASHBACK,new CashbackStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.GROUPBUY,new GroupBuyStrategy());
}
private static final PromotionStrategy DEFAULT_ACTIVITY = new EmptyStrategy();
private PromotionStrategyFactory(){}
public static PromotionStrategy getPromotionStrategy(String promotionKey){
PromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
return promotionStrategy == null?DEFAULT_ACTIVITY:promotionStrategy;
}
private interface PromotionKey{
String COUPON = "COUPON";
String CASHBACK = "CASHBACK";
String GROUPBUY = "GROUPBUY";
}
}
/**
* @author : lssffy
* @Description : 测试类
* @date : 2023/12/22 15:50
*/
public class PromotionTest {
public static void main(String[] args) {
String promotionKey = "COUPON";
PromotionActivity promotionActivity = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey));
promotionActivity.execute();
}
}
支付类案例
/**
* @author : lssffy
* @Description : 抽象类
* @date : 2023/12/22 16:29
*/
public abstract class Payment {
//支付类型
public abstract String getName();
//查询余额
protected abstract double queryBalance(String uid);
//扣款支付
public PayState pay(String uid,double amount){
if(queryBalance(uid) < amount){
return new PayState(500,"支付失败","余额不足");
}
return new PayState(200,"支付成功","支付金额:" + amount);
}
}
/**
* @author : lssffy
* @Description : 支付宝
* @date : 2023/12/22 16:51
*/
public class AliPay extends Payment{
public String getName() {
return "支付宝";
}
protected double queryBalance(String uid) {
return 900;
}
}
/**
* @author : lssffy
* @Description : 京东
* @date : 2023/12/22 16:52
*/
public class JDPay extends Payment{
public String getName() {
return "京东白条";
}
protected double queryBalance(String uid) {
return 500;
}
}
/**
* @author : lssffy
* @Description : 微信支付
* @date : 2023/12/22 16:53
*/
public class WechatPay extends Payment{
public String getName() {
return "微信自己付";
}
protected double queryBalance(String uid) {
return 256;
}
}
/**
* @author : lssffy
* @Description : 银联支付
* @date : 2023/12/22 16:53
*/
public class UnionPay extends Payment{
public String getName() {
return "银联支付";
}
protected double queryBalance(String uid) {
return 120;
}
}
/**
* @author : lssffy
* @Description : 支付策略类
* @date : 2023/12/22 16:54
*/
public class PayStrategy {
public static final String ALI_PAY = "AliPay";
public static final String JD_PAY = "JDPay";
public static final String WECHAT_PAY = "WechatPay";
public static final String UNION_PAY = "UnionPay";
public static final String DEFAULT_PAY = ALI_PAY;
private static final Map<String,Payment> payStrategy = new ConcurrentHashMap<String,Payment>();
static {
payStrategy.put(ALI_PAY,new AliPay());
payStrategy.put(JD_PAY,new JDPay());
payStrategy.put(WECHAT_PAY,new WechatPay());
payStrategy.put(UNION_PAY,new UnionPay());
}
public static Payment get(String payKey){
if(!payStrategy.containsKey(payKey)){
return payStrategy.get(DEFAULT_PAY);
}
return payStrategy.get(payKey);
}
}
/**
* @author : lssffy
* @Description : 支付状态类
* @date : 2023/12/22 16:30
*/
public class PayState {
private int code;
private Object data;
private String msg;
PayState(int code, Object data, String msg){
this.code = code;
this.data = data;
this.msg = msg;
}
@Override
public String toString() {
return ("支付状态:[" + code+ "];" + msg + ", 交易详情:" + data);
}
}
/**
* @author : lssffy
* @Description : 订单类
* @date : 2023/12/22 17:00
*/
public class Order {
private String uid;
private String orderId;
private double price;
Order(String uid, String orderId, double price){
this.uid = uid;
this.orderId = orderId;
this.price = price;
}
public PayState pay(){
return pay(PayStrategy.DEFAULT_PAY);
}
public PayState pay(String payKey){
Payment payment = PayStrategy.get(payKey);
System.out.println("欢迎使用" + payment.getName());
System.out.println("本次交易金额为:" + price + ",开始扣款....");
return payment.pay(uid,price);
}
}
/**
* @author : lssffy
* @Description : 测试类
* @date : 2023/12/22 17:04
*/
public class PayTest {
public static void main(String[] args) {
Order order = new Order("1","2011111111",500);
System.out.println(order.pay(PayStrategy.ALI_PAY));
}
}
优点:
1、策略模式符合开闭原则。
2、避免使用多重条件转移语句,如 if…else…语句、switch 语句
3、使用策略模式可以提高算法的保密性和安全性
缺点:
1、客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
2、代码中会产生非常多策略类,增加维护难度
七、适配器模式 HandlerApdapter
八、模板方法模式 JdbcTemplate
九、观察者模式 ContextLoaderListener