Single responsibility principle

Single Responsibility Principle (SRP): A class does one thing.

If you write the code by yourself, you can write it as you like, as long as you can make sure that there are no bugs when you modify it, and change it fast. For example, for A class A, I make both A conditional and A prompt, such as the following code:

Public static Boolean checkAge(int age){if(age < 18) {ToastTool. ShowShort (" juvenile "); return false; } return true; }Copy the code

Alacrity, at any time as long as a direct checkAge (age), the judge also take prompt, suddenly one day, the product says not to tip in some places, if under 18, directly to jump to another page, how to do, the function of the tip, if you remove prompt, other place also have no need to be prompt, namely: In some places need to hint, in some places do not need to be prompt, now want to get rid of tip don’t need to tip, rewrite the only one without a hint of method, the simplest is: put the inside of the original logic, smoke into a method, is dedicated to the judgment, the original method calls with prompt new method to judge, and then their internal popups.

public static boolean checkAge(int age) { if(! CheckAgeInner (age)) {ToastTool. ShowShort (" minors not allowed to view "); return false; } return true; } public static Boolean checkAgeInner(int age) {if(age < 18) return false; return true; }Copy the code

Although it is only a method of stripping, but embodies the advantages of single responsibility, at the time of requirement refinement, reflect the more obvious the effect of single responsibility, is in short demand is directly proportional to the particle size and the advantage of the single responsibility, we should try to avoid major categories, avoid “multi-functional classes”, because the function of a class, the more the place where it is used to it, the more Will lead to any one part, depend on it, need A module, the module also need B, when will split out A module, had to put the multi-function class also copied, and the multi-function class B module logic, lead to A module is not clean, polluted, so we want to avoid the multi-function class, or try to decentralized, Multifunctional classes are the culprits of coupling and should be avoided at all costs.

Single responsibility is not limited to classes, but also limited to methods, modules, etc., applicable to all things in the universe, the universe, this is a kind of thought, must master.

Richter’s substitution principle

The Liskov Substitution Principle LSP: All places that use base classes must be able to use subclasses transparently.

We know that there are three basic principles of object-oriented programming: encapsulation, inheritance, and polymorphism. Richter’s replacement principle is the embodiment of inheritance, some people say that this is not nonsense, subclass inherits the parent class, use the parent class to replace the place into a subclass must be no problem ah, not necessarily! Let’s look at demo:

public abstract class Car { abstract void run(); } public class extends Car {@override void run() {system.out.println (" the ToyCar doesn't run "); }} public class Bike extends Car {@override void run() {system.out.println (); }}Copy the code

We define an abstract car class, then create two subclasses, a toy car and a bicycle, and then look at the usage scenario:

public void toBeijing(Car car) {
    car.run();
}
Copy the code

We’re going to drive, we can replace Bike, because we can ride a Bike, but can we replace Bike with ToyCar? Obviously not, because ToyCar doesn’t run. It’s obviously destroyed the original logic, or we rewrite the Bike, the run () to do nothing, also destroys the original logic, so obviously can’t use inheritance relationships here, you can switch to combination mode and so on, or expand the parent class, add an interface, such as:

Expand the parent class

Public abstract class Car {} public abstract class EngineCar {abstract void run(); } public class extends Car extends Car{} public class Bike extends Car{@override void run() { System.out.println(" bicycle with legs "); }}Copy the code

Adding an interface is easy. Define a Run interface, which defines a Run () method. Cars that can Run implement the Run interface, and those that can’t. It can be seen that the Principle of Richter’s permutation is the verification of Java inheritance, and the improper inheritance relationship will not meet the Richter’s permutation, so if we are entangled in whether to use inheritance relationship, we can apply Richter’s permutation to see the effect.

Dependency inversion principle

DIP(Dipendence Inversion Principle) interface oriented programming.

The official definition of dependency inversion is obscure: the top should not depend on the bottom; both should depend on abstractions; Abstraction should not depend on details; Details should depend on abstractions. To put it bluntly: programming for interfaces, and more broadly: programming for abstractions. That is, when we use a class, we should try to rely on the interface, when we define a function, we should try to pass the parameters to the interface, we should try to return the value to the interface, and all external class dependencies should be interfaces. Let’s look at an example:

private void loadImage(String url, ImageView imageView) {
    Picasso.get().load(url).resize(50, 50).centerCrop().into(imageView)
}
Copy the code

The code is very simple: load an image into ImageView with Picasso. I believe many students have written this code before. Suppose our project was filled with such code. Direct delete Picasso was dependent on and add in the gradle Glide dependent, and then to find an error to Glide not to go, the change is the feeling something was wrong, why not write a utility class directly, the loading pictures of logic encapsulated in, behind this change just to change the tools, and then write a utility class:

