标签:关键字 回调 volatile http 对象 机制 size change iat
要分析常用框架spring、mybatis、springboot、springcloud等的源码,首先要了解各种设计模式,因为框架里面应用了各种设计模式
学习设计模式最重要的是掌握设计思想和设计原则,理解了设计思想和设计原则并运用到平时的编码中是最重要的!!!
天天加班编程,编程到底都做的是什么?
撸代码,加班撸代码,写接口、写类、写方法
用设计模式或做设计的作用是什么?
指导、规定该如何撸代码,如何来写接口、写类、写方法
为什么要做设计、用设计模式?
代码会变,为应对变化,为了以后方便扩展。做到以不变应万变,做一个会偷懒的程序员!
首先是从现实出发理清现实,在写代码之前先从实际分析,然后就开始写代码,写代码时要区分出不变的代码和会变化的代码,会变得代码会怎么变,使用者如何隔绝这种变化,所谓的隔绝这种变化就是不让调用者感知到内部的变化,只需要很简单的方式就能使用不必关心内部的逻辑,这样的话就要用到各种设计模式。不同的变化方式对应不同的设计模式。
设计的最终体现:如何来定义类、接口、方法
1)类是做什么用的?
模拟现实,封装数据与代码
2)接口是做什么用的?
定义相接的口子
定义功能使用者和功能提供者间的接口
3)为什么要有接口?
隔离变化
4)抽象类是做什么用的?
包容不变与变的
1)多态为我们提供了什么?
一种实现变化的方式
1. 找出变化,分开变化和不变的
隔离,封装变化的部分,让其他部分不受它的影响。
2. 面向接口编程 ——隔离变化的方式
使用者使用接口,提供者实现接口。“接口”可以是超类!
3. 依赖倒置原则(里氏替换原则)——隔离变化的方式
依赖抽象,不要依赖具体类!
4. 对修改闭合,对扩展开放——隔离变化的方式
可以继承一个类或者接口扩展功能,但是不能修改类或者接口的原有功能
5. 多用组合,少用继承——灵活变化的方式
“有一个”可能比“是一个”更好。
6. 单一职责原则——方法设计的原则
每个方法只负责一个功能,不要把很多功能写在一个方法里面
应用设计模式的目的:
易扩展,易维护
少改代码,不改代码
示例:
京东、天猫双十一促销,各种商品有多种不同的促销活动:
满减:满400减50
每满减:每满100减20
数量折扣:买两件8折、三件7折
数量减:满三件减最低价的一件
……
顾客下单时可选择多种促销活动的其中一种来下单
后端代码中如何来灵活应对订单金额的计算?以后还会有很多的促销活动出现!
控制器OrderController.java
@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; /** * 计算订单的促销金额 */ @RequestMapping("prepare") public Order prepareOrder(Order order, String promotion) { …….. return this.orderService.prepareOrder(order, promotion); } }
OrderService.java改怎么来写呢
@Service public class OrderService { public Order prepareOrder(Order order, String promotion) { // 该如何写 return order; } }
@Service public class OrderService { public Order prepareOrder(Order order, String promotion) { switch (promotion) { case "promotion-1": // 促销1的算法 …… break; case "promotion-2": // 促销2的算法 …… break; case "promotion-3": // 促销3的算法 …… break; …… } return order; } }
营销活动有很多,这个switch会变得很庞大,不利于维护,并且很容易引入新的问题
@Service public class OrderService { public Order prepareOrder(Order order, String promotion) { switch (promotion) { case "promotion-1": // 促销1的算法 return calPromotion1(order); case "promotion-2": // 促销2的算法 return calPromotion2(order); case "promotion-3": // 促销3的算法 return calPromotion3(order); …… } return order; } private Order calPromotion1(Order order) { System.out.println("促销1计算.............................."); return order; } ……. }
把每个促销算法单独抽出一个方法,新加入一个促销活动只需要新增一个方法和case就可以了
这里利用了设计原则的方法设计原则:单一职责原则
但是这样写还会存在如下问题:
营销活动经常变,这个switch就得经常改,还得不断加促销的算法方法…….
改代码是bug的源泉,我们希望少改动OrderService!!!
分析:这里变的是什么?
促销的金额的算法!同一行为的不同算法!
我们不希望OrderService被算法代码爆炸!
同一行为的不同算法实现,我们可以用接口来定义行为,不同的算法分别去实现接口。
这里利用了设计原则:对修改关闭,对扩展开放!
这就是策略模式的应用!
策略模式的的定义:
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的用户而独立变化。
@Service public class OrderService { public Order prepareOrder(Order order, String promotion) { switch (promotion) { case "promotion-1": // 促销1的算法 return new Promotion1Calculation().calculate(order); case "promotion-2": // 促销2的算法 return new Promotion2Calculation().calculate(order); case "promotion-3": // 促销3的算法 return new Promotion3Calculation().calculate(order); ...... } } }
但是switch中的代码还是会不断变!!!switch中需要知道所有的实现!
如何让OrderService的代码不要改变?
把变的部分移出去!改怎么移呢?
@Component public class PromotionCalculationFactory { public PromotionCalculation getPromotionCalculation(String promotion) { switch (promotion) { case "promotion-1": // 促销1的算法 return new Promotion1Calculation(); case "promotion-2": // 促销2的算法 return new Promotion2Calculation(); case "promotion-3": // 促销3的算法 return new Promotion3Calculation(); ...... } } }
这是简单工厂模式:所有产品由一个工厂创建
@Service public class OrderService { @Autowired private PromotionCalculationFactory promotionCalculationFactory; public Order prepareOrder(Order order, String promotion) { return promotionCalculationFactory.getPromotionCalculation(promotion).calculate(order); } }
想要工厂中的代码也不要随促销的变化而变化,你觉得该怎么办?
方式一:promotion = beanName
把各种促销算法的实现交给spring容器来管理,用户选择的促销活动promotion 作为bean的名字,在PromotionCalculationFactory 工厂里面通过getBean("promotion")就能拿到各种促销算法的实现了
方式一的伪代码实现:
spring里面的bean配置:
<bean id="promotion1" calss="Promotion1Calculation"> <bean id="promotion2" calss="Promotion2Calculation"> <bean id="promotion3" calss="Promotion3Calculation">
PromotionCalculationFactory 工厂改写:
@Component public class PromotionCalculationFactory { public PromotionCalculation getPromotionCalculation(String promotion) { return getBean("promotion1/promotion2/promotion3"); } } }
方式二: 配置promotion与实现类的对应关系
把用户选择的促销活动promotion和对应的促销算法的实现类放到map里面,或者存到数据库里面,在PromotionCalculationFactory 工厂里面通过map.get("promotion"),或者从数据库里面获取对应促销算法的实现类路径通过Class.forName("促销算法的实现类路径")就能拿到各种促销算法的实现了
方式二的伪代码实现:
PromotionCalculationFactory 工厂改写:
package com.study.design.mode.service; import java.util.Map; import org.springframework.stereotype.Component; @Component public class PromotionCalculationFactory { private Map<String, PromotionCalculation> maps; public PromotionCalculation getPromotionCalculation(String promotion) { PromotionCalculation prom = maps.get(promotion); if (prom == null) { // 从配置的地方加载 prom = getFromDb(promotion); if (prom != null) maps.put(promotion, prom); } return prom; } public void init() { // 第一次将所有的促销策略都加载到Map中 } private PromotionCalculation getFromDb(String promotion) { // 从数据库中取到对应的类名 //配置的格式: promotion1=com.study.dn.promotion.calculation.Promotion1 String className = 从数据库(或其他配置源)中获得; // Class c = Class.forName(className); // 实例化 // 返回 } }
一个工厂负责创建所有实例。比如上面的策略模式中使用的就是简单工厂模式
根据传入的工厂类型参数String创建对应的实例(产品)
父类中定义工厂方法,各子类在+factoryMethod():Product方法里面实现具体的实例创建
使用者持有具体的工厂ChildAClass、ChildBClass、ChildCClass,传入对应的工厂ChildAClass、ChildBClass、ChildCClass创建对应的工厂实例
定义一个工厂接口,所有具体工厂实现工厂接口
使用者调用FactoryProducer的getFactory(type)方法传入type,type为AFactory、BFactory、CFactory对应的类型,就会返回对应的工厂AFactory、BFactory、CFactory,不需要传入AFactory、BFactory、CFactory,因为type已经跟AFactory、BFactory、CFactory绑定了。
示例:促销活动可多重叠加,该如何灵活实现订单金额计算?
OrderController
@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; /** * 计算订单的促销金额,促销按给入的顺从叠加 */ @RequestMapping("prepare") public Order prepareOrder(Order order, String... promotion) { return this.orderService.prepareOrder(order, promotion); } }
OrderService
@Service public class OrderService { @Autowired private PromotionCalculationFactory promotionCalculationFactory; public Order prepareOrder(Order order, String... promotion) { for (String p : promotion) { order = promotionCalculationFactory. getPromotionCalculation(p).calculate(order); } return order; } }
装饰者模式的定义:以装饰的方式,动态地将责任附加到对象上。
说明:
不改变具体类代码(被装饰者ConcreteComponent),动态叠加增强行为功能。
若要扩展功能,装饰者提供了比继承更有弹性的替代方案
相较于前面的for循环,有何区别?
当需要对一个类的多个方法进行增强,使用者会随意使用被增强方法时,for循环就不够灵活了。
责任链和装饰者模式完成的是相同的事情。
装饰者模式-代码示例:
共同的需装饰的行为定义成接口
public interface Component { String methodA(); int methodB(); }
被装饰者实现接口Component
public class ConcreteComponent implements Component { public String methodA() { return "concrete-object"; } public int methodB() { return 100; } }
装饰者实现接口Component
public class Decorator implements Component { //装饰者包含被装饰者(被装饰者实现的接口) protected Component component; public Decorator(Component component) { super(); this.component = component; } public String methodA() { return this.component.methodA(); } public int methodB() { return this.component.methodB(); } }
装饰者派生出的装饰者
public class DecoratorA extends Decorator { public DecoratorA(Component component) { super(component); } public String methodA() { //在这里可以进行前置增强,实现要处理的逻辑 return this.component.methodA() + " + A"; //在这里可以进行后置增强,实现要处理的逻辑 } public int methodB() { //在这里可以进行前置增强,实现要处理的逻辑 return this.component.methodB() + 10; //在这里可以进行后置增强,实现要处理的逻辑 } }
调用示例:
public class DecoratorSample { public static void main(String[] args) { //创建一个被装饰者 Component cc = new ConcreteComponent(); //创建一个派生的装饰者,同时把被装饰者传入装饰者里面,即说的装饰者包含被装饰者 cc = new DecoratorA(cc); //方法调用 System.out.println(cc.methodA()); System.out.println(cc.methodB()); } }
输出结果:
concrete-object + A
110
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
作用:不改变原类的代码,而增强原类对象的功能,可选择前置、后置、环绕、异常处理增强
代理模式的类图:
类图与装饰者模式一样,那么代理模式和装饰者模式有什么区别呢?
代理模式意在在代理中控制使用者对目标对象的访问,以及进行功能增强。装饰者模式意在对功能的叠加,比如对多种促销活动的叠加
代理模式有两种实现方式:
静态代理:由程序员创建或由特定工具自动生成代理类源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:代理类在程序运行时,运用反射机制动态创建而成。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
有一个土豪要找苍老师约会,他不能直接和苍老师约,需要经过一个中间代理Tony
需要被代理控制增强的行为定义成接口或者超类
public interface Girl { boolean dating(float length); }
代理和被代理的目标对象都要实现接口Girl
代理:
package com.study.design.mode.samples.proxy; /** * * @Description: 代理类实现Girl * @author leeSamll * @date 2018年11月24日 * */ public class Tony implements Girl { //代理类持有被代理的目标对象TeacherCang(目标对象实现的超类或者接口) private Girl girl; public Girl getGirl() { return girl; } public void setGirl(Girl girl) { this.girl = girl; } //代理:控制、增强被代理对象的行为 public boolean dating(float length) { // 前置增强 doSomethingBefore(); boolean res = this.girl.dating(length); // 后置增强 doSomethingAfter(); return res; } private void doSomethingBefore() { System.out.println("老板,这个我试过了,很不错,推荐给你!"); } private void doSomethingAfter() { System.out.println("老板,你觉得怎样,欢迎下次再约!"); } }
被代理的目标对象
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理的目标对象实现Girl * @author leeSamll * @date 2018年11月24日 * */ public class TeacherCang implements Girl { public boolean dating(float length) { if (length >= 1.7F) { System.out.println("身高可以,可以约!"); return true; } System.out.println("身高不可以,不可约!"); return false; } }
土豪使用者
package com.study.design.mode.samples.proxy; /** * * @Description: 使用者 * @author leeSamll * @date 2018年11月24日 * */ public class TuHao { private float length; public TuHao(float length) { super(); this.length = length; } public float getLength() { return length; } public void setLength(float length) { this.length = length; } //约会 public void dating(Girl g) { g.dating(length); } }
调用示例:
package com.study.design.mode.samples.proxy; /** * * @Description: 调用示例 * @author leeSamll * @date 2018年11月24日 * */ public class PlayGame { public static void main(String[] args) { //创建土豪(使用者)、苍老师(目标对象)、tony(代理)三个对象 TuHao th = new TuHao(1.7F); Girl tc = new TeacherCang(); Tony tony = new Tony(); //tony对苍老师进行代理 tony.setGirl(tc); //土豪和tony约 th.dating(tony); }
输出结果:
老板,这个我试过了,很不错,推荐给你!
身高可以,可以约!
老板,你觉得怎样,欢迎下次再约!
静态代理缺点:
扩展能力差
横向扩展:代理更多的类
纵向扩展:增强更多的方法
可维护性差
由于静态代理的扩展能力差、可维护性差,这就需要使用动态代理了!!!
在运行时,动态为不同类的对象创建代理,增强功能。灵活扩展,易维护!
动态代理的实现方式:
JDK动态代理:只可对接口创建代理
CGLIB动态代理:可对接口、类创建代理
在运行时,对接口创建代理对象
生成代理类$Proxy0的方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:需要被代理的目标对象实现的接口,可以传入多个
InvocationHandler h:功能增强的接口
功能增强的接口:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
参数说明:
Object proxy:被代理的目标对象(接口)
Method method:要调用的目标对象的方法
Object[] args:要调用的目标对象的方法的参数
被代理控制增强的行为生成接口:
Girl
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理控制增强的行为生成接口Girl * @author leeSamll * @date 2018年11月24日 * */ public interface Girl { boolean dating(float length); }
Boy
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理控制增强的行为生成接口Boy * @author leeSamll * @date 2018年11月24日 * */ public interface Boy { boolean dating(char cup); void show(); }
被代理的目标对象
TeacherCang
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理的目标对象TeacherCang实现Girl * @author leeSamll * @date 2018年11月24日 * */ public class TeacherCang implements Girl { public boolean dating(float length) { if (length >= 1.7F) { System.out.println("身高可以,可以约!"); return true; } System.out.println("身高不可以,不可约!"); return false; } }
TeacherChen
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理的目标对象TeacherChen实现Boy * @author leeSamll * @date 2018年11月24日 * */ public class TeacherChen implements Boy { public boolean dating(char cup) { if (cup == ‘E‘) { System.out.println("这个女老板品德正好,可以约!"); return true; } System.out.println("这个女老板品德不行,不可以约!"); return false; } public void show() { System.out.println("开始进入拍摄模式。。。。。。。。"); } }
JDK动态代理
package com.study.design.mode.samples.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * * @Description: JDK动态代理 * @author leeSamll * @date 2018年11月24日 * */ public class TonyCompany { //动态生成代理对象 传入target的是被代理的类 public static Object proxy(Object target) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new MyInvationHandler(target)); } //特定的功能增强实现 private static class MyInvationHandler implements InvocationHandler { //被被代理的目标对象 private Object target; public MyInvationHandler(Object target) { super(); this.target = target; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前置增强 doSomethingBefore(); // 调用被代理对象的方法 Object res = method.invoke(target, args); // 后置增强 doSomethingAfter(); return res; } private void doSomethingAfter() { System.out.println("老板,你觉得怎样,欢迎下次再约!"); } private void doSomethingBefore() { System.out.println("老板,这个我试过了,很不错,推荐给你!"); } } }
调用示例:
package com.study.design.mode.samples.proxy; /** * * @Description: 调用示例 * @author leeSamll * @date 2018年11月24日 * */ public class PlayGame { public static void main(String[] args) { System.out.println("----------------1.静态代理TeacherCang-----------------------"); //创建土豪(使用者)、苍老师(目标对象)、tony(代理)三个对象 TuHao th = new TuHao(1.7F); Girl tc = new TeacherCang(); Tony tony = new Tony(); //tony对苍老师进行代理 tony.setGirl(tc); //土豪和tony约 th.dating(tony); System.out.println("----------------2.JDK动态代理TeacherCang-----------------------"); //生成代理类$Proxy0 Girl tony1 = (Girl) TonyCompany.proxy(tc); //土豪直接和代理tony约 th.dating(tony1); System.out.println("----------------3.JDK动态代理TeacherChen,横向纵向扩展:代理更多的类和方法-----------------------"); //代理另外一个目标对象TeacherChen Boy tcc = new TeacherChen(); //生成代理类$Proxy0 Boy tony2 = (Boy) TonyCompany.proxy(tcc); //tony2约TeacherChen 纵向扩展:增强更多的方法 System.out.println("----------------3.1 JDK动态代理TeacherChen,调用TeacherChen的dating方法-----------------------"); tony2.dating(‘E‘); System.out.println("----------------3.2 JDK动态代理TeacherChen,调用TeacherChen的show方法-----------------------"); tony2.show(); } }
输出结果:
----------------1.静态代理TeacherCang----------------------- 老板,这个我试过了,很不错,推荐给你! 身高可以,可以约! 老板,你觉得怎样,欢迎下次再约! ----------------2.JDK动态代理TeacherCang----------------------- 老板,这个我试过了,很不错,推荐给你! 身高可以,可以约! 老板,你觉得怎样,欢迎下次再约! ----------------3.JDK动态代理TeacherChen,横向纵向扩展:代理更多的类和方法----------------------- ----------------3.1 JDK动态代理TeacherChen,调用TeacherChen的dating方法----------------------- 老板,这个我试过了,很不错,推荐给你! 这个女老板品德正好,可以约! 老板,你觉得怎样,欢迎下次再约! ----------------3.2 JDK动态代理TeacherChen,调用TeacherChen的show方法----------------------- 老板,这个我试过了,很不错,推荐给你! 开始进入拍摄模式。。。。。。。。 老板,你觉得怎样,欢迎下次再约!
cglib是什么?
cglib( Byte Code Generation Library),一个高层次的java字节码生成和转换的api库.
ASM:一个低层次的字节码操作库
它的主要用途
在运行期为类、接口生成动态代理对象。 以达到不改动原类代码而实现功能增强的目的
常在哪里用它?
常在 AOP、test、orm框架中用来生成动态代理对象、拦截属性访问
如何使用它?
1)引入它的jar
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.6</version> </dependency>
2)学习它的API
https://github.com/cglib/cglib/wiki
cglib动态代理-类图和API
说明:
实现思想和前面的JDK动态代理一样,只是使用了不同的API。
代理类由Enhancer生成,代理类实现被代理的类或者接口,特定的功能增强的实现MyMethodInterceptor实现MethodInterceptor接口,特定的功能增强实现MyMethodInterceptor里面持有被代理的类或者接口target
eg2:cglib动态代理-代码示例
被代理对象的接口:
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理控制增强的行为生成接口Girl * @author leeSamll * @date 2018年11月24日 * */ public interface Girl { boolean dating(float length); }
被代理对象:
package com.study.design.mode.samples.proxy; /** * * @Description: 被代理的目标对象TeacherCang实现Girl * @author leeSamll * @date 2018年11月24日 * */ public class TeacherCang implements Girl { public boolean dating(float length) { if (length >= 1.7F) { System.out.println("身高可以,可以约!"); return true; } System.out.println("身高不可以,不可约!"); return false; } }
cglib动态代理主类:
package com.study.design.mode.samples.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * * @Description: cglib动态代理 * @author leeSamll * @date 2018年11月24日 * */ public class CglibDemo { // 特定的功能增强的实现 static class MyMethodInterceptor implements MethodInterceptor { //特定的功能增强实现MyMethodInterceptor里面持有被代理的类或者接口target private Object target; public MyMethodInterceptor(Object target) { this.target = target; } //在intercept方法进行调用被代理类或者接口的方法之前进行拦截实现前置、后置、环绕、异常处理等功能的增强 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("**************** " + method.getName()); // 前置增强 doSomethingBefore(); // 返回值 Object res = null; // 这里可以调用父类的该方法,当是生成接口的代理时不可调用。 // Object res = methodProxy.invokeSuper(proxy, args); // 通过method来调用被代理对象的方法 if (this.target != null) { res = method.invoke(target, args); } // 后置增强 doSomethingAfter(); return res; } private void doSomethingBefore() { System.out.println("老板你好,这个我试过了,很不错,推荐给你!"); } private void doSomethingAfter() { System.out.println("老板你觉得怎样? 欢迎下次....."); } }; public static void main(String[] args) { //创建Enhancer对象用来生成代理类 Enhancer e = new Enhancer(); //创建需要被代理的类TeacherCang TeacherCang tc = new TeacherCang(); // 设置增强回调 e.setCallback(new MyMethodInterceptor(tc)); //对接口生成代理对象 System.out.println("--------------------cglib动态代理:对接口Girl进行代理----------------------"); //设置要代理的接口 e.setInterfaces(new Class[] { Girl.class }); //生成代理的接口的动态代理对象 Girl g = (Girl) e.create(); //调用被代理的接口的dating方法 g.dating(1.8f); // 对类生成代理对象 System.out.println("--------------------cglib动态代理:对类TeacherCang进行代理----------------------"); //设置要代理的类 e.setSuperclass(TeacherCang.class); //把前面的设置的接口Girl置为空 e.setInterfaces(null); //当有多个callback时,需要通过callbackFilter来指定被代理方法使用第几个callback /* e.setCallbackFilter(new CallbackFilter() { @Override public int accept(Method method) { return 0; } });*/ //生成代理的类TeacherCang的动态代理对象 TeacherCang proxy = (TeacherCang) e.create(); //调用代理的类TeacherCang的dating方法 proxy.dating(1.8f); } }
输出结果:
--------------------cglib动态代理:对接口Girl进行代理---------------------- **************** dating 老板你好,这个我试过了,很不错,推荐给你! 身高可以,可以约! 老板你觉得怎样? 欢迎下次..... --------------------cglib动态代理:对类TeacherCang进行代理---------------------- **************** dating 老板你好,这个我试过了,很不错,推荐给你! 身高可以,可以约! 老板你觉得怎样? 欢迎下次.....
http web请求处理,请求过来后将经过转码、解析、参数封装、鉴权......一系列的处理(责任),而且要经过多少处理是可以灵活调整的。
将所有的处理都写在一个类中可否?
不行
分成多个类如何灵活组合在一起?
责任链:所有的处理者都加入到这个链式,一个处理完后,转给下一个。
责任链模式具体实现步骤:
1)抽象出责任接口,具体责任逻辑实现责任接口。
2)根据处理过程需要,将具体责任实现逻辑组合成链
3)使用者使用链
典型代表:Filter(过滤器)、Intercept(拦截器)
责任链模式类图:
和装饰者模式的区别在哪里?
装饰者模式意在功能的叠加,责任链模式意在链式的处理
eg:责任链模式代码示例
抽象出责任接口:
/** * * @Description: 责任接口 * @author leeSamll * @date 2018年11月25日 * */ public interface Responsibility { void process(Request request, ResponsibilityChain chain); }
具体的责任逻辑实现责任接口:
ResponsibilityA
package com.study.design.mode.samples.responsibility; /** * * @Description: 具体的责任逻辑实现 * @author leeSamll * @date 2018年11月25日 * */ public class ResponsibilityA implements Responsibility { @Override public void process(Request request, ResponsibilityChain chain) { //前置增强 System.out.println("Before Responsibility-A done something..."); //ResponsibilityA处理完以后调用ResponsibilityChain的process方法交给下一个责任逻辑处理 chain.process(request); //后置增强 } }
ResponsibilityB
package com.study.design.mode.samples.responsibility; /** * * @Description: 具体的责任逻辑实现 * @author leeSamll * @date 2018年11月25日 * */ public class ResponsibilityB implements Responsibility { @Override public void process(Request request, ResponsibilityChain chain) { //前置增强 System.out.println("Before Responsibility-B done something..."); //ResponsibilityB处理完以后调用ResponsibilityChain的process方法交给下一个责任逻辑处理 chain.process(request); //后置增强 } }
责任链:
ResponsibilityChain
package com.study.design.mode.samples.responsibility; import java.util.ArrayList; import java.util.List; /** * * @Description: 责任链,所有的责任加到责任链里面进行处理 * @author leeSamll * @date 2018年11月25日 * */ public class ResponsibilityChain { //存放具体的责任逻辑 private List<Responsibility> responsibilitys; private int index = 0; public ResponsibilityChain() { this.responsibilitys = new ArrayList<>(); } //顺序调用加入的责任逻辑,一个处理完以后交给下一个继续处理,下一个处理完以后会通过this回调process看是否有下一个继续处理 public void process(Request request) { if (this.index < this.responsibilitys.size()) { this.responsibilitys.get(index++).process(request, this); } } //加入具体的责任逻辑 public void register(Responsibility res) { this.responsibilitys.add(res); } }
请求接口:
package com.study.design.mode.samples.responsibility; /** * * @Description: 请求接口 * @author leeSamll * @date 2018年11月25日 * */ public interface Request { }
调用者调用示例
package com.study.design.mode.samples.responsibility; /** * * @Description: 调用者调用示例 * @author leeSamll * @date 2018年11月25日 * */ public class PlayGame { public static void main(String[] args) { //创建一个责任链 ResponsibilityChain chain = new ResponsibilityChain(); //往责任链里面加入具体的责任逻辑 chain.register(new ResponsibilityA()); chain.register(new ResponsibilityB()); //开始处理 chain.process(new Request() { }); } }
输出结果:
Before Responsibility-A done something...
Before Responsibility-B done something...
使用者依赖的接口与提供者的接口不匹配时,就加一层适配,而不改两端的代码。
适配器模式类图:
说明:
使用者使用Target接口,但是提供者Provider又没有实现Target接口,这个时候就需要加一层适配Adaper,Adaper里面持有Provider,在Adapter的methodA()方法里面调用Provider的methodB方法
和代理、装饰的区别在哪里?
适配器模式不进行功能增强
使用方要完成一个功能,需要调用提供方的多个接口、方法,调用过程复杂时,我们可以再提供一个高层接口(新的外观),将复杂的调用过程向使用方隐藏。
外观(门面)模式类图:
这里使用了设计原则:最少知识原则(迪米特原则)
观察者模式类图:
说明:
主题Subject面向观察者接口Observer编程,主题里面可以添加、删除和通知观察者Observer;
注意每个观察者都有一个回调方法update,如果有变化就会在主题的notifyObservers()方法里面调用update方法,把最新的变化给到观察者
变化之处:观察者会变,观察者的数量会变。
不变:主题的代码要不受观察者变化的影响。
观察者模式定义:
定义了对象之间一对多的依赖关系,当一端对象改变状态时,它的所有依赖者都会收到通知并自动更新(被调用更新方法)。也称为:监听模式、发布订阅模式。提供一种对象之间松耦合的设计方式。
设计原则:为了交互对象之间的松耦合设计而努力!
Java.util. Observable 可被观察的(主题),具体主题扩展它。
java.util.Observer 观察者接口,具体观察者实现该接口。
主题Observable:
观察者接口Observer
使用代码示例:
package com.study.design.mode.samples; /** * * @Description: java中提供的观察者设计模式 * @author leeSamll * @date 2018年11月25日 * */ import java.util.Observable; import java.util.Observer; public class ObserverSample { public static void main(String[] args) { //创建主题 Observable subject1 = new Observable() { //通知观察者变化的数据data public synchronized void notifyObservers(Object data) { //设置 java.util.Observable.changed = true表示发生了改变 setChanged(); //调用父类的notifyObservers方法通知观察者发生变化 //调用链java.util.Observable.notifyObservers(Object)->java.util.Observer.update(Observable, Object) super.notifyObservers(data); } }; //添加观察者 subject1.addObserver(new Observer() { //主题回调观察者的update方法通知改变 @Override public void update(Observable o, Object arg) { System.out.println("观察者1收到通知被更新了..." + arg); } }); //添加观察者 subject1.addObserver(new Observer() { //主题回调观察者的update方法通知改变 @Override public void update(Observable o, Object arg) { System.out.println("观察者2收到通知被更新了..." + arg); } }); //通知改变 subject1.notifyObservers("change1"); subject1.notifyObservers("change2"); } }
输出结果:
观察者2收到通知被更新了...change1
观察者1收到通知被更新了...change1
观察者2收到通知被更新了...change2
观察者1收到通知被更新了...change2
示例:
请为你的系统设计一个命令行界面,用户可输入命令来执行某项功能。
系统的功能会不断增加,命令也会不断的增加。
如何将一项一项的加入到这个命令行界面?
如何让我们的命令程序写好以后,不因为功能的添加而修改,又可灵活的加入命令、功能。
命令模式类图:
命令模式的定义:
以命令的方式,解耦调用者与功能的具体实现者,降低系统耦合度,提供了灵活性。
适用场景:Servlet、Controller、线程池
命令模式伪代码示例:
package com.study.design.mode.samples.command; /** * * @Description: 命令模式 * @author liguangsheng * @date 2018年11月25日 * */ public class Receiver { //存放具体的命令实现 private Map<String,Command> commands; //把具体的命令和对应的实现加入commands public void register(String strCommand,Command command) { commands.put(strCommand,command); } //使用者调用receive方法传入命令去执行 public void receive(String command) { Command commandObj = commands.get(command); if(null != commandObj) { commandObj.exceute(); return; } System.out.println("不支持此命令" + command); } }
命令模式与策略模式的区别:
命令模式类图:
策略模式类图:
区别:
策略模式侧重的是一个行为的多个算法的实现,可互换算法。
命令模式侧重的是为多个行为提供灵活的执行方式
示例:一个类对外提供了多个行为,同时该类对象有多种状态,不同的状态下对外的行为表现不同,我们该如何来设计该类,让它对状态可以灵活扩展?
如请为无人自动咖啡售卖机开发一个控制程序。
说明:用户可以在咖啡机上进行支付、退款、购买、取咖啡等操作
咖啡机状态转换图:
说明:
不同的状态下这四种操作将有不同的表现。如在没有支付的状态下,用户在咖啡机上点退款、购买、取咖啡,和在已支付的状态下做这三个操作。
普通实现:
package com.study.design.mode.samples.state; /** * * @Description: 普通的咖啡机: 没有使用状态模式的咖啡机 * @author liguangsheng * @date 2018年11月25日 * */ public class CoffeeMachine { final static int NO_PAY = 0; final static int PAY = 1; final static int SOLD = 2; final static int SOLD_OUT = 4; private int state = SOLD_OUT; private int store; public CoffeeMachine(int store) { this.store = store; if (this.store > 0) { this.state = NO_PAY; } } public void pay() { switch (this.state) { case NO_PAY: System.out.println("支付成功,请确定购买咖啡。"); this.state = PAY; break; case PAY: System.out.println("已支付成功,请确定购买咖啡。"); break; case SOLD: System.out.println("待取咖啡中,请稍后购买!"); break; case SOLD_OUT: System.out.println("咖啡已售罄,不可购买!"); } } public void refund() { switch (this.state) { case NO_PAY: System.out.println("你尚未支付,请不要乱按!"); break; case PAY: System.out.println("退款成功!"); this.state = NO_PAY; break; case SOLD: System.out.println("已购买,请取用!"); break; case SOLD_OUT: System.out.println("咖啡已售罄,不可购买!"); } } // 购买 public void buy() { switch (this.state) { case NO_PAY: System.out.println("你尚未支付,请不要乱按!"); break; case PAY: System.out.println("购买成功,请取用!"); this.state = SOLD; break; case SOLD: System.out.println("已购买,请取用!"); break; case SOLD_OUT: System.out.println("咖啡已售罄,不可购买!"); } } // 取coffee public void getCoffee() { switch (this.state) { case NO_PAY: System.out.println("你尚未支付,请不要乱按!"); break; case PAY: System.out.println("已购买,请取用!"); break; case SOLD: System.out.println("请放好杯子,3秒后将出咖啡!"); this.store--; if (this.store == 0) { this.state = SOLD_OUT; } else { this.state = NO_PAY; } break; case SOLD_OUT: System.out.println("咖啡已售罄,不可购买!"); } } }
如何让状态可以灵活扩展?
从分析可以看出,变化的是状态,同时不同的状态同一个行为的表现不同,这样的话就可以把变化的状态抽象生成接口,然后不同的状态行为实现状态接口做该状态下的具体行为。这里可以采用状态模式
状态模式类图:
状态模式代码示例:
把变化的状态抽象生成接口State,里面含有不同状态下的行为方法
package com.study.design.mode.samples.state; /** * * @Description: 状态接口 * @author liguangsheng * @date 2018年11月25日 * */ public interface State { void pay(); void refund(); void buy(); void getCoffee(); }
不同的状态实现状态接口State
没有支付状态
package com.study.design.mode.samples.state; /** * * @Description: 没有支付状态 * @author liguangsheng * @date 2018年11月25日 * */ public class NoPayState implements State { private NewCoffeeMachine machine; public NoPayState(NewCoffeeMachine machine) { this.machine = machine; } @Override public void pay() { System.out.println("支付成功,请去确定购买咖啡。"); this.machine.state = this.machine.PAY; } @Override public void refund() { System.out.println("你尚未支付,请不要乱按!"); } @Override public void buy() { System.out.println("你尚未支付,请不要乱按!"); } @Override public void getCoffee() { System.out.println("你尚未支付,请不要乱按!"); } }
已支付状态
package com.study.design.mode.samples.state; /** * * @Description: 已支付状态 * @author liguangsheng * @date 2018年11月25日 * */ public class PayState implements State { private NewCoffeeMachine machine; public PayState(NewCoffeeMachine machine) { this.machine = machine; } @Override public void pay() { System.out.println("您已支付,请去确定购买!"); } @Override public void refund() { System.out.println("退款成功,请收好!"); this.machine.state = this.machine.NO_PAY; } @Override public void buy() { System.out.println("购买成功,请取用"); this.machine.state = this.machine.SOLD; } @Override public void getCoffee() { System.out.println("请先确定购买!"); } }
售出状态
package com.study.design.mode.samples.state; /** * * @Description: 售出状态 * @author liguangsheng * @date 2018年11月25日 * */ public class SoldOutState implements State { private NewCoffeeMachine machine; public SoldOutState(NewCoffeeMachine machine) { this.machine = machine; } @Override public void pay() { System.out.println("当前状态为售出,请取咖啡!"); } @Override public void refund() { System.out.println("当前状态为售出,不能退款!"); } @Override public void buy() { System.out.println("当前状态为售出,请取咖啡!"); } @Override public void getCoffee() { System.out.println("咖啡已出,请取咖啡!"); } }
售罄状态
package com.study.design.mode.samples.state; /** * * @Description: 售罄状态 * @author liguangsheng * @date 2018年11月25日 * */ public class SoldState implements State { private NewCoffeeMachine machine; public SoldState(NewCoffeeMachine machine) { this.machine = machine; } @Override public void pay() { System.out.println("咖啡已卖完,不能支付!"); } @Override public void refund() { System.out.println("不能退款!"); } @Override public void buy() { System.out.println("咖啡已卖完,不能购买!"); } @Override public void getCoffee() { System.out.println("咖啡已卖完!"); } }
使用了状态模式的咖啡机
package com.study.design.mode.samples.state; /** * * @Description: 使用了状态模式的咖啡机 * @author liguangsheng * @date 2018年11月25日 * */ public class NewCoffeeMachine { final State NO_PAY, PAY, SOLD, SOLD_OUT; State state; int store; //初始化状态 public NewCoffeeMachine(int store) { NO_PAY = new NoPayState(this); PAY = new PayState(this); SOLD = new SoldState(this); SOLD_OUT = new SoldOutState(this); this.store = store; if (this.store > 0) { this.state = NO_PAY; } } //支付行为委托给当前状态实例 public void pay() { this.state.pay(); } //退款行为委托给当前状态实例 public void refund() { this.state.refund(); } //买咖啡行为委托给当前状态实例 public void buy() { this.state.buy(); } //取咖啡行为委托给当前状态实例 public void getCoffee() { this.state.getCoffee(); } }
状态模式、命令模式、策略模式的区别
状态模式类图:
命令模式类图:
策略模式类图:
区别:
状态模式应用于状态机的情况
策略模式侧重的是一个行为的多个算法的实现,可互换算法。
命令模式侧重的是为多个行为提供灵活的执行方式
请开发一个画图程序,可以画各种颜色不同形状的图像,请用面向对象的思想设计图像
分析:
1)比如有红、黄、蓝三种颜色
2)形状有方形、圆、三角形
3)圆可以是红圆、黄圆、蓝圆
变化:
会从两个维度发生变化:形状、颜色
任其在这两个维度各自变化,为这两个维度搭个桥,让他们可以融合在一起:桥接模式
桥接模式的实现步骤:
1)抽象:分别对各自的维度进行抽象,将共同部分抽取出来
2)组合:将抽象组合在一起(桥接)
桥接模式的定义:将多个维度的变化以抽象的方式组合在一起。使用者面向抽象。个维度间解耦,可自由变化。
饥汉式1——可用
package com.study.design.mode.service; public class Singleton { private final static Singleton INSTANCE = new Singleton(); private Singleton() { } public static Singleton getInstance() { return INSTANCE; } }
饥汉式2——可用
package com.study.design.mode.service; public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return instance; } }
懒汉式1——不可用
package com.study.design.mode.service; public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
当两个线程同时进入if里面时就会创建两个实例,不是单例,线程不安全,所以不可用
懒汉式2——不推荐使用
package com.study.design.mode.service; public class Singleton { private static Singleton singleton; private Singleton() { } public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
线程安全,但不推荐使用。缺点是实例化后就不应该再同步了,效率低
懒汉式3——不可用
package com.study.design.mode.service; public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { singleton = new Singleton(); } } return singleton; } }
当两个线程同时进入if里面时就会产生两个实例,做不到单例
懒汉式4——双重检查——推荐使用
package com.study.design.mode.service; public class Singleton { private static volatile Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
注意:volatile关键字修饰很关键,保证可见性,一个线程先创建了,其他线程就就会看到这个改变,不会再创建,如果没有这个关键字还是不能保证单例。
优点:线程安全;延迟加载;效率较高
懒汉式5——静态内部类方式——推荐使用
package com.study.design.mode.service; public class Singleton { private Singleton() { } private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonInstance.INSTANCE; } }
优点:避免了线程不安全,延迟加载,效率高
原理:类的静态属性只会在第一次加载类的时候初始化。在这里,JVM的加载机制帮助我们保证了线程安全性,在类进行初始化时,别的线程是无法进入的
懒汉式6——用枚举——推荐使用
package com.study.design.mode.service; public enum Singleton { INSTANCE; public void whateverMethod() { } }
示例:
当我们设计一个类时,我们能明确它对外提供的某个方法的内部执行步骤,但一些步骤,不同的子类有不同的行为时,我们该如何来设计该类?
可以用模板方法设计模式
优点:
1)封装不变的部分,扩展可变的部分
2)提取公共代码,便于维护。
3)行为由父控制,子类实现。
适用场景:
1)有多个子类共有的方法,且逻辑相同
2)重要的、复杂的方法,可以考虑作为模板方法
模板方法设计模式代码示例:
package com.study.design.mode.service; public abstract class Game { protected abstract void initialize(); protected abstract void startPlay(); protected abstract void endPlay(); // 模板方法 public final void play() { // 初始化游戏 initialize(); // 开始游戏 startPlay(); // 结束游戏 endPlay(); } }
设计模式总结
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用新的运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
这些设计模式特别关注对象之间的通信。
设计原则总结
1. 变化隔离原则:找出变化,分开变化和不变的
隔离,封装变化的部分,让其他部分不受它的影响。
2. 面向接口编程 ——隔离变化的方式
使用者使用接口,提供者实现接口。“接口”可以是超类!
3. 依赖倒置原则(里氏替换原则)——隔离变化的方式
依赖抽象,不要依赖具体类!
4. 开闭原则:对修改闭合,对扩展开放——隔离变化的方式
可以继承一个类或者接口扩展功能,但是不能修改类或者接口的原有功能
5. 最少知道原则,又称迪米特法则
6. 多用组合,少用继承——灵活变化的方式
“有一个”可能比“是一个”更好。
7. 单一职责原则——方法设计的原则
每个方法只负责一个功能,不要把很多功能写在一个方法里面
说明:如果前面的设计思想和设计原则都忘记了,就要找出变化,区分出不变的和变化的,把变化的部分独立出接口,或者使用组合
示例代码获取地址:
https://github.com/leeSmall/FrameSourceCodeStudy
参考文章:
框架源码系列一:设计模式(设计思想、设计原则、各种设计模式介绍、设计模式总结)
标签:关键字 回调 volatile http 对象 机制 size change iat
原文地址:https://www.cnblogs.com/leeSmall/p/10010006.html