《大话设计模式》java实现:第二章-策略模式
第二章是使用策略模式实现商场收银系统,使收银系统的促销策略可以灵活更改。
1. 原始代码实现:
package gof; /* * 《大话设计模式》第二章策略模式 * 实现商场收银系统,可以选择不同促销策略 */ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class StrategyPattern { public static void main(String[] args) { new Gui(); } } // 收银系统UI class Gui { private JFrame frame; private JTextField numberField; private JTextField priceField; private JComboBox<String> discountBox; private JLabel resultLabel; public Gui() { frame = new JFrame("商场收银系统"); frame.setSize(400, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new GridLayout(5, 2)); // 输入商品数量 JLabel numberLabel = new JLabel("商品数量:"); numberField = new JTextField(); frame.add(numberLabel); frame.add(numberField); // 输入商品单价 JLabel priceLabel = new JLabel("商品单价:"); priceField = new JTextField(); frame.add(priceLabel); frame.add(priceField); // 选择折扣方式 JLabel discountLabel = new JLabel("折扣方式:"); discountBox = new JComboBox<>(new String[]{"无折扣", "打八折", "满二十减五"}); frame.add(discountLabel); frame.add(discountBox); // 计算按钮 JButton calcButton = new JButton("计算"); calcButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { calculate(); } }); frame.add(calcButton); // 显示结果 resultLabel = new JLabel("总计: 0.0"); frame.add(resultLabel); frame.setVisible(true); } //计算函数 private void calculate() { try { double number = Double.parseDouble(numberField.getText()); double price = Double.parseDouble(priceField.getText()); String discount = (String) discountBox.getSelectedItem(); //调用计算类的计算函数 double result=SimpleCount.getCount(number, price, discount); resultLabel.setText("总计: " + result); } catch (NumberFormatException e) { resultLabel.setText("输入错误!"); } } } // 商品价格结算类,最原始的实现。 class SimpleCount { static double getCount(double number, double price, String discount) { double sum = number * price; // 打折 switch (discount) { case "无折扣": { return sum; } case "打八折": { return sum * 0.8; } case "满二十减五": { return sum >=20 ? sum - 5 : sum; } default: throw new IllegalArgumentException("Unexpected value: " + discount); } } }
在这个原始类中,每次更改促销策略都需要修改计算类和GUI,十分不方便。
简单工厂实现
为了节省篇幅,仅贴出需要修改的calculate()函数和其它类
private void calculate() { try { double number = Double.parseDouble(numberField.getText()); double price = Double.parseDouble(priceField.getText()); String discountString = (String) discountBox.getSelectedItem(); //调用折扣工厂初始化折扣类,调用折扣类获取打折后的价格 double sum=number*price; Discount discount=DiscountFactory.getDiscount(sum, discountString); double result=discount.getDiscount(); resultLabel.setText("总计: " + result); } catch (NumberFormatException e) { resultLabel.setText("输入错误!"); } }
// 抽象打折类 abstract class Discount { double sum; abstract double getDiscount(); } class NoDiscount extends Discount { @Override double getDiscount() { return sum; } } class P8Discount extends Discount { @Override double getDiscount() { return sum * 0.8; } } class Return5Discount extends Discount { @Override double getDiscount() { return sum >= 20 ? sum - 5 : sum; } } // 打折类工厂 class DiscountFactory { static Discount getDiscount(double sum, String discountString) { Discount discount; switch (discountString) { case "无折扣": { discount = new NoDiscount(); break; } case "打八折": { discount = new P8Discount(); break; } case "满二十减五": { discount = new Return5Discount(); break; } default: throw new IllegalArgumentException("Unexpected value: " + discountString); } // 在最后给成员变量赋值,避免写出多个赋值语句 discount.sum = sum; return discount; } }
每次更改促销策略仍然需要修改计算类和GUI,十分不方便。
原始策略模式实现
删去了工厂,在计算函数中进行计算策略的实例化:
private void calculate() { try { double number = Double.parseDouble(numberField.getText()); double price = Double.parseDouble(priceField.getText()); String discountString = (String) discountBox.getSelectedItem(); Discount discount; //实例化discount switch (discountString){ case "无折扣": { discount = new NoDiscount(); break; } case "打八折": { discount = new P8Discount(); break; } case "满二十减五": { discount = new Return5Discount(); break; } default: throw new IllegalArgumentException("Unexpected value: " + discountString); } discount.sum=number*price; //调用上下文获取折后价 DiscountContext discountContext=new DiscountContext(discount); double result=discountContext.getDiscount(); resultLabel.setText("总计: " + result); } catch (NumberFormatException e) { resultLabel.setText("输入错误!"); } }
新增了一个策略上下文类:
// 抽象打折类 abstract class Discount { double sum; abstract double getDiscount(); } class NoDiscount extends Discount { @Override double getDiscount() { return sum; } } class P8Discount extends Discount { @Override double getDiscount() { return sum * 0.8; } } class Return5Discount extends Discount { @Override double getDiscount() { return sum >= 20 ? sum - 5 : sum; } } //使用原始策略模式 class DiscountContext{ private Discount discountClass; //构造时传入具体折扣策略类 public DiscountContext(Discount discountSuper) { this.discountClass=discountSuper; } //调用策略类的方法得到值。 public double getDiscount() { return discountClass.getDiscount(); } }
这样一来,在客户端实例化算法类,如果需要修改算法类,就需要修改客户端的实例化代码。还是很不方便。
策略模式+简单工厂实现
将工厂模式和策略模式上下文结合,在策略模式上下文中实例化算法类。
修改calculate函数:
private void calculate() { try { double number = Double.parseDouble(numberField.getText()); double price = Double.parseDouble(priceField.getText()); String discountString = (String) discountBox.getSelectedItem(); //调用上下文获取折后价 DiscountContext discountContext=new DiscountContext(number, price, discountString); double result=discountContext.getDiscount(); resultLabel.setText("总计: " + result); } catch (NumberFormatException e) { resultLabel.setText("输入错误!"); } }
在DiscountContxt类增加实例化语句:
//策略模式与简单工厂模式组合 class DiscountContext{ private Discount discount; //构造函数与简单工厂结合 public DiscountContext(double number, double price, String discountString) { switch (discountString) { case "无折扣": { discount = new NoDiscount(); break; } case "打八折": { discount = new P8Discount(); break; } case "满二十减五": { discount = new Return5Discount(); break; } default: throw new IllegalArgumentException("Unexpected value: " + discountString); } // 在最后给成员变量赋值,避免写出多个赋值语句 discount.sum = number*price; } //调用策略类的方法得到值。 public double getDiscount() { return discount.getDiscount(); } }
这种设计模式比上面几种更加清楚。GUI的计算函数调用上下文,算法的实例化和计算都由上下文来调用。这样GUI只依赖于上下文类。
与简单工厂的区别就在于简单工厂中,GUI的计算函数需要调用工厂类和算法类两个类,而策略模式+简单工厂只需要调用上下文类,进一步降低了耦合。
下面附上完全体代码:
package gof; /* * 《大话设计模式》第二章策略模式 * 实现商场收银系统,可以选择不同促销策略 */ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class StrategyPattern { public static void main(String[] args) { new Gui(); } } // 收银系统UI class Gui { private JFrame frame; private JTextField numberField; private JTextField priceField; private JComboBox<String> discountBox; private JLabel resultLabel; public Gui() { frame = new JFrame("商场收银系统"); frame.setSize(400, 300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new GridLayout(5, 2)); // 输入商品数量 JLabel numberLabel = new JLabel("商品数量:"); numberField = new JTextField(); frame.add(numberLabel); frame.add(numberField); // 输入商品单价 JLabel priceLabel = new JLabel("商品单价:"); priceField = new JTextField(); frame.add(priceLabel); frame.add(priceField); // 选择折扣方式 JLabel discountLabel = new JLabel("折扣方式:"); discountBox = new JComboBox<>(new String[]{"无折扣", "打八折", "满二十减五"}); frame.add(discountLabel); frame.add(discountBox); // 计算按钮 JButton calcButton = new JButton("计算"); calcButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { calculate(); } }); frame.add(calcButton); // 显示结果 resultLabel = new JLabel("总计: 0.0"); frame.add(resultLabel); frame.setVisible(true); } private void calculate() { try { double number = Double.parseDouble(numberField.getText()); double price = Double.parseDouble(priceField.getText()); String discountString = (String) discountBox.getSelectedItem(); //调用上下文获取折后价 DiscountContext discountContext=new DiscountContext(number, price, discountString); double result=discountContext.getDiscount(); resultLabel.setText("总计: " + result); } catch (NumberFormatException e) { resultLabel.setText("输入错误!"); } } } // 抽象打折类 abstract class Discount { double sum; abstract double getDiscount(); } class NoDiscount extends Discount { @Override double getDiscount() { return sum; } } class P8Discount extends Discount { @Override double getDiscount() { return sum * 0.8; } } class Return5Discount extends Discount { @Override double getDiscount() { return sum >= 20 ? sum - 5 : sum; } } //策略模式与简单工厂模式组合 class DiscountContext{ private Discount discount; //构造函数与简单工厂结合 public DiscountContext(double number, double price, String discountString) { switch (discountString) { case "无折扣": { discount = new NoDiscount(); break; } case "打八折": { discount = new P8Discount(); break; } case "满二十减五": { discount = new Return5Discount(); break; } default: throw new IllegalArgumentException("Unexpected value: " + discountString); } // 在最后给成员变量赋值,避免写出多个赋值语句 discount.sum = number*price; } //调用策略类的方法得到值。 public double getDiscount() { return discount.getDiscount(); } }