概念:策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。
策略模式是一种定义一系列算法的方法,从概念上看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合
做一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费。(商场可能会有打折满减等活动,设计的软件应该易扩展、易维护)
CashSuper.java
package strategy;
public abstract class CashSuper {
public abstract double acceptCash(double money);
}
CashNormal.java
package strategy;
/**
* @ClassName: CashNormal
* @Description: 正常收费子类
*/
public class CashNormal extends CashSuper {
@Override
public double acceptCash(double money) {
return money;
}
}
CashRebate.java
package strategy;
/**
* @ClassName: CashRebate
* @Description: 打折收费子类
*/
public class CashRebate extends CashSuper {
/**
* @Fields moneyRabate : 折率
*/
private double moneyRabate=1;
public CashRebate(double moneyRebate) {
this.moneyRabate=moneyRebate;
}
@Override
public double acceptCash(double money) {
return money*moneyRabate;
}
}
CashReturn.java
package strategy;
/**
* @ClassName: CashReturn
* @Description: 满减收费子类
*/
public class CashReturn extends CashSuper {
/**
* @Fields moneyCondition : 满
*/
private double moneyCondition=0;
/**
* @Fields moneyReturn : 减
*/
private double moneyReturn=0;
public CashReturn(double moneyCondition,double moneyReturn) {
this.moneyCondition=moneyCondition;
this.moneyReturn=moneyReturn;
}
@Override
public double acceptCash(double money) {
double result=money;
if(money>=moneyCondition){
result=money-Math.floor(money/moneyCondition)*moneyReturn;
}
return result;
}
}
CashContext.java
package strategy;
/**
* @ClassName: CashContext
* @Description: 策略类
*/
public class CashContext {
private CashSuper cs;
public CashContext(CashSuper cashSuper) {
this.cs=cashSuper;
}
public double getResult(double money) {
return cs.acceptCash(money);
}
}
MyFrame.java
package strategy;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
@SuppressWarnings("serial")
public class MyFrame extends JFrame{
public static JPanel lowPanel,topPanel,hidePanel,basepPanel;
public static JLabel priceLabel,numLabel,typeLabel,startLabel,endLabel;
// 输入框
public static JTextField priceTextField,numTextField,starTextField,endTextField;
//按钮
public static JButton submitButton,resetButton;
//文本框
public static JTextArea textArea;
public static JScrollPane textScroll;
//下拉框
public static JComboBox jcb;
//监听器
private MyListener listener=new MyListener();
//总金额
public static double totalMoney=0;
public MyFrame() {
//设置文本框字体
Font textFont=new Font("宋体", Font.PLAIN, 20);
//定义顶部组件
topPanel=setTopPanel();
//定义中间显示框
textArea=new JTextArea(20,10);
textScroll=new JScrollPane(textArea);
textArea.setFont(textFont);
textArea.setEditable(false);
//定义底部组件
lowPanel=new LowPanel();
basepPanel=new JPanel();
//定义各组件位置
basepPanel.setLayout(null);
topPanel.setBounds(10, 5, 400, 130);
textScroll.setBounds(10, 140, 380, 120);
lowPanel.setBounds(10, 270, 400, 120);
basepPanel.add(topPanel);
basepPanel.add(textScroll);
basepPanel.add(lowPanel);
this.setLayout(null);
basepPanel.setBounds(0, 0, 420, 400);
this.add(basepPanel);
this.setTitle("商场收银系统") ;
setSize(420, 400);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLocation(350,350);
}
/**
* @Title: setTopPanel
* @Description: 定义顶部组件
* @return
* @return JPanel 返回类型
* @throws
*/
private JPanel setTopPanel() {
JPanel panel=new JPanel();
//重置布局
panel.setLayout(null);
priceLabel=new JLabel("单价:");
numLabel=new JLabel("数量:");
typeLabel=new JLabel("计算方式:");
startLabel=new JLabel("打");
endLabel=new JLabel("折");
priceTextField=new JTextField();
numTextField=new JTextField();
starTextField=new JTextField();
endTextField=new JTextField();
String[] typeStrings={"正常价格","打折","满减"};
jcb=new JComboBox<>(typeStrings);
jcb.addItemListener(listener);
hidePanel=new JPanel();
submitButton=new JButton("确定");
resetButton=new JButton("重置");
submitButton.addActionListener(listener);
resetButton.addActionListener(listener);
//设置各组件的位置
priceLabel.setBounds(5, 5, 100, 30);
priceTextField.setBounds(110, 5, 110, 30);
submitButton.setBounds(270, 5, 100, 30);
numLabel.setBounds(5, 45, 100, 30);
numTextField.setBounds(110, 45, 110, 30);
resetButton.setBounds(270, 45, 100, 30);
typeLabel.setBounds(5, 85, 100, 30);
jcb.setBounds(110, 85, 80, 30);
hidePanel.setBounds(190, 85, 200, 30);
panel.add(priceLabel);
panel.add(priceTextField);
panel.add(submitButton);
panel.add(numLabel);
panel.add(numTextField);
panel.add(resetButton);
panel.add(typeLabel);
panel.add(jcb);
panel.add(hidePanel);
return panel;
}
//内部类,顶部组件
class LowPanel extends JPanel{
Font font=new Font("宋体", Font.PLAIN, 30);
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.black);
g.setFont(font);
g.drawString("总计:"+String.format("%.2f", totalMoney) +"元", 20, 50);
}
}
}
MyListener.java
package strategy;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JOptionPane;
public class MyListener implements ActionListener,ItemListener{
static MyFrame myFrame;
private CashContext cashContext=null;
private double money;
private int count;
private String type;
private String typeString;
private double moneyCondition=0;
private double moneyReturn=0;
private double result=0;
public static void main(String[] args) {
myFrame=new MyFrame();
}
@Override
public void actionPerformed(ActionEvent e) {
//点击确定按钮
if(e.getSource()==MyFrame.submitButton){
try {
money=Double.valueOf(MyFrame.priceTextField.getText());
count=Integer.valueOf(MyFrame.numTextField.getText());
type=(String) MyFrame.jcb.getSelectedItem();
switch (type) {
case "正常价格":
cashContext=new CashContext(new CashNormal());
typeString=type;
break;
case "打折":
moneyCondition=Double.valueOf(MyFrame.starTextField.getText());
cashContext=new CashContext(new CashRebate(moneyCondition/10));
typeString="打"+moneyCondition+"折";
break;
case "满减":
moneyCondition=Double.valueOf(MyFrame.starTextField.getText());
moneyReturn=Double.valueOf(MyFrame.endTextField.getText());
cashContext=new CashContext(new CashReturn(moneyCondition,moneyReturn));
typeString="满"+moneyCondition+"减"+moneyReturn;
break;
default:
break;
}
//获取实际付款金额
result=cashContext.getResult(money*count);
MyFrame.totalMoney+=result;
//显示框输出
String str="单价:"+money+" 数量:"+count+" "+typeString+" 合计:"+result+"\n";
MyFrame.textArea.append(str);
MyFrame.lowPanel.repaint();
} catch (NumberFormatException e1) {
//输入非数字时抛出异常
//弹出错误提示框
JOptionPane.showMessageDialog(myFrame, "请勿输入非数字",
"错误提示", JOptionPane.ERROR_MESSAGE);
}
}else if (e.getSource()==MyFrame.resetButton) {
//点击重置按钮
MyFrame.priceTextField.setText("");
MyFrame.numTextField.setText("");
MyFrame.starTextField.setText("");
MyFrame.endTextField.setText("");
MyFrame.textArea.setText("");
MyFrame.totalMoney=0;
MyFrame.topPanel.remove(MyFrame.hidePanel);
MyFrame.jcb.setSelectedIndex(0);
MyFrame.basepPanel.updateUI();
MyFrame.basepPanel.repaint();
MyFrame.lowPanel.repaint();
}
}
@Override
public void itemStateChanged(ItemEvent e) {
//若下拉框的选择有变化
if(e.getStateChange()==ItemEvent.SELECTED){
if(MyFrame.jcb.getSelectedIndex()==1){
//若是打折
MyFrame.hidePanel.removeAll();
MyFrame.startLabel.setText("打");
MyFrame.endLabel.setText("折");
MyFrame.startLabel.setBounds(30, 0, 20, 30);
MyFrame.starTextField.setBounds(55, 0, 100, 30);
MyFrame.endLabel.setBounds(160, 0, 35, 30);
MyFrame.hidePanel.setLayout(null);
MyFrame.hidePanel.add(MyFrame.startLabel);
MyFrame.hidePanel.add(MyFrame.starTextField);
MyFrame.hidePanel.add(MyFrame.endLabel);
MyFrame.hidePanel.setBounds(190, 85, 200, 30);
MyFrame.topPanel.add(MyFrame.hidePanel);
MyFrame.basepPanel.updateUI();
MyFrame.basepPanel.repaint();
}else if (MyFrame.jcb.getSelectedIndex()==2) {
//若是满减
MyFrame.hidePanel.removeAll();
MyFrame.startLabel.setText("满");
MyFrame.endLabel.setText("减");
MyFrame.startLabel.setBounds(2, 0, 18, 30);
MyFrame.starTextField.setBounds(22, 0, 76, 30);
MyFrame.endLabel.setBounds(100, 0, 20, 30);
MyFrame.endTextField.setBounds(122, 0, 76, 30);
MyFrame.hidePanel.setLayout(null);
MyFrame.hidePanel.add(MyFrame.startLabel);
MyFrame.hidePanel.add(MyFrame.starTextField);
MyFrame.hidePanel.add(MyFrame.endLabel);
MyFrame.hidePanel.add(MyFrame.endTextField);
MyFrame.hidePanel.setBounds(190, 85, 200, 30);
MyFrame.topPanel.add(MyFrame.hidePanel);
MyFrame.basepPanel.updateUI();
MyFrame.basepPanel.repaint();
}else if (MyFrame.jcb.getSelectedIndex()==0) {
MyFrame.topPanel.remove(MyFrame.hidePanel);
MyFrame.basepPanel.updateUI();
MyFrame.basepPanel.repaint();
}
}
}
}
策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。对于打折、满减或其他的算法其实都是对实际商品的一种收费方式,通过继承可以得到它们的公共功能(公共功能就是计算费用的结果getResult()),这使得算法间有了抽象的父类CashSuper。另一个优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。