策略模式 Strategy(行为型模式)
1.概述
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
2.问题
如何让算法和对象分开来,降低他们之间的耦合度,使得算法可以独立于使用它的客户而变化?
3.解决方案
策略模式:它定义了一系列算法,把每一个算法封装起来,让它们之间可以相互替换,本模式使得算法可独立于使用它的客户而变化。
4.结构
5.例子
商场收银软件:营业员根据顾客所购买商品的单价和数量,计算总价。商场可能会有促销活动,比如全场打8折,全场打5折,买200返100等。
实现方式一:可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的打折算法;当然也可以将这些打折算法封装在一个统一的方法中,通过if…else…或者case等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的打折算法,需要修改封装算法类的源代码。该类代码将较复杂,维护较为困难。
如果需要修改或者新增算法,需要修改原有的类,违反了 开闭原则 ,系统的灵活性和可扩展性差。
算法的复用性较差,无法重用某些算法。
实现方式二:使用策略模式。代码如下:
1 package strategy; 2 /* 3 * 策略类 4 */ 5 public interface Strategy { 6 double Promote(double money); 7 }
1 package strategy; 2 /* 3 * 不使用打折的具体策略类 4 */ 5 public class CashNormalImpl implements Strategy { 6 7 public CashNormalImpl() { 8 9 } 10 11 @Override 12 public double Promote(double money) { 13 14 return money; 15 } 16 17 }
1 package strategy; 2 /* 3 * 使用按比例打折的具体策略类 4 */ 5 public class CashRebateImpl implements Strategy { 6 7 private double moneyRebate ; 8 public CashRebateImpl(double moneyRebate) { 9 this.moneyRebate = moneyRebate ; 10 } 11 12 @Override 13 public double Promote(double money) { 14 15 return money*moneyRebate; 16 } 17 18 }
1 package strategy; 2 /* 3 * 使用返利打折方式的具体策略类 4 */ 5 public class CashReturnImpl implements Strategy { 6 7 private double moneyCondition ; 8 private double moneyReturn ; 9 10 public CashReturnImpl(double moneyCondition , double moneyReturn) { 11 this.moneyCondition = moneyCondition ; 12 this.moneyReturn = moneyReturn ; 13 } 14 15 @Override 16 public double Promote(double money) { 17 double result = 0.0d; 18 if (money >= moneyCondition){ 19 result = money - Math.floor(money/moneyCondition) * moneyReturn ; 20 } 21 return result; 22 } 23 24 }
1 package strategy; 2 /* 3 * Context类 4 */ 5 public class CashContext { 6 7 //Strategy对象的引用 8 private Strategy contreteStrategy = null ; 9 10 public CashContext(String str) { 11 if (str.equals("正常收费")){ 12 contreteStrategy = new CashNormalImpl() ; 13 }else if (str.equals("打八折")){ 14 contreteStrategy = new CashRebateImpl(0.8) ; 15 }else if (str.equals("打五折")){ 16 contreteStrategy = new CashRebateImpl(0.5) ; 17 }else if (str.equals("满200返100")){ 18 contreteStrategy = new CashReturnImpl(200, 100) ; 19 } 20 21 } 22 23 public double getResult(double money){ 24 //当添加具体策略类时,这条代码也不用变,利用的多态的特点 25 return contreteStrategy.Promote(money); 26 } 27 28 }
界面(部分代码):
1 btnOK.addActionListener(new ActionListener() { 2 @Override 3 public void actionPerformed(ActionEvent e) { 4 CashContext context = new CashContext(boxStrategy.getSelectedItem().toString()) ; 5 double price = Double.parseDouble(textPrice.getText()) ; 6 double num = Double.parseDouble(textNum.getText()) ; 7 double total = context.getResult(price*num); 8 sum += total ; 9 textSum.setText(String.valueOf(sum)); 11 textNum.setText(""); 12 textPrice.setText(""); 13 14 } 15 });
界面(完整代码):
1 package strategy; 2 3 import java.awt.Font; 4 import java.awt.event.ActionEvent; 5 import java.awt.event.ActionListener; 6 import java.text.SimpleDateFormat; 7 import java.util.Date; 8 9 import javax.swing.JButton; 10 import javax.swing.JComboBox; 11 import javax.swing.JFrame; 12 import javax.swing.JLabel; 13 import javax.swing.JOptionPane; 14 import javax.swing.JPanel; 15 import javax.swing.JScrollPane; 16 import javax.swing.JTextArea; 17 import javax.swing.JTextField; 18 19 20 21 public class CashFrame extends JFrame { 22 23 private JPanel contentPane = new JPanel() ;//主面板 24 25 26 27 private JLabel labPrice ; //单价提示文本 28 private JTextField textPrice ; //单价设置框 29 private JButton btnOK ; //确定 30 31 private JLabel labNum ; //数量设置提示文本 32 private JTextField textNum ; //数量设置框 33 private JButton btnReset ; //重置 34 35 private JLabel labStrategy ; //策略设置提示文本 36 private JComboBox boxStrategy ; //策略设置框 37 38 private JScrollPane scrollPane ; // 滚动面板 39 private JTextArea textArea ; //信息显示 40 41 private JLabel labSum ; //总价提示文本 42 private JLabel textSum ; //总价设置框 43 private double sum = 0.0D ;//总价 44 45 private static CashFrame instance ; 46 47 private CashFrame(){ 48 init() ; 49 UiUtil.setFrameCenter(this); 50 this.setTitle("商场收银系统") ; 51 this.setResizable(false); 52 this.setVisible(true); 53 } 54 55 public synchronized static CashFrame getInstance(){ 56 if (instance == null){ 57 instance = new CashFrame(); 58 } 59 return instance ; 60 } 61 62 public void init(){ 63 this.setBounds(200,100,450,450); 64 this.setDefaultCloseOperation(EXIT_ON_CLOSE); 65 this.setContentPane(contentPane); 66 67 contentPane.setLayout(null); 68 /*----------------------------*/ 69 labPrice = new JLabel("单价: ") ; 70 labPrice.setBounds(40,20,80,30); 71 contentPane.add(labPrice) ; 72 73 textPrice = new JTextField() ; 74 textPrice.setBounds(90,20,150,30); 75 contentPane.add(textPrice) ; 76 77 btnOK = new JButton("确定") ; 78 btnOK.setBounds(290,20,80,30); 79 contentPane.add(btnOK) ; 80 81 82 /*----------------------------*/ 83 labNum = new JLabel("数量: ") ; 84 labNum.setBounds(40,60,80,30); 85 contentPane.add(labNum) ; 86 87 textNum = new JTextField() ; 88 textNum.setBounds(90,60,150,30); 89 contentPane.add(textNum) ; 90 91 btnReset = new JButton("重置") ; 92 btnReset.setBounds(290,60,80,30); 93 contentPane.add(btnReset) ; 94 95 /*----------------------------*/ 96 labStrategy = new JLabel("促销方式: ") ; 97 labStrategy.setBounds(15,100,80,30); 98 contentPane.add(labStrategy) ; 99 100 boxStrategy = new JComboBox() ; 101 boxStrategy.setBounds(90,100,150,30); 102 contentPane.add(boxStrategy) ; 103 boxStrategy.addItem("正常收费"); 104 boxStrategy.addItem("打八折"); 105 boxStrategy.addItem("打五折"); 106 boxStrategy.addItem("满200返100"); 107 108 /*----------------------------*/ 109 textArea = new JTextArea() ; 110 textArea.setBounds(0,0,400,200); 111 textArea.setEditable(false); 112 textArea.setFont(new Font("宋体", Font.PLAIN, 15)); 113 114 scrollPane = new JScrollPane(textArea) ; 115 scrollPane.setBounds(20,145,400,200); 116 scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 117 scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 118 119 contentPane.add(scrollPane) ; 120 /*----------------------------*/ 121 122 labSum = new JLabel("总价: ") ; 123 labSum.setBounds(40,370,80,30); 124 contentPane.add(labSum) ; 125 126 textSum = new JLabel() ; 127 textSum.setBounds(90,370,150,30); 128 contentPane.add(textSum) ; 129 130 btnOK.addActionListener(new ActionListener() { 131 @Override 132 public void actionPerformed(ActionEvent e) { 133 CashContext context = new CashContext(boxStrategy.getSelectedItem().toString()) ; 134 double price = Double.parseDouble(textPrice.getText()) ; 135 double num = Double.parseDouble(textNum.getText()) ; 136 double total = context.getResult(price*num); 137 sum += total ; 138 showInfo("单价:"+textPrice.getText()+" 数量:"+textNum.getText()+" 方式:"+boxStrategy.getSelectedItem().toString()+" 合计:"+String.valueOf(total)); 139 textSum.setText(String.valueOf(sum)); 140 textNum.setText(""); 141 textPrice.setText(""); 142 143 } 144 }); 145 146 btnReset.addActionListener(new ActionListener() { 147 @Override 148 public void actionPerformed(ActionEvent e) { 149 textNum.setText(""); 150 textPrice.setText(""); 151 textSum.setText(""); 152 textArea.setText(""); 153 154 } 155 }); 156 157 } 158 159 public void showInfo(String info){ 160 textArea.append(info+"\r\n"); 161 textArea.setCaretPosition(textArea.getText().length()) ;//光标定位到最后一行 可以让滚动条保持在最下方 162 } 163 164 public static void main(String[] args) { 165 instance = new CashFrame() ; 166 167 } 168 169 }