为什么要学习设计模式?
我们之所以要学习设计模式,并不是把具体的某些模式原原本本地照搬来用,而是通过学习这些模式,了解到底什么是“封装变化”,“对象间松耦合”,“针对接口编程”等概念,从而设计出符合前面所讲到四个特性——
易维护,易扩展,易服用,灵活性好
的程序。
1.简介
指对象的某个行为
在不同时间/场景下有不同的实现方式
,即实现的算法。
策略模式是定义一系列算法的方法,从概念上来看,所有这些算法完成的都是
相同的工作
(计算实际应收钱),只是实现的方式有所不同
(即算法不同),它可以用相同的方式调用所有方法,减少各类算法类和使用算法类之间的耦合。
- 特点:定义一组的算法(
业务的实现规则
);封装具体的每个算法(封装变化
);这一族的算法可以互相替代; - 组成: 抽象的策略角色:策略类,接口或者抽象类;具体的策略角色:包装了相关的算法和行为;环境角色:持有策略类的引用,供客户端调用;
- 优点:将公共代码转移到父类中,避免了代码的重复;动态替换继承关系,使得动态替换算法可实现;便于单元测试;
- 缺点:使用者必须知道每个具体的策略角色所使用的算法;很多策略类的具体实现,代码臃肿;
UML类图结构为:(引自百度百科)
2.使用场景
- 多个类只区别在行为方式不同
- 需要在不同情况下使用不同策略(算法)来实现,将来可能用其他的方法来实现
- 对客户端隐藏具体实现,完全隔离开
比如:商场收费系统,旅游的出行方式的选择等都可以使用策略模式来实现。
3.案例(商场收费系统)
具体功能:输入单价以及数量,按照正常情况/打折情况/满返情况计算实收费用。
分析:完成的共同方法都是计算最后实际收款额,不同的情况看成不同的业务规则(算法);
抽象的策略角色:提供抽象计算方法(业务规则)
package com.example.administrator.strategy.strategymode;
/**
* 创建时间: 2019/1/25 13:36
* 描述: 抽象的策略角色:可以是接口可以是抽象类
* 策略模式中的策略类 Strategy
* 定义所有支持算法的公共接口
*/
@SuppressWarnings("unused")
public abstract class CashSuper {
/**
* @param money 收取现金
* @return 返回应收金额
*/
public abstract double acceptCash(String money);
}
具体的策略角色:不同情况的计算方式封装成类
正常情况:
package com.example.administrator.strategy.strategymode;
import java.math.BigDecimal;
/**
* 创建时间: 2019/1/25 13:38
* 描述: 具体的某个优惠方式,即算法的具体实现
* 具体的策略角色
*/
@SuppressWarnings("unused")
public class CashNormal extends CashSuper {
/**
* @param money 收取现金
* @return 返回最终的金额
*/
@Override
public double acceptCash(String money) {
BigDecimal value = new BigDecimal(money);
return value.doubleValue();
}
}
打折情况:
package com.example.administrator.strategy.strategymode;
import java.math.BigDecimal;
/**
* 创建时间: 2019/1/25 13:40
* 描述: 打折算法的具体实现类
* 具体的策略角色
*/
@SuppressWarnings("unused")
public class CashRebate extends CashSuper {
private double rebate;
/**
* @param rebateRate 打折的程度
*/
public CashRebate(double rebateRate) {
this.rebate = rebateRate;
}
/**
* @param money 收取现金
* @return 返回打折后应收
*/
@Override
public double acceptCash(String money) {
BigDecimal value = new BigDecimal(money);
return value.multiply(new BigDecimal(String.valueOf(rebate))).doubleValue();
}
}
满返情况:
package com.example.administrator.strategy.strategymode;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 创建时间: 2019/1/25 13:44
* 描述: 满返回的具体算法实现
* 具体的策略角色
*/
@SuppressWarnings("unused")
public class CashReturn extends CashSuper {
private double moneyCondition;
private double moneyReturn;
/**
* 构造
*
* @param moneyCondition 满多少金额
* @param moneyReturn 返多少金额
*/
public CashReturn(double moneyCondition, double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double acceptCash(String money) {
BigDecimal value = new BigDecimal(money);
BigDecimal bgCondition = new BigDecimal(String.valueOf(this.moneyCondition));
//判断金额是否大于300
if (value.compareTo(bgCondition) >= 0) {
return value.subtract(value.divide(bgCondition, 2, RoundingMode.HALF_UP)
.multiply(new BigDecimal(String.valueOf(this.moneyReturn)))).doubleValue();
} else {
return value.doubleValue();
}
}
}
环境角色:持有策略类的引用,供客户端使用
package com.example.administrator.strategy.strategymode;
import com.example.administrator.strategy.R;
import java.math.BigDecimal;
/**
* 创建时间: 2019/1/25 13:51
* 描述: 环境角色:供客户端调用,内部持有策略类的引用
* Context层: 添加简单的工厂模式
* 提供策略的实现
*/
@SuppressWarnings("unused")
public class CashContext {
private CashSuper cashSuper;
/**
* 根据传入的resID创建对应的收费策略
*
* @param redId 选择ID
*/
public CashContext(int redId) {
switch (redId) {
//正常
case R.id.rb_normal:
cashSuper = new CashNormal();
break;
//8折
case R.id.rb_20_off:
cashSuper = new CashRebate(0.8d);
break;
//6折
case R.id.rb_40_off:
cashSuper = new CashRebate(0.6d);
break;
//满300-100
case R.id.rb_over_300_minus_100:
cashSuper = new CashReturn(300d, 100d);
break;
}
}
/**
* 暴露方法
*
* @param price 单价
* @param count 数量
* @return 实际收的钱
*/
public double getResult(String price, String count) {
BigDecimal p = new BigDecimal(price);
BigDecimal c = new BigDecimal(count);
return cashSuper.acceptCash(p.multiply(c).toString());
}
}
客户端使用: 持有环境角色的引用,根据优惠方式进行调用。
package com.example.administrator.strategy;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.example.administrator.strategy.strategymode.CashContext;
public class MainActivity extends AppCompatActivity {
private Button btnCaculate;
private EditText etPrice;
private EditText etCount;
private TextView tvResult;
private RadioGroup rgGroup;
private int resId = -1;
private CashContext cashContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rgGroup = findViewById(R.id.rg);
btnCaculate = findViewById(R.id.btn_cac);
etPrice = findViewById(R.id.et_price);
etCount = findViewById(R.id.et_count);
tvResult = findViewById(R.id.tv_result);
resId = R.id.rb_normal;
rgGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
resId = checkedId;
}
});
btnCaculate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String price = etPrice.getText().toString();
String count = etCount.getText().toString();
if (!TextUtils.isEmpty(price) && !TextUtils.isEmpty(count)) {
if ((!price.contains(".") && price.substring(0, 1).equals("0")) ||
(!count.contains(".") && count.substring(0, 1).equals("0")))
Toast.makeText(MainActivity.this, "输入单价或者数量有问题", Toast.LENGTH_SHORT).show();
else {
cashContext = new CashContext(resId);
tvResult.setText("应收:"+String.valueOf(cashContext.getResult(price, count)));
}
}
}
});
}
}
**
参考自:《大话设计模式》
**