In this chapter, we’ll focus on making code more readable. Someone once said that the best patterns are those he thinks will be laughed at by others. In the process of learning design patterns by myself, I often feel that I can learn them quickly, but I can’t remember which one TO use in practice. Even if I encounter it, I dare not change it for fear of failure on the line. And then eventually I went back to programming for the business.
The simplest example is ali’s Java code specification, which you’ve probably seen, and most of you have installed, ali’s Java code detection plug-in. One of the plug-ins is that when your function line number exceeds a certain range, it will warn you that the function is too long and needs to be optimized. Many people do not understand why, even the official reason given by Alibaba is that most people can only see N lines of code on a screen of their computer, so we need to increase the number of lines of verification of this function.
In fact, after so many years of my own work, I feel that the above reason is far-fetched. Doesn’t a short function need to be optimized? Take a look at the following function, which is also short but not very readable.
public class CustomList {
private boolean readOnly; Private int size; private int size; Private Object[] elements = new Object[5]; /** * Only read-only mode can add elements, * * @param element */ public void add(Object element) {if the array is long enough, add an element to it.if (!readOnly) {
int newSize = size + 1;
if (newSize > elements.length) {
Object[] newElements = new Object[elements.length + 10];
for(int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; } elements[size++] = element; }}}Copy the code
The first step in optimization:
Public void add(Object element) {// Read-only mode does nothingif (readOnly) {
return;
}
if (atCapacity()) {
Object[] newElements = new Object[elements.length + 10];
for(int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; } elements[size++] = element; } // Whether capacity expansion is required private BooleanatCapacity() {
int newSize = size + 1;
return newSize > elements.length;
}
Copy the code
The second step:
Public void add(Object element) {// Read-only mode does nothingif (readOnly) {
return;
}
if(atCapacity()) { grow(); } addElement(element); } // Whether capacity expansion is required private BooleanatCapacity() {
int newSize = size + 1;
returnnewSize > elements.length; } // Expand capacity private voidgrow() {
Object[] newElements = new Object[elements.length + 10];
for(int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; Private void addElement(element) {element [size++] = element; }Copy the code
Now our Add method is just five lines of code. The readability is a world away from the first edition.
If you have a class with too many public methods and too few private methods, then I think there must be room for optimization.
If we want to keep the simplicity of the system, we should try our best to apply the combination methods mentioned above to reconstruct the details. One by one, complex public methods are reconstituted into simple private methods. Finally, the exposed public methods only exist in the invocation of private methods in the business process.
In many old projects, the biggest problem is that there are too many if else’s in shitmountain code. Many people know that there is a method called strategy mode to solve this problem, but they are afraid to start. Here is an example to see how to start with the if else in Shitmountain code.
Using the PayResult class from this article, now add a method to get a credit for the result of this payment. After all, there are always points to return to you when you buy things in e-commerce.
Public doublegetIntegral() {// UnionPay will return points according to 1.5 actual amount of paymentif (payChannel instanceof BankChannel) {
returnPaymentValue * 1.5; }else if(payChannel instanceof WxChannel) {// wechat pay will pay twice as much as the actual amount, and then subtract the amount of couponreturn paymentValue * 2 - couponValue;
} else if(payChannel instanceof AliPayChannel) {// Alipay payment *2, and then you can add the points paid by Bai Ma Dad niubireturn paymentValue * 2 + loanValue * 1;
}
return 0;
}
Copy the code
Such a code project is certainly not uncommon, let’s now look at how to optimize this code in a mature system without risk. Expand readability and maintainability. After all, many people know how to reduce if else, but in real practice, they often cannot do it, do not know how to do, and dare not do it.
Let’s start with an integration strategy class:
Public class IntegralStrategy {public doublegetIntegral() {
return0; }}Copy the code
And then put our actual integration algorithm logic into this strategy class
Obviously we need some arguments, otherwise the variables we need to refer to in the logic will not be found.
Public class IntegralStrategy {public double getIntegral(PayResult PayResult)if (payResult.getPayChannel() instanceof BankChannel) {
returnPayResult. GetPaymentValue () * 1.5; }else if(payresult.getPayChannel () instanceof WxChannel) {// wechat pay will pay twice as much as the actual amount, and then subtract the amount of couponreturn payResult.getPaymentValue() * 2 - payResult.getCouponValue();
} else if(payresult.getPayChannel () instanceof AliPayChannel) {// Alipay pay *2, and then can also add huayabei pay points Ma Dad nibireturn payResult.getPaymentValue() * 2 + payResult.getLoanValue() * 1;
}
return0; }}Copy the code
Then modify our PayResult main class:
Public doublegetIntegral() {
returnintegralStrategy.getIntegral(this); Private private PayResult(PayChannel PayChannel, Date payDate, Double totalValue); Double paymentValue, Double couponValue, Double loanValue) { this.payChannel = payChannel; this.payDate = payDate; this.totalValue = totalValue; this.paymentValue = paymentValue; this.couponValue = couponValue; this.loanValue = loanValue; // Just add one line to the constructor integralStrategy=new integralStrategy (); }Copy the code
At this point, we’ve done some initial decoupling, but the whole if else logic hasn’t been completely removed, just moved one place, and continued to optimize.
Note here: If you don’t need too many arguments from the main class in your if else logic, then you don’t need to pass a reference to the main class directly, just pass the arguments directly. For the sake of demonstration, we pass the main class as arguments directly.
Passing only parameters without passing references to the main class has the advantage that only minimal coupling between the context class and these policy classes is involved.
So let’s get rid of the if else
Public abstract class IntegralStrategy {abstract double getIntegral(PayResult PayResult); } public class AliPayIntegralStrategy extends IntegralStrategy { @Override double getIntegral(PayResult payResult) {return payResult.getPaymentValue() * 2 + payResult.getLoanValue() * 1;
}
}
public class UnionPayIntegralStrategy extends IntegralStrategy {
@Override
double getIntegral(PayResult payResult) {
returnPayResult. GetPaymentValue () * 1.5; } } public class WxPayIntegralStrategy extends IntegralStrategy { @Override double getIntegral(PayResult payResult) {returnpayResult.getPaymentValue() * 2 - payResult.getCouponValue(); }}Copy the code
Then change our PayResult constructor:
Public static PayResult createUnionPayResult(Date payDate, Double totalValue, Double paymentValue) {public static PayResult createUnionPayResult(Date payDate, Double totalValue, Double paymentValue) {returnNew PayResult(new BankChannel(), payDate, totalValue, paymentValue, 0.0d, 0.0d,new UnionPayIntegralStrategy()); } public static PayResult createWxPayResult(Date payDate, Double totalValue, Double paymentValue, Double paymentValue, Double paymentValue) Double couponValue) {returnNew PayResult(new WxChannel(), payDate, totalValue, paymentValue, couponValue, 0.0d,new WxPayIntegralStrategy()); } public static PayResult createAliPayResult(PayChannel PayChannel, Date payDate, Double totalValue) Double paymentValue, Double couponValue, Double loanValue) {returnNew PayResult(New AliPayChannel(), payDate, totalValue, paymentValue, couponValue, 0.0d, New AliPayIntegralStrategy()); } private PayResult(PayChannel PayChannel, Date payDate, Double totalValue, Double paymentValue, Double couponValue, Double loanValue, IntegralStrategy integralStrategy) { this.payChannel = payChannel; this.payDate = payDate; this.totalValue = totalValue; this.paymentValue = paymentValue; this.couponValue = couponValue; this.loanValue = loanValue; this.integralStrategy = integralStrategy; }Copy the code
At this point, we have completely reconstructed the integral system.