Public class PicassoUtils {public static void loadByUrl(String url, IamgeView imageView){ Picasso.get().load(url).resize(50, Public static void loadByPath(String path, imageView imageView) {} //.... Load from elsewhere}Copy the code

Feel great after change, directly in this class Picasso related Api to Glide can be replaced, cool! After the change, the boss said, could YOU let me choose whether TO use Glide or Picasso, such as Picasso for Huawei mobile phone or Xiaomi Glide? WTF!!!! How to do this, you need to use the interface, define the top-level interface, use the interface in the code where image loading is used, as to how to implement the low-level logic, whether Picasso or Glide, the top-level logic does not need to change:

Interface ImageLoader {// Load void loadByUrl(String URL, ImageView ImageView); Void loadByPath(String path, ImageView, ImageView); / /... Picasso Public class PicassoLoader implements ImageLoader {... Glide public class GlideLoader implements ImageLoader {... } // Create an imageLoader based on your needs, of course you can define a global management class, Imageloader. loadByUrl(url, imageView);Copy the code

From this demo, you can see the benefits of being interface oriented: less coupling, easier code to maintain, easier to expand, and because interfaces are abstract, you can implement them however you want without having to deal with specific logic. And because interfaces are top-level, there are more layers, which are easier to expand and more flexible.

Interface Isolation Principle

Interface Segregation Principle (ISP): Interfaces should be small and have simple functions. In other words, interfaces should be fine-grained.

Interface isolation requires interface functionality to be as simple as possible, and a single responsibility requires a class to do only one thing. What’s the difference?

A single responsibility is a “responsibility”. A responsibility may have multiple functions and may be performed by multiple interfaces. For example, if I want to implement a music player, I define only one interface:

Interface IMusicPlayer {// start void start(); // stop void stop(); // pause void pause(); // restore void resume(); String getSongLength(); }Copy the code

If we now have a SongDisplayer that needs to display the length of the song, then we should also have a getSongLength() function. Should we implement the IMusicPlayer interface directly? The problem is that the interface is not small enough, not clean enough, not pure enough, and clearly violates the principle of interface isolation, so we can split the interface:

Interface IMusicPlayer {// start void start(); // stop void stop(); // pause void pause(); // restore void resume(); . } interface ISongDisplayer {// Get the length of the song String getSongLength(); String getSongName(); . }Copy the code

At this point, we can create a player that can implement both interfaces at the same time, and only implement ISongDisplayer for displaying songs.

In a word: the interface should be as small as possible, as simple as possible, as clean as possible, as far as possible partial branch.

Least knowledge principle

The Least Knowledge Principle (LKP), also known as Demeter’s Principle (LOD), states that an object should know the Least about other objects.

Without further ado, let’s look at demo:

Public class MainActivity {// Define a Button private Button btnOK; protected void onCreate(){ setContentView(R.layout.main); // Initialize and set the click event btnOk = findViewById(r.i.btn_OK); btnOk.setOnClickListenter(v->{ Log.d(TAG,"hello world") }) } }Copy the code

The code runs perfectly, but one day, the product says “Button” is not good, we need to replace it with another control, ok, we directly replace TextView, and then run, directly crashed, why? Since TextView is not a Button, we also need to add:

private Button btnOk;
Copy the code

to

private TextView btnOk;
Copy the code

If there’s only one in the code, that’s fine, if there’s a lot of it, the grumpy one will explode immediately; What if I change it to an ImageView? At this point we not only think: We don’t really care what this thing is, except that it sets the click event, because we only call its setOnClickListener() method, and this method can be set as long as it’s a View. In other words, we only care about what we use. This is the least knowledge rule, the less we know the better. Only care about what you need. So eventually we changed it to:

privagte View btnOk;
Copy the code

After that, it doesn’t matter how you modify the XML file, isn’t it beautiful! Button has a setText() function, but it inherits from TextView. Button has a setText() function, but it inherits from TextView. Not directly related, and Button has other properties that we don’t need, so we don’t care.

So we should only care about what is directly relevant to us, not what we don’t need, so that what happens in areas we don’t care about doesn’t cause us any changes, which greatly improves the robustness of the code.

Open and close principle

OCP(Open Close Principle): A class should be Open for extension and closed for modification.

Open and close principle is the most basic principle, but also the most perfect principle, it is difficult to achieve 100%, he required that any modification is not to change the old code, only add new code, so as not to cause the change of the old logic, making the code more secure.

For example, let’s write a calculator that supports addition and subtraction of short integers. It’s easy. Let’s write it like this:

public class Calculator { public static int calculate(int left, int right, String option) {// add if("+". Equals (option)) return left + right; // subtracting if("-". Equals (option)) return left-right; New IllegalArgumentException(" Unsupported operators "); }}Copy the code

If you want to support a variety of other operations, you need to add an if branch to it, which has many disadvantages:

  • If (1) {if (1) {if (1) {if (1)}}
  • 2 inefficient, too many ifs, for example, there are 100 ifs, the operation I want to calculate is in the last if, it must be inefficient.
  • 3 is not easy to maintain, because they are all in one class and one function, and only one person can modify them at the same time

Our first optimization step is to split the function:

public class Calculator { public static int calculate(int left, int right, String option) {// Add if("+". Equals (option)) return plus(left, right); // subtracting if("-". Equals (option)) return sub(left, right); New IllegalArgumentException(" Unsupported operators "); } public static int plus(int left, int right){return lefft + right; } // add public static int sub(int left, int right){return lefft-right; }}Copy the code

If you need to change something later, you can just add the function, and then add the if branch, but it’s still a hassle. Since you can’t just propose a function, how about you go one step further and propose a class?

// Define the abstract Calculator class public abstract Calculator {// the left operand protected String leftOpt; // protected String rightOpt; // protected String operator; Public void setLeftOpt(String leftOpt) {this.leftOpt = leftOpt; } public void setRightOpt(String rightOpt) {this.rightOpt = rightOpt; } // calculate, provide a template function for subclasses to implement protected abstract int calculate(); Public String getResult(){String result = calculate(); // Clear operand clear(); // return the result retrun result; } public void clear(){leftOpt = null; rightOpt = null; }} public class extends Calculator extends Calculator {public static String OPERATOR = "+"; public PlusCalculator() { super(); this.operator = OPERATOR; @override public String Calculate () {return string.valueof (integer.parseint (leftOpt) + Integer.parseInt(rightOpt)); }} public class SubCalculator extends Calculator {public static String OPERATOR = "-"; public SubCalculator() { super(); this.operator = OPERATOR; @override public String Calculate () {return string.valueof (integer.parseint (leftOpt) - Integer.parseInt(rightOpt)); }}Copy the code

Code is very simple, according to the different operators define different calculator implementation classes, each class is responsible for implementing their own calculation logic, if need to support other operators in the future, add A corresponding class can directly, and support people change at the same time, such as A to do multiplier, B to do the divider, finished after the merge the code. Let’s take a look at the use:

// Add Calculator = new PlusCalculator(); calculator.setLeftOpt("10"); calculator.setRightOpt("20"); calculator.cal(); // calculator = new SubCalculator(); calculator.setLeftOpt("10"); calculator.setRightOpt("20"); calculator.cal();Copy the code

As you can see, I’m just creating the calculator for whatever operation I want to use, so I don’t have to go through the if statement, which is a lot more efficient. This code can also be extended so that by using the factory pattern, we can cache the corresponding calculator to avoid repeated creation:

// Define a Calculator factory that relies directly on the abstract class Calculator, Typical interface oriented programming public Abstract class CalFactory {public Abstract Calculator Create (String operator); Public Class ConcreteFactory extends CalFactory {public class ConcreteFactory extends CalFactory {private static final HashMap<String,  Class<? extends Calculator>> map = new HashMap<>(); Private static final HashMap<String, Calculator> calculatorHashMap = new HashMap<>(); private static final HashMap<String, calculatorHashMap = new HashMap<>(); Static {map.put(PlusCalculator.OPERATOR, pluscalculator.class); static {map.put(PlusCalculator.OPERATOR, plusCalculator.class); map.put(SubCalculator.OPERATOR, SubCalculator.class); } @Override public Calculator create(String operator) { Calculator calculator = null; // Get Calculator = calculatorHashmap.get (operator) from the cache; if (calculator ! = null) return calculator; //create Class<? extends Calculator> aClass = map.get(operator); try { calculator = aClass.newInstance(); // Put cache calculatorHashmap. put(operator, calculator); } catch (Exception e) { e.printStackTrace(); } return calculator; }}Copy the code

Take a look at the use of factory mode:

CalFactory factory = new ConcreteFactory();
String operator = "+";
Calculator calculator = factory.create(operator);
calculator.setLeftOpt("10");
calculator.setRightOpt("20");
String result = calculator.getResult();
Copy the code

It is very simple to use, just pass in the operator, do not directly create a specific calculator in the logical layer, and come with a cache, more efficient.

This chapter talks about six design principles, which is the basis of all design modes. According to these six design principles, 23 design modes are comprehended, and then 23 design modes are summarized to these six design principles. The closed loop is once again, and it is really comprehending through, reaching the tenth level.