Writing in the front
If else nesting like a horizontal pyramid:
if (true) {
if (true) {
if (true) {
if (true) {
if (true) {
if (true) {
}
}
}
}
}
}
Copy the code
I’m not exaggerating. I did! Nesting 6, 7 layers, a function hundreds of lines, Jane! Straight! Look! Death! People!
If else is an indispensable conditional statement in every programming language, and we use it a lot in programming. However, it is generally not recommended to nest more than three layers of if else. If there is too much if else nesting in a piece of code, the readability of the code will decrease rapidly and the maintenance difficulty will be greatly improved. Therefore, we programmers should try to avoid excessive if else nesting. I’ll talk about how I reduce if else nesting in my work.
I heard that more people will see the picture. JPG
The body of the
Before I get to my method, I’ll use an example to illustrate the downside of too much if else nesting.
Imagine a business requirement for simple sharing: support for sharing links, images, text, and graphics, and for sharing results back to the user. When taking over such a business, do you feel very simple, a little brain can start:
Define the share type, the share Bean, and the share callback class:
private static final int TYPE_LINK = 0;
private static final int TYPE_IMAGE = 1;
private static final int TYPE_TEXT = 2;
private static final int TYPE_IMAGE_TEXT = 3;
public class ShareItem {
int type;
String title;
String content;
String imagePath;
String link;
}
public interface ShareListener {
int STATE_SUCC = 0;
int STATE_FAIL = 1;
void onCallback(int state, String msg);
}
Copy the code
Create a share interface for each type:
public void share (ShareItem item, ShareListener listener) { if (item ! = null) {if (item.type == TYPE_LINK) {// Share link if (! TextUtils.isEmpty(item.link) && ! TextUtils.isEmpty(item.title)) { doShareLink(item.link, item.title, item.content, listener); } else { if (listener ! = null) {listener.oncallback (shareListener.state_fail, "Share information incomplete "); }}} else if (item.type == TYPE_IMAGE) { TextUtils.isEmpty(item.imagePath)) { doShareImage(item.imagePath, listener); } else { if (listener ! = null) {listener.oncallback (shareListener.state_fail, "Share information incomplete "); }} else if (item.type == TYPE_TEXT) {// Share text if (! TextUtils.isEmpty(item.content)) { doShareText(item.content, listener); } else { if (listener ! = null) {listener.oncallback (shareListener.state_fail, "Share information incomplete "); }}} else if (item.type == TYPE_IMAGE_TEXT) { TextUtils.isEmpty(item.imagePath) && ! TextUtils.isEmpty(item.content)) { doShareImageAndText(item.imagePath, item.content, listener); } else { if (listener ! = null) {listener.oncallback (shareListener.state_fail, "Share information incomplete "); } } } else { if (listener ! = null) {listener.oncallback (shareListener.state_fail, "unsupported share type "); } } } else { if (listener ! = null) {listener.onCallback(shareListener. STATE_FAIL, "ShareItem cannot be null"); }}}Copy the code
At this point, a simple sharing model is created. Okay? To be honest, if there is no pursuit, there is really no problem, at least the thinking is clear. But a week later? A month later? Or a year from now? The Share method has 15 branches, which means you have to turn your brain into a microprocessor every time you look back at the code, considering 15 scenarios. If there is a bug, you have to consider another 15 cases and test them all. Now if you want to add more sharing videos, you have to add three more branches and change the code so it’s not “open-closed” at all. And if the project is handed off to someone else, and they have to turn their brain into a processor to figure out what each branch does, I’m sure 80 percent of them will make fun of the code.
Our programmer brain power should not be spent on endless branch statements, but on the business itself. So it’s important to avoid writing multi-branch nested statements. Ok, let’s analyze the reason why the above code has multiple branches:
- A null value judgment
- Business judgment
- State judgment
Almost all businesses rely on these judgments, resulting in excessive if else nesting. Is there nothing we can do about it? The answer is definitely not.
I wrote the above code in Java, and null-value judgments are frustrating and exhausting for Java programmers. Each callback checks whether the listener is empty, whether the ShareItem passed in by the user is empty, and whether the ShareItem field is empty……
In this case, my approach is simple: interface layering.
Reduce if else method one: interface layering
The so-called interface layering refers to: the interface is divided into external and internal interfaces, all null value judgment is placed on the external interface, only once processing; The variables passed in by the internal interface are guaranteed not to be null by the external interface, thus reducing null judgment.
Here’s a more intuitive look at the code:
public void share(ShareItem item, ShareListener listener) { if (item == null) { if (listener ! = null) {listener.onCallback(shareListener. STATE_FAIL, "ShareItem cannot be null"); } return; } if (listener == null) { listener = new ShareListener() { @Override public void onCallback(int state, String msg) { Log.i("DEBUG", "ShareListener is null"); }}; } shareImpl(item, listener); } private void shareImpl (ShareItem item, ShareListener Listener) {if (item.type == TYPE_LINK) {// Share link if (! TextUtils.isEmpty(item.link) && ! TextUtils.isEmpty(item.title)) { doShareLink(item.link, item.title, item.content, listener); } else {listener.oncallback (shareListener.state_fail, "Share info incomplete "); }} else if (item.type == TYPE_IMAGE) { TextUtils.isEmpty(item.imagePath)) { doShareImage(item.imagePath, listener); } else {listener.oncallback (shareListener.state_fail, "Share info incomplete "); }} else if (item.type == TYPE_TEXT) {// Share text if (! TextUtils.isEmpty(item.content)) { doShareText(item.content, listener); } else {listener.oncallback (shareListener.state_fail, "Share info incomplete "); }} else if (item.type == TYPE_IMAGE_TEXT) { TextUtils.isEmpty(item.imagePath) && ! TextUtils.isEmpty(item.content)) { doShareImageAndText(item.imagePath, item.content, listener); } else {listener.oncallback (shareListener.state_fail, "Share info incomplete "); }} else {listener.oncallback (shareListener.state_fail, "unsupported share type "); }}Copy the code
Share as you can see, the code above is divided into external interface and internal interface shareImpl, ShareItem are put in the share and ShareListener judgment, then shareImpl is reduced if the else nested, equivalent to the if split the else. This makes the code much more readable and has no more than three layers of nesting.
But as you can see, shareImpl still contains sharing type judgments, i.e. business judgments, and we all know how imaginative the product manager is, and sharing types can change or be added at any time. Well, at this point I’m sure you’re all ready to use polymorphism. Not only can polymorphism cope with business changes, but it can also be used to reduce if else nesting.
Reduce if else method two: polymorphism
With polymorphism, each business is handled separately without making any business judgments at the interface. Abstract ShareItem as the base class, and then subclass it for each business:
public abstract class ShareItem {
int type;
public ShareItem(int type) {
this.type = type;
}
public abstract void doShare(ShareListener listener);
}
public class Link extends ShareItem {
String title;
String content;
String link;
public Link(String link, String title, String content) {
super(TYPE_LINK);
this.link = TextUtils.isEmpty(link) ? link : "default";
this.title = TextUtils.isEmpty(title) ? title : "default";
this.content = TextUtils.isEmpty(content) ? content : "default";
}
@Override
public void doShare(ShareListener listener) {
// do share
}
}
public class Image extends ShareItem {
String imagePath;
public Image(String imagePath) {
super(TYPE_IMAGE);
this.imagePath = TextUtils.isEmpty(imagePath) ? imagePath : "default";
}
@Override
public void doShare(ShareListener listener) {
// do share
}
}
public class Text extends ShareItem {
String content;
public Text(String content) {
super(TYPE_TEXT);
this.content = TextUtils.isEmpty(content) ? content : "default";
}
@Override
public void doShare(ShareListener listener) {
// do share
}
}
public class ImageText extends ShareItem {
String content;
String imagePath;
public ImageText(String imagePath, String content) {
super(TYPE_IMAGE_TEXT);
this.imagePath = TextUtils.isEmpty(imagePath) ? imagePath : "default";
this.content = TextUtils.isEmpty(content) ? content : "default";
}
@Override
public void doShare(ShareListener listener) {
// do share
}
}
Copy the code
(Note: the constructor for each of the subclasses above also handles each field with a null value. If it is null, assign default, so that if the user passes a null value, the problem will be discovered during debugging.)
With polymorphism, the sharing interface is much simpler:
public void share(ShareItem item, ShareListener listener) { if (item == null) { if (listener ! = null) {listener.onCallback(shareListener. STATE_FAIL, "ShareItem cannot be null"); } return; } if (listener == null) { listener = new ShareListener() { @Override public void onCallback(int state, String msg) { Log.i("DEBUG", "ShareListener is null"); }}; } shareImpl(item, listener); } private void shareImpl (ShareItem item, ShareListener listener) { item.doShare(listener); }Copy the code
If this sharing function is a function in your App, not a third party SDK, there is no problem here. However, if a third party shares the FUNCTIONALITY of the SDK, this exposes the user to many more classes (subclasses of each ShareItem, which is equivalent to throwing the if else to the user), and increases the user’s access cost, violating the Demeter principle.
Handling this situation is as simple as wrapping another layer. Reduce the access to subclasses of ShareItem, define a few methods in the main class exposed to the user, and help the user create specific share types internally, so that the user does not need to know the specific class:
public ShareItem createLinkShareItem(String link, String title, String content) { return new Link(link, title, content); } public ShareItem createImageShareItem(String ImagePath) { return new Image(ImagePath); } public ShareItem createTextShareItem(String content) { return new Text(content); } public ShareItem createImageTextShareItem(String ImagePath, String content) { return new ImageText(ImagePath, content); }Copy the code
Or, one might say, the user needs to know a few more methods. Personally, I think it is better to let users know more methods than classes, but the method name can know the intent at a glance, the cost is still quite small, is acceptable.
In fact, in this case, more people think of using the factory model. Well, factory mode solves this problem (it also requires the user to know a few more types), but factory mode inevitably introduces branches, which can be eliminated with maps.
Eliminate if else method three: Use Map instead of branch statements
Cache all shared types in the Map, so you can get the specific type directly, eliminating branching:
private Map<Integer, Class<? extends ShareItem>> map = new HashMap<>(); private void init() { map.put(TYPE_LINK, Link.class); map.put(TYPE_IMAGE, Image.class); map.put(TYPE_TEXT, Text.class); map.put(TYPE_IMAGE_TEXT, ImageText.class); } public ShareItem createShareItem(int type) { try { Class<? extends ShareItem> shareItemClass = map.get(type); return shareItemClass.newInstance(); } catch (Exception e) { return new DefaultShareItem(); Return the default implementation, do not return null}}Copy the code
This way is divided into several methods with the above way each has advantages and disadvantages, see you choose ~
Write in the last
Have you learned anything at this point? To summarize the method of reducing if else:
- The interface is divided into external and internal interfaces, and all null value judgment is done by external interfaces. The variables passed in by the internal interface are guaranteed not to be null by the external interface, thus reducing null judgment.
- The use of polymorphism, eliminate the business judgment, each subclass respectively concerned about their own implementation, and the implementation of subclass creation method, to avoid users to understand too much class.
- Cache the branch state information in advance
Map
In the directget
Get specific values and eliminate branches.
Well, here is the end of the introduction, I hope you can pay attention to the future code, there is to avoid, no is added mian. I hope you write more and more concise code ~