策略模式属于对象的行为模式。其用意是针对一组算法封装到具有共同接口的独立的类中,从而使得他们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
策略模式是对算法的封装,是把使用算法的责任和算法本身区分开来。为派给不同的对象进行管理。策略模式通常会把一个系列的算法包装到一些列的策略类里面。作为一个抽象策略类的子类。用一句话说,就是: 准备一组算法,并且将每一个算法封装起来,使得他们可以互换。下面就是以一个示意性的实现讲解策略模式实例的结构。
这里模式涉及3个角色
环境(Context)角色:持有一个Strategy的引用
抽象策略(Strategy)角色:这是一个抽象角色,通常有一个接口或抽象类实现。
此角色给出所有的具体的策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
源代码:
环境角色类:
public class Context {
//持有一个具体策略的对象
private Strategy strategy;
/**
* 构造函数,传入一个具体策略对象
* @param strategy 具体策略对象
*/
public Context(Strategy strategy){
this.strategy = strategy;
}
/**
* 策略方法
*/
public void contextInterface(){
strategy.strategyInterface();
}
}
抽象策略类
public interface Strategy {
/**
* 策略方法
*/
public void strategyInterface();
}
具体策略类
public interface Strategy {
/**
* 策略方法
*/
public void strategyInterface();
}
具体的策略类:
public interface Strategy {
/**
* 策略方法
*/
public void strategyInterface();
}
public class ConcreteStrategyB implements Strategy {
@Override
public void strategyInterface() {
//相关的业务
}
}
public class ConcreteStrategyC implements Strategy {
@Override
public void strategyInterface() {
//相关的业务
}
}
使用场景
假设现在要设计一个贩卖各种书籍的电子商务网站的购物车系统。有个最简单的情况就是把所有货品的单价乘以数量。但是实际情况可能比这个要复杂。比如本网站可能对所有的高级会员提供每本20% 的促销折扣。对中级会员提供每本10%的促销折扣。对初级会员没有折扣。
根据描述: 折扣是根据以下的几个算法中的一个进行的:
算法一: 对初级会员没有折扣
算法二: 对中级会员提供10%的促销折扣,
算法三: 对高级会员提供20% 的促销折扣
使用策略模式来实现的结构图如下:
策略模式环境搭建
1 Maven依赖信息:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<!-- sprinboot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
2,MemberStrategy(抽象角色)
package com.taoao.juhe.service;
public interface MemberStrategy {
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算折扣后的价格
*/
public double calcPrice(double booksPrice);
}
3,ConcreteStrategy (具体实现角色)
/**
*初级会员的折扣价
*/
@Component
public class PprimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于初级会员没有折扣");
return booksPrice;
}
}
/**
* 中级会员
*/
@Component
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("中级会员的折扣为10%");
return booksPrice*0.9;
}
}
/***
* 高级会员价格
*/
@Component
public class AdvanceMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("高级会员的折扣为20%");
return booksPrice*0.8;
}
}
PayContextService (上下文)
@Component
public class PayContextStrategy {
public Double calcPrice(double booksPrice) {
PriceEntity priceEntity=priceMapper.priceEntity(booksPrice);
if(priceEntity==null){
return 0d;
}
//获取code,使用spring容器获取实例对象
String code=priceEntity.getCode();
if(Strings.isBlank(code)){
return 0d;
}
//执行实现的方法即可
MemberStrategy memberStrategy=springUtils.getBean(code,MemberStrategy.class);
return memberStrategy.calcPrice(100d);
}
}
SpringUtils
/**
* 使用beanid 获取spring容器中的bean对象
*/
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
}
}
数据库访问层
DROP TABLE IF EXISTS `price`;
CREATE TABLE `price` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`state` int(11) DEFAULT NULL,
`code` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of price
-- ----------------------------
INSERT INTO `price` VALUES ('1', '1', 'pprimaryMemberStrategy');
INSERT INTO `price` VALUES ('2', '2', 'intermediateMemberStrategy');
INSERT INTO `price` VALUES ('3', '3', 'advanceMemberStrategy');
数据库访问层
@Data
public class PriceEntity {
private int id;
private int state;
private String code;
}
Mapper层
package com.taoao.juhe.mapper;
import com.taoao.juhe.entity.CsNumberEntity;
import com.taoao.juhe.entity.PriceEntity;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface PriceMapper {
@Select("select * from price where state=#{booksPrice}")
public PriceEntity priceEntity(@Param(value = "booksPrice") double booksPrice);
}
Controller层
@RestController
public class PayController {
@Autowired
private PayContextStrategy payContextStrategy;
/**
* 找到对应的 会员层级
* @param booksPrice
* @return 折扣后的价格
*/
@RequestMapping("/calcPrice")
public Double calcPrice(double booksPrice){
if(Double.isNaN(booksPrice)){
return 0d;
}
return payContextStrategy.calcPrice(booksPrice);
}
}