设计模式(1)---策略模式

策略模式 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 }
View Code

 

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 }

 

posted @ 2016-07-31 20:23  __Meng  阅读(353)  评论(0编辑  收藏  举报