Definition of a policy pattern:
A policy pattern defines a series of algorithms and encapsulates each one so that they can be substituted with each other. A policy pattern makes an algorithm independent of the clients that use it.
Scenarios for using the policy pattern:
- Multiple ways of dealing with the same type of problem, only when specific behaviors differ
- When multiple operations of the same type need to be securely encapsulated
- When multiple subclasses of the same abstract class occur and if-else or switch-case is needed to select a specific subclass
Class Diagram:
- Context: Context used to manipulate policies
- Stragety: Abstraction of Policy
- ConcreteStragegyA,ConcreteStragegyB,ConcreteStragegyC: specific policy implementation
Simple implementation:
Usually if there are multiple solutions to a problem, the simpler way is to use if-else or switch-case to choose different solutions according to different scenarios. OK. Let's take the bus in Beijing as an example, refer to the "Android Source Design Mode", first look at the usual writing method
public class PriceCalculator {
//Bus type
private static final int BUS = 1;
//Metro type
private static final int SUBWAY = 2;
public static void main(String[] args) {
PriceCalculator calculator = new PriceCalculator();
System.out.println("Bus fare of 16km:" + calculator.calculatePrice(16, BUS));
}
/**
* Beijing bus, within 10 kilometers of a yuan, after more than 10 kilometers, every dollar can take 5 kilometers
*
* @param km
* @return
*/
private int busPrice(int km) {
//Distance over ten kilometers
int extraTotal = km - 10;
//Distance over 5 km multiple
int extraFactor = extraTotal / 5;
//Excess distance to 5 km
int fraction = extraTotal % 5;
//Price calculation
int price = 1 + extraFactor * 1;
return fraction > 0 ? ++price : price;
}
/**
* 6 3 yuan in kilometers, 4 yuan in 6-12 kilometers, 5 yuan in 12-22 kilometers and 6 yuan in 22-32 kilometers
*
* @param km
* @return
*/
private int subwayPrice(int km) {
if (km <= 6) {
return 3;
} else if (km < 12) {
return 4;
} else if (km < 22) {
return 5;
} else if (km < 32) {
return 6;
}
return 7;
}
private int calculatePrice(int km, int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == SUBWAY) {
return subwayPrice(km);
}
return 0;
}
}
If you want to add an option, you can only add it in else-if
private int calculatePrice(int km, int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == SUBWAY) {
return subwayPrice(km);
}else if(type == TAXT) {
return taxiPrice(km);
}
return 0;
}
Clearly, the PriceCalculator class is not a single responsibility. 1. The responsibility for calculating bus and subway ride prices.2. There is also an if-else form to determine which form of calculation to use.When you add a travel mode, you need to add a method.As shown in the code above.The overall code is messy, so it's time for our hero-strategy model to come on.We can separate each scenario into a function and call the method externally, but this is another form of coupling that is not appropriate for a family of highly variable algorithms.
The policy pattern code refactoring is as follows:
//Computing Interface
public interface CalculateStrategy {
/**
* Calculate price by distance
* @param km
* @return
*/
int calculatePrice(int km);
}
//Bus Price Calculation Strategy
public class BusStrategy implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
//Distance over ten kilometers
int extraTotal = km - 10;
//Distance over 5 km multiple
int extraFactor = extraTotal / 5;
//Excess distance to 5 km
int fraction = extraTotal % 5;
//Price calculation
int price = 1 + extraFactor * 1;
return fraction > 0 ? ++price : price;
}
}
//Metro Price Calculation Strategy
public class SubwayStrategy implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
if (km <= 6) {
return 3;
} else if (km < 12) {
return 4;
} else if (km < 22) {
return 5;
} else if (km < 32) {
return 6;
}
return 7;
}
}
Let's create another class to play the role Context
//Bus Travel Price Calculator
public class TranficCalculator {
public static void main(String[] args){
TranficCalculator calculator = new TranficCalculator();
// //Set Policy Mode
calculator.setStrategy(new BusStrategy());
//Calculate price
System.out.println("Price of 16 kilometers by bus:"+calculator.calculatePrice(16));
//Set Policy Mode
calculator.setStrategy(new TaxiStrategy());
//Calculate price
System.out.println("Taxi fare of 16 kilometers:"+calculator.calculatePrice(16));
}
CalculateStrategy mStrategy;
public void setStrategy(CalculateStrategy mStrategy) {
this.mStrategy = mStrategy;
}
public int calculatePrice(int km){
return mStrategy.calculatePrice(km);
}
}
The code is refactored, the logic is clear, the coupling is reduced, and the scalability is enhanced. Now if you add a taxi,
//Taxi Calculation Strategy
public class TaxiStrategy implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
return 2 * km;
}
}
Summary:
Policy patterns are primarily used to separate algorithms and have different implementation strategies under the same behavioral abstraction.This pattern is a good illustration of the open and close principle, that is, defining abstraction and injecting different implementations for scalability.
Advantage:
- Clear structure, simple and intuitive use
- Low coupling and easy expansion
-
Operations are encapsulated more thoroughly and data is safer
Disadvantages:
- As policy patterns increase, so do subclasses.
- The client must know all the policy classes and decide which one to use
- Communication overhead between Strategy and Context
Now that the strategy mode is here, do you have a new view on it?
This article refers to the book "Analysis and Practice of android Source Design Patterns".