Appearance
策略模式,重点是先找到能够进行策略划分的重点,然后将其一部分作为策略点进行分类,也不用非要强行去进行策略,在合理阶段时机进行就可以。
java 案例一
在第一讲的时候使用 创建一个类的然后这类会收集职责和判断条作为绑定,来解决一些复杂判断情况,这个案例将使用将条件和职责维护一起
java
public interface TravelStrategy {
void travel();
boolean isSuitable(Condition condition);
}
// 判断条件的对象
public class Condition {
private String weather;
private String traffic;
private int distance;
// getters and setters
}
// 实现接口的策略
public class BikeTravelStrategy implements TravelStrategy {
@Override
public void travel() {
System.out.println("Travel by bike.");
}
@Override
public boolean isSuitable(Condition condition) {
return "sunny".equals(condition.getWeather())
&& "light".equals(condition.getTraffic())
&& condition.getDistance() < 5;
}
}
// implement `isSuitable` for other strategies in the same way
// 上下文信息类(Context)
public class TravelContext {
private List<TravelStrategy> strategies = new ArrayList<>();
private TravelStrategy travelStrategy;
public TravelContext() {
strategies.add(new BikeTravelStrategy());
strategies.add(new CarTravelStrategy());
strategies.add(new WalkTravelStrategy());
}
// 使用的时候先注入 判读条件根据条件选出符合的策略
public void setCondition(Condition condition) {
for (TravelStrategy strategy : strategies) {
if (strategy.isSuitable(condition)) {
travelStrategy = strategy;
return;
}
}
throw new IllegalArgumentException("No suitable strategy found for condition: " + condition);
}
// 使用策略
public void travel() {
if (travelStrategy != null) {
travelStrategy.travel();
} else {
throw new IllegalStateException("Travel strategy is not set.");
}
}
}
// 实际使用
public class Main {
public static void main(String[] args) {
TravelContext travelContext = new TravelContext();
Condition condition = new Condition();
condition.setWeather("sunny");
condition.setTraffic("light");
condition.setDistance(3);
travelContext.setCondition(condition);
travelContext.travel(); // Output: Travel by bike.
condition.setWeather("rainy");
condition.setTraffic("heavy");
condition.setDistance(10);
travelContext.setCondition(condition);
travelContext.travel(); // Output: Travel by car.
condition.setWeather("windy");
condition.setTraffic("light");
condition.setDistance(1);
travelContext.setCondition(condition);
travelContext.travel(); // Output: Travel by walking.
}
}
java 案例二
现在 有三种组合状态组合对价格优惠,例如 第一个按钮是9 折 第二个按钮是 减10元 第三个按钮是 优惠20
java
public interface DiscountStrategy {
double apply(double price);
}
public class DiscountNine implements DiscountStrategy {
@Override
public double apply(double price) {
return price * 0.9;
}
}
public class DiscountTen implements DiscountStrategy {
@Override
public double apply(double price) {
return price - 10;
}
}
public class DiscountTwenty implements DiscountStrategy {
@Override
public double apply(double price) {
return price - 20;
}
}
public class PriceCalculator {
private List<DiscountStrategy> strategies = new ArrayList<>();
public void addStrategy(DiscountStrategy strategy) {
strategies.add(strategy);
}
public double calculate(double price) {
for (DiscountStrategy strategy : strategies) {
price = strategy.apply(price);
}
return price;
}
}
// 使用
public static void main(String[] args) {
PriceCalculator calculator = new PriceCalculator();
calculator.addStrategy(new DiscountNine());
calculator.addStrategy(new DiscountTen());
System.out.println(calculator.calculate(100)); // 输出: 71.0
}
js 案例一
上面同样的问题 js 的解决方法
html
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<form action="" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/>
请输入密码:<input type="text" name="passWord"/>
请输入手机号码:<input type="text" name="phoneNumber"/>
<input type="submit" value="提交"/>
</form>
<script>
/***************策略对象*************/
const strategies = {
isNotEmpty(value, errorMsg) {
if(value === ''){
return errorMsg;
}
},
minLength(value, length, errorMsg) {
if(value.length < length){
return errorMsg;
}
},
isMobile(value, errorMsg) {
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)){
return errorMsg;
}
}
}
/***************validator类***************/
class Validator {
constructor() {
this.cache = [];
}
add(dom, rules) {
for(let i = 0; i < rules.length; i++) {
let rule = rules[i];
let strategyAry = rule.strategy.split(':');
let errorMsg = rule.errorMsg;
this.cache.push(() => {
let strategy = strategyAry.shift();
strategyAry.unshift(dom.value);
strategyAry.push(errorMsg);
return strategies[strategy].apply(dom, strategyAry);
});
}
}
start() {
for(let i = 0; i < this.cache.length; i++) {
let validatorFunc = this.cache[i];
let errorMsg = validatorFunc();
if(errorMsg){
return errorMsg;
}
}
}
}
/************客户调用代码***********/
const registerForm = document.getElementById('registerForm');
const validatorFunc = () => {
const validator = new Validator();
validator.add(registerForm.userName,[{
strategy:'isNotEmpty',
errorMsg:'用户名不能为空'
},{strategy:'minLength:10',
errorMsg:'用户名长度不能小于10位'
}]);
validator.add(registerForm.passWord,[{
strategy:'minLength:6',
errorMsg:'用户密码不能小于6位'
}]);
validator.add(registerForm.phoneNumber,[{
strategy:'isMobile',
errorMsg:'手机号码格式不正确'
}]);
const errorMsg = validator.start();
return errorMsg;
}
registerForm.onsubmit = () => {
const errorMsg = validatorFunc();
if(errorMsg){
alert(errorMsg);
return false;
}
}
</script>
</body>
</html>
js 案例二
表单验证,通过if else来进行判断验证,后期会维护大量的if - else的逻辑验证
html
<body>
<form id='login-form' action="" method="post">
<label for="userName">用户名</label>
<input type="text" id="userName" name="userName">
<label for="phoneNumber">手机号</label>
<input type="number" id="phoneNumber" name="phoneNumber">
<label for="password">密码</label>
<input type="password" id="password" name="password">
<button id='login'>登录</button>
</form>
</body>
<script>
var loginForm = document.getElementById('login-form');
loginForm.onsubmit = function () {
if(loginForm.userName.value ===''){
alert('用户名不能为空')
return false
}
if(loginForm.password.value.length<6){
alert('密码长度不能小于六位')
return false;
}
if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(loginForm.phoneNumber.value)) {
alert('手机号格式错误')
return false;
}
}
</script>
- 利用策略模式要将代码拆成两个两部分,第一个部分'封装不同策略的策略组',也就是要将这些验证对象封装成策略对象,第二部分需要'Context'执行这些策略的调用,调用策略对象
第一部分要封装的策略组代码,也就是上面的if else,都需要被判断参数,报错信息。其中判断长度比较特别需要,一个额外的长度来控制各种长度出现的情况
二部分也就是一个'执行这些策略的调用',构想就是分成两部分,第一部分就是添加你想要的策略,第二部分就是统一执行你添加的策略
根据上面分析,整个负责验证的策略对象中的每个策略,最少需要两个参数,第一个参数就是要验证的值,第二个参数就是验证后的报错提示信息。除了特殊策略,例如长度验证需要一个关于长度的判断值
- es5 版本
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="registerForm" method="post">
<label for="userName">用户名</label>
<input type="text" id="userName" name="userName">
<label for="phoneNumber">手机号</label>
<input type="number" id="phoneNumber" name="phoneNumber">
<label for="password">密码</label>
<input type="password" id="password" name="password">
<button id='login'>登录</button>
</form>
</body>
<script>
/**
* 策略模式的两个点
* 1.存所有的验证(策略)
* 2.有一个可以调用所有验证(执行策略)
* 策略模式执行的类:
* 3.有一个方法这个方法是专门又来存这些验证的,存这些验证需要什么
* 3.1 需要一个能存的变量 因为要存多个这个变量选数组
* 3.2 要存的是什么,这里要存的是错误提示
* 3.2.1 要存错误提示怎么存是进来一个就调用把错误提示存进这个变量?
* 还是说存这些方法,在最后执行的时候调用这些方法比较好
* 这里选用后者存这些验证方法
* 3.3 能存了核心在哪,在策略模式最后需要有个执行者,执行这些策略,也就是
* 这个类需要一个执行方法start
* 4.要细分存储策略的add方法的具体参数,需要有一个要验证的对象,需要有验证的规则,需要有验证的提示
* **/
// 策略对象
const strategies = {
isNonEmpty: function (value, errorMsg) { // 不能为空,第一个参数要验证的值,第二个是提示
if(value === ''){
return errorMsg
}
},
minLength:function (value, length,errorMsg) { // 最小长度
if(value.length < length){
return errorMsg
}
},
isMobile:function (value,errorMsg) {
if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(value)) { // 验证电话号码
return errorMsg
}
}
}
// 委托来让 Context 拥有执行策略的能力Validator
function Validator() {
// 对应第三条的分析
this.cache = []
}
// 关于参数对照第四条
Validator.prototype.add = function (dom,rule,errorMsg) {
// 规则针对要传入自己额外规则参数的验证 我们规定用':' 分开
// 因此需要对规则在进行拆分
var ary = rule.split(':')
// 对规则进一步的验证发方法存放在this.cache中
this.cache.push(function () {
// 验证规则
var strategy = ary.shift()
// 分析我么的策略中最特殊需要参数的方法function (value, length,errorMsg)
// 经过上一步的操作 ary 中只能有两种情况,一种是空数组,一种是有一个自己定义的验证判断规则参数
ary.unshift(dom.value)
ary.push(errorMsg)
console.log(strategy)
return strategies[strategy].apply(dom,ary)
})
}
// 最后调用策略的方法
Validator.prototype.start = function () {
for (var i=0,validatorFunc;validatorFunc = this.cache[i++];){
var msg = validatorFunc()
if(msg){
return msg
}
}
}
var registerForm = document.getElementById('registerForm')
var validataFun = function () {
var validator = new Validator()
validator.add(registerForm.userName,'isNonEmpty','用户名不能为空')
validator.add(registerForm.password,'minLength:6','密码长度不能少于六位')
validator.add(registerForm.phoneNumber,'isMobile','手机格式不正确')
var errorMsg = validator.start()
return errorMsg
}
registerForm.onsubmit = function (e) {
e.preventDefault()
var errorMsg = validataFun()
if(errorMsg){
alert(errorMsg)
return false // 组织表单提交
}
}
</script>
</html>
- es6 思路
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<label for="userName">用户名</label>
<input type="text" id="userName" name="userName">
<label for="phoneNumber">手机号</label>
<input type="number" id="phoneNumber" name="phoneNumber">
<label for="password">密码</label>
<input type="password" id="password" name="password">
<button id='login'>登录</button>
</body>
<script>
// 策略对象
const strategies = {
isNonEmpty: function (value, errorMsg) { // 不能为空,第一个参数要验证的值,第二个是提示
if(value === ''){
return errorMsg
}
},
minLength:function (value, length,errorMsg) { // 最小长度
if(value.length < length){
return errorMsg
}
},
isMobile:function (value,errorMsg) {
if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(value)) { // 验证电话号码
return errorMsg
}
}
}
// 执行这些策略的调用的类Validator -- 这里直接es6 写法
class Validator{
constructor(strategies){
this.cache = []
this.strategies = strategies // 策略对象
}
// 保存所有验证方法
add(value, rule, errorMsg){
let ary = rule.split(':')
this.cache.push(()=>{
let strategy = ary.shift()
ary.unshift(value)
ary.push(errorMsg)
// 当想给函数传多个参数利用数组的形式的话es5 首先考虑利用apply
// es6 理解结构赋值的方法this.strategies[strategy](...ary)
return this.strategies[strategy].apply(this,ary)
})
}
// 统一执行调用
start(){
let msg = ''
for(let item of this.cache){
msg = item()
if(msg){
return msg
}
}
}
}
let userName = document.getElementById('userName');
let password = document.getElementById('password');
let phoneNumber = document.getElementById('phoneNumber');
// 每次执行调用为了获取最新的输入value值
let validataFun = function () {
// 创建一个'执行这些策略的调用' 的Validator实例
let validator = new Validator(strategies)
validator.add(userName.value,'isNonEmpty','用户名不能为空')
validator.add(password.value,'minLength:6','密码长度不能少于六位')
validator.add(phoneNumber.value,'isMobile','手机格式不正确')
var errorMsg = validator.start()
return errorMsg
}
document.getElementById('login').onclick = ()=>{
let errorMsg = validataFun()
if(errorMsg){
alert(errorMsg)
}
}
</script>
</html>
上面是单个验证 如果多个验证验证代码解决思路
html
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<form action="" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/>
请输入密码:<input type="text" name="passWord"/>
请输入手机号码:<input type="text" name="phoneNumber"/>
<input type="submit" value="提交"/>
</form>
<script>
/***************策略对象*************/
var strategies = {
isNotEmpty:function(value,errorMsg){
if(value === ''){
return errorMsg;
}
},
minLength:function(value,length,errorMsg){
if(value.length<length){
return errorMsg;
}
},
isMobile:function(){
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)){
return errorMsg;
}
}
}
/***************validator类***************/
var Validator = function(){
this.cache = [];
};
Validator.prototype.add = function(dom,rules){
var self = this;
for(var i=0,rule;rule = rules[i++];){
(function(rule){
var strategyAry = rule.strategy.split(':');
var errorMsg = rule.errorMsg;
self.cache.push(function(){
var strategy = strategyAry.shift();
strategyAry.unshift(dom.value);
strategyAry.push(errorMsg);
return strategies[strategy].apply(dom,strategyAry);
})
})(rule);
}
}
Validator.prototype.start = function(){
for(var i=0,validatorFunc;validatorFunc = this.cache[i++];){
var errorMsg = validatorFunc();
if(errorMsg){
return errorMsg;
}
}
}
/************客户调用代码***********/
var registerForm = document.getElementById('registerForm');
var validatorFunc = function(){
var validator = new Validator();
validator.add(registerForm.userName,[{
strategy:'isNotEmpty',
errorMsg:'用户名不能为空'
},{strategy:'minLength:10',
errorMsg:'用户名长度不能小于10位'
}]);
validator.add(registerForm.passWord,[{
strategy:'minLength:6',
errorMsg:'用户密码不能小于6位'
}]);
validator.add(registerForm.phoneNumber,[{
strategy:'isMobile',
errorMsg:'手机号码格式不正确'
}]);
var errorMsg = validator.start();
return errorMsg;
}
registerForm.onsubmit = function(){
var errorMsg = validatorFunc();
if(errorMsg){
alert(errorMsg);
return false;
}
}
</script>
</body>
</html>
es6 写法
html
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<form action="" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/>
请输入密码:<input type="text" name="passWord"/>
请输入手机号码:<input type="text" name="phoneNumber"/>
<input type="submit" value="提交"/>
</form>
<script>
/***************策略对象*************/
const strategies = {
isNotEmpty(value, errorMsg) {
if(value === ''){
return errorMsg;
}
},
minLength(value, length, errorMsg) {
if(value.length < length){
return errorMsg;
}
},
isMobile(value, errorMsg) {
if(!/(^1[3|5|8][0-9]{9}$)/.test(value)){
return errorMsg;
}
}
}
/***************validator类***************/
class Validator {
constructor() {
this.cache = [];
}
add(dom, rules) {
for(let i = 0; i < rules.length; i++) {
let rule = rules[i];
let strategyAry = rule.strategy.split(':');
let errorMsg = rule.errorMsg;
this.cache.push(() => {
let strategy = strategyAry.shift();
strategyAry.unshift(dom.value);
strategyAry.push(errorMsg);
return strategies[strategy].apply(dom, strategyAry);
});
}
}
start() {
for(let i = 0; i < this.cache.length; i++) {
let validatorFunc = this.cache[i];
let errorMsg = validatorFunc();
if(errorMsg){
return errorMsg;
}
}
}
}
/************客户调用代码***********/
const registerForm = document.getElementById('registerForm');
const validatorFunc = () => {
const validator = new Validator();
validator.add(registerForm.userName,[{
strategy:'isNotEmpty',
errorMsg:'用户名不能为空'
},{strategy:'minLength:10',
errorMsg:'用户名长度不能小于10位'
}]);
validator.add(registerForm.passWord,[{
strategy:'minLength:6',
errorMsg:'用户密码不能小于6位'
}]);
validator.add(registerForm.phoneNumber,[{
strategy:'isMobile',
errorMsg:'手机号码格式不正确'
}]);
const errorMsg = validator.start();
return errorMsg;
}
registerForm.onsubmit = () => {
const errorMsg = validatorFunc();
if(errorMsg){
alert(errorMsg);
return false;
}
}
</script>
</body>
</html>