《大话设计模式》java实现:第二章-策略模式

《大话设计模式》java实现:第二章-策略模式

第二章是使用策略模式实现商场收银系统,使收银系统的促销策略可以灵活更改。

1. 原始代码实现:

java 复制代码
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()函数和其它类

java 复制代码
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("输入错误!");
        }
    }
java 复制代码
// 抽象打折类
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,十分不方便。

原始策略模式实现

删去了工厂,在计算函数中进行计算策略的实例化:

java 复制代码
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("输入错误!");
        }
    }

新增了一个策略上下文类:

java 复制代码
// 抽象打折类
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函数:

java 复制代码
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类增加实例化语句:

java 复制代码
//策略模式与简单工厂模式组合
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的计算函数需要调用工厂类和算法类两个类,而策略模式+简单工厂只需要调用上下文类,进一步降低了耦合。

下面附上完全体代码:

java 复制代码
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();
	}
}