设计模式详解链接
设计模式(一)之单例模式(Singleton Pattern)深入浅出
设计模式(二)之委派模式(Delegate Pattern)深入浅出
设计模式(三)之策略模式(Strategy Pattern)深入浅出
设计模式(四)之模板模式(Template Method Pattern)深入浅出
设计模式(五)之适配器模式(Adapter Pattern)深入浅出
设计模式(六)之装饰器模式(Decorator Pattern)深入浅出
设计模式(七)之观察者模式(Observer Pattern)深入浅出
本篇期望目标:
- 简要分析GOF23种设计模式和设计原则,做整体认知。
- 剖析Spring的编程思想,启发思维,为之后深入学习Spring做铺垫。
- 了解个设计模式之间的关联,解决设计模式混淆的问题。
本篇内容定位:
- 设计模式在于理解,不只是在于形式。
- 不要为了套用设计模式而使用设计模式,而是在业务上遇到问题时,很自然地想到设计模式作为一种解决方案。
设计模式时一门艺术,设计模式来源于生活
孔子人生之道就是模板模式(前人栽树,后人乘凉):
从出生元婴、二十加冕、三十而立、四十不惑、五十知天命、六十花甲、七十古稀不逾矩、八、九十耄耋 ......
GOF23种设计模式分类
分类 | 设计模式 |
创新型 | 工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)、建造者模式(Builder)、原型模式(Prototype)、单例模式(Singleton) |
结构型 | 适配器模式(Adapter)、桥接模式(Bridge)、组合模式(Composite)、装饰者模式(Decorator)、门面模式(Facade)、享元模式(Flyweight)、代理模式(Proxy) |
行为型 | 解释器模式(Interpreter)、模板方法模式(Template Method)、责任链模式(Chain of Responsibility)、命令模式(Command)、迭代器模式(Iterator)、调解者模式(Mediator)、备忘录模式(Memento)、观察者模式(Observer)、状态模式(State)、策略模式(Strategy)、访问者模式(Visitor) |
容易混淆的设计模式对比
单例模式和工厂模式
- 工厂类一般就是被设计为单例。Spring的ApplicationContext它既是工厂也是单例
策略模式和工厂模式
- 工厂模式包含工厂方法和抽象工厂属于创新型模式,策略模式属于行为型模式。
- 工厂模式主要目的是封装号好创建逻辑,策略模式接收工厂创建好的对象,从而实现不同的行为。创建:new 行为:invoke
策略模式和委派模式
- 策略模式是委派模式内部的一种实现形式,策略模式关注的是结果是否能互相替代。例如选择支付方式,支付宝支付、微信支付等...,它们可以相互替换。
- 委派模式更关注分发和调度的过程。有可能采用if..else...条件分支语句来分发,如果结果相似规整的话,内部也可以用策略模式,来替代条件分支语。
模板方法模式和工厂方法模式
- 工厂方法模式提共多个算法共用户选择,工厂内部逻辑不允许改变。
- 模板方法模式不允许改变执行逻辑的流程,用户也没有选择的权力。
模板方法模式和策略模式
- 模板方法和策略模式都有封装算法。
- 策略模式是使不同算法可以相互替换,且不影响客户端应用层的使用。
- 模板方法是针对定义一个算法的流程,将一些有细微差异的部分交给子类实现。策略模式算法实现是封闭的。模板方法可以微调逻辑,但流程不能改变。
- 模板模式不能改变算法流程,策略模式可以改变算法流程且可以替换。策略模式通常用来替代if...else..等条件分支语句。
装饰者模式和静态代理模式
- 装饰者模式关注点在于给对象动态扩展、添加方法、而代理更加注重控制对对象的访问。
- 代理模式通常会在代理类中创建被代理对象的实例,而装饰者模式通常把被装饰者作为构造参数。
装饰者模式和适配器模式
- 装饰者模式和适配器模式都属于包装器模式(Wrapper)。
- 装饰器模式可以实现被装饰者与相同的接口或者继承被装饰者作为它的子类,而适配器和被适配者可以实现不同的接口。
适配器模式和静态代理模式
- 适配器可以结合静态代理来实现,保存被适配对象的引用,但不是唯一的实现方式。适配器还可以通过继承来实现,有很多实现方式。
适配器模式和策略模式
- 在适配业务复杂的情况下,利用策略模式优化动态适配逻辑。
Spring中常用的设计模式对比
提问:如何让学过的设计模式真正的属于自己?
不能死记硬背,穷举法 + 类比法
设计模式 | 一句话归纳 | 举例 |
工厂模式(Factory) | 只对结果负责,封装创建过程 | BeanFactory、Calendar |
单例模式(Singleton) | 保证独一无二 | ApplicationContext、Calendar |
原型模式(Prototype) | 拔一根猴毛,吹出千万个 | ArrayList、PrototypeBean |
代理模式(Proxy) | 找人办事,增强职责 | ProxyFactoryBean、JdkDynamicAopProxy、CglibAopProxy |
委派模式(Delegate) | 干活算你的(普通员工),功劳算我的(项目经理) | DispatcherServlet、BeanDefinitionParserDelegate |
策略模式(Strategy) | 用户选择,结果统一 | InstantiationStrategy |
模板模式(Template) | 流程标准化,自己实现定制 | JdbcTemplate、HttpServlet |
适配器模式(Adapter) | 兼容转换头 | AdvisorAdapter、HandlerAdapter |
装饰者模式(Decorator) | 包装,同宗同源 | BufferedReader、InputStream、OutputStream、HttpHeadResponseDecorator |
观察者模式(Observer) | 任务完成时通知 | ContextLoaderListener |
Spring中的编程思想总结
Spring思想 | 应用场景(特点) | 一句话归纳 |
OOP | Object Oriented Programing(面向对象编程)用程序归纳总结生活中一切的事物 | 封装、继承、多态 |
BOP | Bean Oriented Programing(面向Bean编程)面向Bean(普通的Java类)设计程序 | 一切从Bean开始 |
AOP | Aspect Oriented Programing(面向切面编程)找出多个类中有一定规律的代码,开发时拆开,运行时再合并。面向切面编程即面向规则编程。 | 解耦,专人做专事 |
IOC | Inversion of Control(控制反转)将new对象的动作交给Spring管理,并由Spring保存已创建的对象(IOC容器) | 转交控制权(即控制权反转) |
DI/DL | Dependency Injection(依赖注入)或者Dependency Lookup(依赖查找),Spring不仅保存自己创建的对象,而且保存对象与对象之间的关系。注入即赋值,主要三种方式构造方法、set方法、直接赋值。 | 赋值 |
OOP:面向对象编程,用程序来描述生活中一切事务(需求转换为代码实现)。
BOP:面向Bean编程,Java本身就是一种面向对象的编程(用一个一个的Bean来体现的),BOP目的是解放程序员的双手,提出一种代码的管理理念。Spring的理念一切从Bean开始。Spring不需要new bean ,只需要关注bean与bean之间的关系。
AOP:面向切面编程,找出多个bean中有一定规律的代码,开发时将其拆开,运行时再将其合并。面向切面编程就是面向规则编程。只有代理才能达到这样的一个目的。
IOC:控制反转,通俗讲控制权反转,创建对象的控制权反转,反转给Spring(BeanFactory),我们只需要伸手拿。(衣来伸手饭来张口,程序员哥哥解放啦)
DI:依赖注入,依赖查找,Spring不仅能创建对象,能够保存对象与对象之间的关联关系,自动赋值,主要有三种赋值方法,构造方法,set方法,直接赋值(反射,暴力强吻)
@Autowried
private Object object; 直接赋值
@Resource("beanName")
Public void setXXX(){} set方法赋值
OOP理念人工干预太重(其中命名规范、行为特征规则、通用逻辑都需要程序员实现),BOP把对Bean的干预由手动挡变成自动挡,BOP是具体理念,AOP、DI和IOC是基于BOP理念的具体实现。
从
private Persion persion = new Persion();
到
@Autowried private Persion persion;
是BOP的体现,对象由IOC创建,并通过DI注入到变量persion,命名规范、行为特征规则、通用逻辑由Spring完成,AOP实现注解开发
AOP的应用场景:
- Authentication(权限认证)
- Auto Caching(自动缓存处理)
- Error Handling(统一错误处理)
- Debugging(调试信息输出)
- Logging(日志记录)
- Transactions(事务处理)
- .......
SpringAOP之必须明白的几个概念:
Aspect(切面):通常是一个类,里面可以定义切入点和通知。
JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。
Advice(通知):AOP在特定的切入点上执行的增强处理,有before、after、afterReturning、afterThrowing、around
Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
SpringAOP之Execution表达式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
modifiers-pattern:方法的操作权限
ret-type-pattern:返回值【必填】
declaring-type-pattern:方法所在包
name-pattern:方法名【必填】
param-pattern:参数名
throws-pattern:异常
定义规则,找到一个规律,多个类中的方法形成一个切面
XML配置案例:
<!-- 配置 AOP --> <aop:config> <!-- 配置切点表达式 --> <aop:pointcut id="pointcut" expression="execution(* com.itdoc.spring.aop.cutface.*.*(..))"/> <!-- 配置切面通知 --> <aop:aspect ref="asjectLogging" order="2"> <aop:around method="around" pointcut-ref="pointcut"/> <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/> </aop:aspect> <aop:aspect ref="validate" order="1"> <aop:before method="beforeValidate" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
Java配置案例:
/** * 添加aop日志打印 * */ @Aspect @Component @Slf4j public class WebLogAspect { /** * 进入方法时间戳 */ private Long startTime; /** * 方法结束时间戳(计时) */ private Long endTime; public WebLogAspect() { } /** * 定义请求日志切入点,其切入点表达式有多种匹配方式,这里是指定路径 */ @Pointcut("execution(public * com.soyoung.ad.engine.controller.*.*(..))") public void webLogPointcut() { } /** * 前置通知: * 1. 在执行目标方法之前执行,比如请求接口之前的登录验证; * 2. 在前置通知中设置请求日志信息,如开始时间,请求参数,注解内容等 * * @param joinPoint * @throws Throwable */ @Before("webLogPointcut()") public void doBefore(JoinPoint joinPoint) { // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //获取请求头中的User-Agent UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent")); //打印请求的内容 startTime = System.currentTimeMillis(); log.info("请求开始时间:{}", LocalDateTime.now()); log.info("请求Url : {}", request.getRequestURL().toString()); log.info("请求方式 : {}", request.getMethod()); log.info("请求ip : {}", request.getRemoteAddr()); log.info("请求方法 : ", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs())); // 系统信息 log.info("浏览器:{}", userAgent.getBrowser().toString()); log.info("浏览器版本:{}", userAgent.getBrowserVersion()); log.info("操作系统: {}", userAgent.getOperatingSystem().toString()); } /** * 返回通知: * 1. 在目标方法正常结束之后执行 * 1. 在返回通知中补充请求日志信息,如返回时间,方法耗时,返回值,并且保存日志信息 * * @param ret * @throws Throwable */ @AfterReturning(returning = "ret", pointcut = "webLogPointcut()") public void doAfterReturning(Object ret) throws Throwable { endTime = System.currentTimeMillis(); log.info("请求结束时间:{}", LocalDateTime.now()); log.info("请求耗时:{}", (endTime - startTime)); // 处理完请求,返回内容 log.info("请求返回 : {}", ret); } /** * 异常通知: * 1. 在目标方法非正常结束,发生异常或者抛出异常时执行 * 1. 在异常通知中设置异常信息,并将其保存 * * @param throwable */ @AfterThrowing(value = "webLogPointcut()", throwing = "throwable") public void doAfterThrowing(Throwable throwable) { // 保存异常日志记录 log.error("发生异常时间:{}", LocalDateTime.now()); log.error("抛出异常:{}", throwable.getMessage()); } }