Design Patterns in java: Chapter 2 - Strategy Patterns
The second chapter is the implementation of a shopping mall cashiering system using a strategy model that allows for flexible changes in the promotional strategies of the cashiering system.
1. Original code implementation:
package gof;
/*
* 《big talk about design patterns》Chapter 2 Strategy Model
* Realization of shopping mall cash register system,Different promotional strategies can be chosen
*/
import .*;
import .*;
import ;
import ;
public class StrategyPattern {
public static void main(String[] args) {
new Gui();
}
}
// cash register systemUI
class Gui {
private JFrame frame;
private JTextField numberField;
private JTextField priceField;
private JComboBox<String> discountBox;
private JLabel resultLabel;
public Gui() {
frame = new JFrame("商场cash register system");
(400, 300);
(JFrame.EXIT_ON_CLOSE);
(new GridLayout(5, 2));
// Enter the number of products
JLabel numberLabel = new JLabel("Number of products:");
numberField = new JTextField();
(numberLabel);
(numberField);
// Enter the unit price of the product
JLabel priceLabel = new JLabel("Unit price of goods:");
priceField = new JTextField();
(priceLabel);
(priceField);
// Select Discount Method
JLabel discountLabel = new JLabel("Discount Methods:");
discountBox = new JComboBox<>(new String[]{"no discount", "give sb. a 20% discount", "20% off 5"});
(discountLabel);
(discountBox);
// Calculate button
JButton calcButton = new JButton("count");
(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
calculate();
}
});
(calcButton);
// Show results
resultLabel = new JLabel("(grand) total: 0.0");
(resultLabel);
(true);
}
//count函数
private void calculate() {
try {
double number = (());
double price = (());
String discount = (String) ();
//调用count类的count函数
double result=(number, price, discount);
("(grand) total: " + result);
} catch (NumberFormatException e) {
("input error!");
}
}
}
// Commodity price settlement category,The most primitive implementation。
class SimpleCount {
static double getCount(double number, double price, String discount) {
double sum = number * price;
// give a discount
switch (discount) {
case "no discount": {
return sum;
}
case "give sb. a 20% discount": {
return sum * 0.8;
}
case "20% off 5": {
return sum >=20 ? sum - 5 : sum;
}
default:
throw new IllegalArgumentException("Unexpected value: " + discount);
}
}
}
In this original class, every time you change the promotion strategy, you need to modify the calculation class and GUI, which is very inconvenient.
Simple Factory Implementation
To save space, only the calculate() function and other classes that need to be modified are posted
private void calculate() {
try {
double number = (());
double price = (());
String discountString = (String) ();
//Call Discount Factory to initialize the discount class,Call the discount class to get the discounted price
double sum=number*price;
Discount discount=(sum, discountString);
double result=();
("(grand) total: " + result);
} catch (NumberFormatException e) {
("input error!");
}
}
// Abstract Discount Category
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;
}
}
// Discounted factories
class DiscountFactory {
static Discount getDiscount(double sum, String discountString) {
Discount discount;
switch (discountString) {
case "no discount": {
discount = new NoDiscount();
break;
}
case "give sb. a 20% discount": {
discount = new P8Discount();
break;
}
case "20% off 5": {
discount = new Return5Discount();
break;
}
default:
throw new IllegalArgumentException("Unexpected value: " + discountString);
}
// Assigning values to member variables at the end,Avoid writing multiple assignment statements
= sum;
return discount;
}
}
Every time you change the promotion strategy, you still need to modify the calculation class and GUI, which is very inconvenient.
Original Strategy Pattern Implementation
The factory is removed and the instantiation of the computational strategy is done in the computation function:
private void calculate() {
try {
double number = (());
double price = (());
String discountString = (String) ();
Discount discount;
//instantiateddiscount
switch (discountString){
case "no discount": {
discount = new NoDiscount();
break;
}
case "give sb. a 20% discount": {
discount = new P8Discount();
break;
}
case "20% off 5": {
discount = new Return5Discount();
break;
}
default:
throw new IllegalArgumentException("Unexpected value: " + discountString);
}
=number*price;
//Call context to get discounted price
DiscountContext discountContext=new DiscountContext(discount);
double result=();
("(grand) total: " + result);
} catch (NumberFormatException e) {
("input error!");
}
}
A new policy context class has been added:
// Abstract Discount Category
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;
}
}
//Using the Original Strategy Model
class DiscountContext{
private Discount discountClass;
//The specific discount strategy class is passed in at construction time
public DiscountContext(Discount discountSuper) {
=discountSuper;
}
//Call a method of the strategy class to get the value。
public double getDiscount() {
return ();
}
}
This way, if you instantiate the algorithm class on the client side, if you need to modify the algorithm class, you need to modify the instantiation code on the client side. It's still inconvenient.
Strategy Pattern + Simple Factory Implementation
Combine the factory pattern with the strategy pattern context to instantiate the algorithm class in the strategy pattern context.
Modify the calculate function:
private void calculate() {
try {
double number = (());
double price = (());
String discountString = (String) ();
//Call context to get discounted price
DiscountContext discountContext=new DiscountContext(number, price, discountString);
double result=();
("(grand) total: " + result);
} catch (NumberFormatException e) {
("input error!");
}
}
Add instantiation statement to DiscountContxt class:
//Combination of Strategy Pattern and Simple Factory Pattern
class DiscountContext{
private Discount discount;
//Constructors combined with simple factories
public DiscountContext(double number, double price, String discountString) {
switch (discountString) {
case "no discount": {
discount = new NoDiscount();
break;
}
case "give sb. a 20% discount": {
discount = new P8Discount();
break;
}
case "20% off 5": {
discount = new Return5Discount();
break;
}
default:
throw new IllegalArgumentException("Unexpected value: " + discountString);
}
// Assigning values to member variables at the end,Avoid writing multiple assignment statements
= number*price;
}
//Call a method of the strategy class to get the value。
public double getDiscount() {
return ();
}
}
This design pattern is clearer than the above ones. the computational functions of the GUI call the context, and the algorithms are instantiated and computed by the context. This way the GUI depends only on the context class.
The difference with the simple factory is that in the simple factory, the calculation function of the GUI needs to call both the factory class and the algorithm class, while the strategy pattern + simple factory only needs to call the context class, further reducing the coupling.
The full body code is attached below:
package gof;
/*
* 《big talk about design patterns》Chapter 2 Strategy Model
* Realization of shopping mall cash register system,Different promotional strategies can be chosen
*/
import .*;
import .*;
import ;
import ;
public class StrategyPattern {
public static void main(String[] args) {
new Gui();
}
}
// cash register systemUI
class Gui {
private JFrame frame;
private JTextField numberField;
private JTextField priceField;
private JComboBox<String> discountBox;
private JLabel resultLabel;
public Gui() {
frame = new JFrame("商场cash register system");
(400, 300);
(JFrame.EXIT_ON_CLOSE);
(new GridLayout(5, 2));
// Enter the number of products
JLabel numberLabel = new JLabel("Number of products:");
numberField = new JTextField();
(numberLabel);
(numberField);
// Enter the unit price of the product
JLabel priceLabel = new JLabel("Unit price of goods:");
priceField = new JTextField();
(priceLabel);
(priceField);
// Select Discount Method
JLabel discountLabel = new JLabel("Discount Methods:");
discountBox = new JComboBox<>(new String[]{"no discount", "give sb. a 20% discount", "20% off 5"});
(discountLabel);
(discountBox);
// Calculate button
JButton calcButton = new JButton("count");
(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
calculate();
}
});
(calcButton);
// Show results
resultLabel = new JLabel("(grand) total: 0.0");
(resultLabel);
(true);
}
private void calculate() {
try {
double number = (());
double price = (());
String discountString = (String) ();
//Call context to get discounted price
DiscountContext discountContext=new DiscountContext(number, price, discountString);
double result=();
("(grand) total: " + result);
} catch (NumberFormatException e) {
("input error!");
}
}
}
// Abstract Discount Category
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;
}
}
//Combination of Strategy Pattern and Simple Factory Pattern
class DiscountContext{
private Discount discount;
//Constructors combined with simple factories
public DiscountContext(double number, double price, String discountString) {
switch (discountString) {
case "no discount": {
discount = new NoDiscount();
break;
}
case "give sb. a 20% discount": {
discount = new P8Discount();
break;
}
case "20% off 5": {
discount = new Return5Discount();
break;
}
default:
throw new IllegalArgumentException("Unexpected value: " + discountString);
}
// Assigning values to member variables at the end,Avoid writing multiple assignment statements
= number*price;
}
//Call a method of the strategy class to get the value。
public double getDiscount() {
return ();
}
}