【源码分析设计模式 11】SpringMVC中的策略模式(1)

下面就以商场促销为例使用策略模式实现商场促销算法。UML图如下:

1、上下文类


首先声明一个 CashSuper 对象,通过构造方法,传入具体的收费策略, getResult() 方法的功能为根据收费策略的不同获取计算结果。

package designMode.strategy;

public class CashContext {

private CashSuper cashSuper;

public CashContext(CashSuper cashSuper) {

this.cashSuper = cashSuper;

}

public double getResult(double money){

return cashSuper.acceptCash(money);

}

}

2、现金收费抽象类


策略类,为抽象类,抽象出收费的方法供子类实现。

package designMode.strategy;

public abstract class CashSuper {

public abstract double acceptCash(double money);

}

3、正常收费子类


package designMode.strategy;

public class CashNormal extends CashSuper {

@Override

public double acceptCash(double money) {

return money;

}

}

4、打折收费子类


package designMode.strategy;

public class CashRebate extends CashSuper {

private double moneyRebate = 0.8;

public CashRebate(double moneyRebate) {

this.moneyRebate = moneyRebate;

}

@Override

public double acceptCash(double money) {

return money*moneyRebate;

}

}

5、返利收费子类


package designMode.strategy;

public class CashReturn extends CashSuper {

private double moneyConditation = 0.0;

private double moneyReturn = 0.0d;

public CashReturn(double moneyConditation, double moneyReturn) {

this.moneyConditation = moneyConditation;

this.moneyReturn = moneyReturn;

}

@Override

public double acceptCash(double money) {

double result = money;

if(money>moneyConditation){

result = money-Math.floor(money/moneyConditation)*moneyReturn;

}

return result;

}

}

6、client客户端


package designMode.strategy;

import java.util.Scanner;

public class Client {

public static void main(String[] args) {

CashContext cashContext = null;

Scanner scanner = new Scanner(System.in);

System.out.println(“请输入打折方式(1/2/3):”);

int in = scanner.nextInt();

String type = “”;

switch (in){

case 1:

cashContext = new CashContext(new CashNormal());

type += “正常收费”;

break;

case 2:

cashContext = new CashContext(new CashReturn(300,100));

type +=“满300返100”;

break;

case 3:

cashContext = new CashContext(new CashRebate(0.8));

type += “打八折”;

break;

default:

System.out.println(“请输入1/2/3”);

break;

}

double totalPrices = 0;

System.out.print(“请输入单价:”);

double price = scanner.nextDouble();

System.out.println(“请输入数量:”);

double num = scanner.nextDouble();

totalPrices = cashContext.getResult(price * num);

System.out.println(“单价:” + price + “,数量:” + num + “,类型:” + type + “,合计:” + totalPrices);

scanner.close();

}

}

7、运行结果


五、策略模式与工程模式的区别

==================

1、策略模式是行为性模式,适应行为的变化 ,强调父类的调用子类的特性 。

工厂模式是创建型模式,适应对象的变化,强调统一接口 。

2、策略模式封装行为,调用的时候必须先制定实例化具体的类,再调用抽象的方法; 策略模式的作用是让一个对象在许多行为中选择一种行为。

工厂模式封装对象,实例化对象后调用的时候要知道具体的方法。

3、策略模式是调用不同类方法, 工厂模式是对父类进行重写。

这俩个模式本来就是解决类似的问题,可以说是孪生兄弟,且内部实现都差不多,都是通过子类来覆盖父类而已,不过简单工厂是把父类直接摆在客户端,而策略模式是将父类隐藏在Context里面,这样封装更好。

4、举个例子

(1)产品之于加减乘除,水果之于苹果梨橘子香蕉,文具之于笔尺刀,这时产品比较具体、有限和没有多个算法重叠,这时实用简单工厂模式。

(2)产品之于商场促销中的返利(可为300返100、500返200、10000返500等等无数)、折扣(2折、2.5折、6折、9折、9.1折等等无数)、正常购买、消费积分(100元10积分、200元30积分等等无数),这时产品构造又多次重叠,且有在不同时刻应用不同的规则时使用策略模式。

