策略模式 Strategy(行爲型模式)java
1.概述算法
在軟件構建過程當中,某些對象使用的算法可能多種多樣,常常改變,若是將這些算法都編碼到對象中,將會使對象變得異常複雜;並且有時候支持不使用的算法也是一個性能負擔。設計模式
2.問題數據結構
如何讓算法和對象分開來,下降他們之間的耦合度,使得算法能夠獨立於使用它的客戶而變化?app
3.解決方案ide
策略模式:它定義了一系列算法,把每個算法封裝起來,讓它們之間能夠相互替換,本模式使得算法可獨立於使用它的客戶而變化。佈局
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 }