策略模式 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 }
6.适用性
当存在以下情况时使用Strategy模式
1)• 一个系统需要动态地在几种算法中选择一种。
2)• 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
3)• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4)• 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
7.优点
(1)所有这些打折算法完成的都是相同的工作,只是实现不同,策略模式可以以相同的方式调用所有的算法,降低了对象与算法的耦合。
(2)Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
(3)简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
(4)消除了一些if else条件语句 :Strategy模式提供了用条件语句选择所需的行为以外的另一种选择。当不同的行为堆砌在一个类中时 ,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句。含有许多条件语句的代码通常意味着需要使用Strategy模式。
8.总结
1)策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
2)在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。2)
3)策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。
9.策略模式在java容器布局管理中的应用

Container 相当于环境类
LayoutManager 相当于抽象策略类
而具体策略类是LayoutManager的子类,也就是各种具体的布局类,它们封装了不同的布局方式。
1 public class Container extends Component {
2 LayoutManager layoutMgr;
3
4 public void setLayout(LayoutManager mgr) {
5 layoutMgr = mgr;
6 if (valid) {
7 invalidate();
8 }
9 }
10
11
12 }
来源:https://www.cnblogs.com/mengchunchen/p/5723818.html