5、总结

简单工厂模式只是解决了对象的创建问题,工厂需要包括所有的产品对象的创建,如果产品对象形式经常变化,就需要经常改动工厂,以致代码重新编译。所以策略模式就诞生了,策略模式—它定义了算法家族,分别封装起来,而不是像简单产品模式一样定义所有的产品类,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户,使客户拥有相同的访问过程。

简单工厂模式的核心是“简单工厂模式就是用来封装所有的产品对象的”。

策略模式理解核心是“策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中遇到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性”。

在基本的策略模式中,选择所用的具体实现的算法的职责由客户端对象承担,并转给策略模式的Context对象。这是策略模式本身纯粹的定义,所以,“选择所用最终怎样处理”还有很多文章可做。

看了课本之后对于这两个模式还是有很多不理解的地方,但是相信随着对设计模式进一步的学习,能够更好地体会到这其中的奥妙,学习是一个循序渐进的过程。

六、SpringMVC中的策略模式

=================

1、getDefaultStrategies


在DispatchServlet中的初始化组件中,用到了getDefaultStrategies方法,来决定不同组件的默认类型以实现组件的初始化操作。

// 传入ApplicationContext上下文和策略接口的Class类型

protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) {

// 相应组件的类名

String key = strategyInterface.getName();

// 从property中获取当前策略接口实现类的类名集合

String value = defaultStrategies.getProperty(key);

if (value != null) {

// 获取策略接口所有实现类的类名

String[] classNames = StringUtils.commaDelimitedListToStringArray(value);

List strategies = new ArrayList(classNames.length);

for (String className : classNames) {

try {

// 创建相应实现类的bean,并放入集合中

Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());

Object strategy = createDefaultStrategy(context, clazz);

strategies.add((T) strategy);

}

catch (ClassNotFoundException ex) {

throw new BeanInitializationException(

“Could not find DispatcherServlet’s default strategy class [” + className +

“] for interface [” + key + “]”, ex);

}

catch (LinkageError err) {

throw new BeanInitializationException(

“Error loading DispatcherServlet’s default strategy class [” + className +

“] for interface [” + key + “]: problem with class file or dependent class”, err);

}

}

// 返回策略接口实现类的集合

return strategies;

}

else {

return new LinkedList();

}

}

// 初始化真正调用的重载方法,默认返回策略实现类的第一个

protected T getDefaultStrategy(ApplicationContext context, Class strategyInterface) {

List strategies = getDefaultStrategies(context, strategyInterface);

if (strategies.size() != 1) {

throw new BeanInitializationException(

“DispatcherServlet needs exactly 1 strategy for interface [” + strategyInterface.getName() + “]”);

}

return strategies.get(0);

}

我们可以看到,DispatchServlet在初始化组件时,会传入相应组件的接口,获取到该组件的实现类集合,并将第一个实现类作为默认的组件使用,例如我们来看initLocaleResolver方法,它初始化了一个默认的本地化处理组件。

private void initLocaleResolver(ApplicationContext context) {

try {

this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);

if (logger.isDebugEnabled()) {

logger.debug(“Using LocaleResolver [” + this.localeResolver + “]”);

}

}

catch (NoSuchBeanDefinitionException ex) {

// We need to use the default.

this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

分享

这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

Spring Cloud实战

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

Spring Boot实战

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

面试题整理(性能优化+微服务+并发编程+开源框架+分布式)
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
mg src=“https://i-blog.csdnimg.cn/blog_migrate/e8329e1d1ac18a33ef119828189f2670.jpeg” alt=“img” style=“zoom: 33%;” />

分享

这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!

[外链图片转存中…(img-rxO19zbD-1713551174008)]

Spring Cloud实战

[外链图片转存中…(img-ydEinR8o-1713551174009)]

Spring Boot实战

[外链图片转存中…(img-6i5MzM5R-1713551174010)]

面试题整理(性能优化+微服务+并发编程+开源框架+分布式)
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值