Contents of article series (continuously updated) :

Chapter 1: Overview, Coupling, UML, Seven Principles, Detailed Analysis and summary (Based on Java)

Chapter two: Several implementations of singleton pattern And its destruction by reflection

【 Design Pattern 】 Chapter 3: A Simple factory, factory method pattern, abstract factory pattern 】

The Builder model is not that difficult

Chapter 5: What is a Prototype Pattern? Shallow fetch shallow copy and deep copy

Article 6: Look at the adapter pattern

[Design Pattern] Chapter 7: Understand the bridge pattern with me

[Design mode] Chapter 8: Drinking soy milk is decorator mode?

[Design Pattern] Chapter 9: Combination pattern to solve the problem of hierarchical structure

[Design mode] Chapter 10: Appearance mode, the joy of driving a small broken car

[Design mode] Chapter 11: Let’s take a look at the yuan model

[Design Mode] Chapter 12: explanation of agent mode in the ticket purchase scene

After a week of confusion and repeated CRUD for a period of time, I am really bored. Recently, I plan to shelve these articles on technology stack series and open a new chapter “Design Mode”. After all, I have written many articles on “martial arts moves” in the past, so I should improve my internal skills

Overview of design patterns

(a) What is the design mode

Design Patterns are code Design lessons that are used over and over again in software Design. The purpose of using design patterns is to make code reusable, extensible and maintainable

In 1995, GoF (The Gang of Four) published design Patterns: The Foundations of Reusable Object-oriented Software, a collection of 23 design patterns that has since set a milestone in the field of software design patterns.

(2) Why learn design patterns

We’ve looked at a bunch of different techniques, but at the end of the day, it’s just CRUD and call. Maybe the idea or business is perfect and powerful, and there’s a clever use of efficient algorithms, but frankly, it’s just to implement or solve a problem

Sometimes, two people develop the same product at the same time, which both meet the expected requirements. However, the program of A is not only robust, but also convenient to maintain and expand in the later period (this feeling will be increasingly felt in the specific design mode later), while the code of B is A long story

One sentence sums it up very well:

  • The essence of design pattern is the practical application of object-oriented design principles, and it is a full understanding of the encapsulation, inheritance and polymorphism of classes, as well as the association and combination of classes

That is, after all, how to implement maintainable, maintainable code in an object-oriented language like Java, for example, is necessarily to decouple the code and reuse the code properly, and to achieve all of this, you need to make full use of OOP programming features and ideas

Note: The second point below complements the related concept of “coupling”. If there is no need to jump to the third and fourth points [UML class diagrams and relationships between class diagrams] / [Seven Principles of Design Patterns].

In my previous article on Spring dependency Injection, I wrote an easy introduction to the Spring framework step by step (0C and D). I talked about how to achieve decoupling step by step through the factory pattern and the improvement from multiple instances to singletons

Spring Framework Layer by layer easy introduction (IOC and DI) juejin.cn/post/684490…

What is coupling? (High/low)

As a novice can understand the article, the beginning of a pile of IOC AOP and other professional terms thrown out, as if it is not too polite, I have to lay the groundwork for the knowledge to everyone as far as possible to say, if this piece of more understand the big guy, directly skip OK

Coupling is the degree of association between modules. The more the connections between each module are, the stronger the coupling is, the worse the independence will be. Therefore, in software design, we should try to achieve low coupling and high cohesion

Life example: one of lamps in the home, there are a lot of the light bulb above, if the light is broken, you need to replace the whole lamp tape, this is the face of high coupling, for light and light between is closely linked, inseparable, but if the bulb can be taken apart, does not affect the whole lamp tape, so this is called low coupling

Example in code: Look at A polymorphic call, given that B inherits from A, referenced many times

A a = new B();
a.method();
Copy the code

If you want to change B to C, you have to change everything new B() to new C() and that’s high coupling

You can greatly reduce coupling if you use the Spring framework we’ll talk about today

A A = BeanFactory().getBean(B); a.method();Copy the code

At this point, we just need to change the B name to C and change the B in the configuration file to C

Common couplings are classified as follows:

(1) Content coupling

Content coupling occurs when one module directly modifies or manipulates data from another module, or directly transfers to another module. At this point, the module being modified is completely dependent on the module that modified it. This coupling is high and best avoided

public class A {
    public int numA = 1;
}

public class B {
    public static A a = new A();
    public static void method(a){
        a.numA += 1;
    }
    public static void main(String[] args) { method(); System.out.println(a.numA); }}Copy the code

(2) Common coupling

Common coupling is when two or more modules reference a global data item together. A large number of common coupling constructs can make it difficult to determine which module assigned a particular value to a global variable

(3) External coupling

If a group of modules all access the same global simple variable and do not pass the information of the global variable through the parameter list, it is called external coupling. As can be seen from the definition and figure, the difference between public coupling and external coupling is that the former is a global data structure, while the latter is a global simple variable

(4) Control coupling

Control coupling. A module transmits a control signal to another module through an interface, and the module receiving the signal performs appropriate actions according to the signal value. This coupling is called control coupling, that is to say, what is transmitted between modules is not data, but some signs, switching quantities and so on

(5) Mark coupling

Tag coupling refers to the transfer of data structures between two modules, such as array names, record names, file names, etc. These names are tags, but actually transfer the address of the data structure

(6) Data coupling

Modules pass data through parameters, which is called data coupling. Data coupling is the lowest type of coupling, and it is common in systems because the output data of some modules is used as the input data of others in order to perform meaningful functions

(7) Non-direct coupling

There is no direct relationship between the two modules, and the connection between them is completely achieved through the control and invocation of the main module

UML class diagram and the relationship between class diagrams

In a relatively perfect software system, each class has its own responsibility, class and between classes, there are all kinds of relations between classes and interfaces at the same time, UML (unified modeling language) defines a variety of figure from different angles, are very common in software modeling, let’s say a more involved in the design pattern is relatively in the class diagram, because behind a single design patterns, the interpretation of We’re going to touch on that, but it’s kind of a foundation.

(a) class

A class is a set of related attributes and behaviors. It is an abstract concept. In UML, a class is usually represented by a rectangular box divided into three layers

  • Level 1: The class name, which is a string, such as Student

  • Level 2: Class attributes (fields, member variables) in the following format:

    • [Visibility] Attribute name: Type [= default]
    • For example: – name: String
  • Level 3: Class operations (methods, actions) in the following format:

    • [Visibility] Name (parameter list)[: return type]
  • Example: + display():void

(2) Interfaces

Interfaces, a special and common class that cannot be instantiated, define abstract operations (methods) but do not contain attributes. Interfaces are described in UML in three forms:

  • The first is represented by a small circle with a name, with Dog at the top being the interface name and the methods defined by the interface below
  • Second: use a “box” to indicate the class, similar to the class, but with a special label at the top<<interface>>

(3) Relationships

(1) Dependence

Definition: If A change in one element A affects another element B, but the reverse is not true, then elements B and A are said to depend on A

  • For example: the person who opens the door wants to perform the action of opening the door, he must use the key. Here it can be said that the person who opens the door depends on the key. If the key changes, it will affect the person who opens the door, but the change of the person who opens the door will not affect the key to open the door
  • For example, an animal needs oxygen, water, and food to live. This is a literal dependency

Dependency is a kind of temporary association with the lowest degree of coupling between objects

In code, a method of one class performs some responsibility by accessing some method in another (dependent class) through local variables, method arguments, or calls to static methods.

(2) Correlation

An association is a relationship between classes (specifically, instantiated objects), that is, if two objects need to be in a certain relationship for a certain amount of time, it is called an association.

  • For example, a Student learns Knowledge at School. Then there is a certain connection between the three
  • For example, the WildGoose migrates south every year because it knows the laws of climate

The two sides of an association can communicate with each other, that is, “one class knows the other class.”

The association can be two-way or one-way

  • A bidirectional association can be represented by a solid line with two arrows or no arrows

  • A one-way association is represented by a solid line with an arrow pointing from the using class to the associated class

  • You can also annotate role names at both ends of the association line to represent two different roles

Associations are often implemented in code by treating objects of one class as member variables of another class

The diagram below shows a two-way relationship between teacher and student

(3) Aggregation relationship

Aggregation relation is also called aggregation relation, which is a special strong correlation relation. Representing the whole and part relationship between classes (specifically, instantiated objects) is a has-A relationship

  • For example, a Car has a Wheel. Car has a Wheel. This is an aggregation relationship, but a Wheel can also exist independently of a Car

The polymerization relationship can be represented by a solid line arrow with a hollow diamond pointing towards the whole

(4) Combinatorial relations

Composition is a stronger association than aggregation and also represents the relationship between the whole and parts of a class. However, the whole object can control the life cycle of some objects. Once the whole object disappears, the part also disappears naturally, that is, the part cannot exist independently

The aggregation relationship can be represented by a solid line arrow with a solid diamond pointing towards the whole

Generalization describes the relationship between general and special (in the class diagram, “general” is called superclass or parent class, “special” is called subclass), is the relationship between the parent class and subclass, is a kind of inheritance relationship, describes a kind of relationship, in particular, is the relationship between the maximum coupling degree between the generalized relational objects

The extend keyword in Java represents this relationship, usually with abstract classes as parent and concrete classes as subclasses

  • For example: vehicles are abstract superclasses, cars, aircraft, etc. fall into concrete subclasses

The generalization relationship is represented by a solid line with a hollow triangular arrow pointing from the subclass to the parent class

(6) Realize the relationship

An implementation relationship is a relationship between an interface and an implementation class that implements the abstract operations defined in the interface

Implementation relationships are represented by dashed lines with hollow triangular arrows that point from the implementation class to the interface

Four design modes and seven principles

(1) Open and close principle

Definition: Software entities should be open for extension and closed for modification

We are in the development of any products, don’t expect demand is fixed, when you have to change your code, a high quality program reflects its value, it only need to add a few extended on the basis of the original, and not to modify the original code, because it often will be extremely important.

That is to say, the open closed principle requires us to ensure that we can expand the function of a software (module) without modifying the original code module

Let’s talk about it

(1) Close for modification

To turn off changes, that is, to not allow changes to the original module or code.

A: Level of abstraction

For example, to define an interface, what are the differences between different definitions

To define a

boolean connectServer(String ip, int port, String user, String pwd)
Copy the code

Definition 2

boolean connectServer(FTP ftp)
Copy the code
public class FTP{
    private String ip;
    private int port;
    private String user;
    privateString pwd; . Omit get set}Copy the code

Both approaches look similar and work, but if we want to add a new parameter to them

  • With definition 1, once the interface is modified, all the places where the connectServer method is called will have problems
  • If we use definition 2, we just need to modify the FTP entity class and add a property
    • In this case, calls that do not use the new parameter will not be a problem. Even if we need to call this parameter, we can assign it a default value in the constructor of the FTP class

B: Specific level

It is also not a good idea to make changes to the original concrete level of code, although the changes may not be as big as those at the abstract level, or it may happen to be ok, but this problem is sometimes unexpected, and some inadverent changes may bring completely different results than expected

(2) Open to expansion

Open to extensions, that is, we don’t need to modify the original code, because the abstraction layer we defined is reasonable enough and inclusive enough that we can simply extend it by deriving an implementation class as required

(3) How to deal with it during development

No matter how “closed” a module is, there will be changes that cannot be closed. Since complete closure is impossible, the designer must make a choice about what kind of change his module should be closed to. He must first guess the kind of change he is most likely to find, and then construct abstractions to isolate those changes.

Guess program changes in advance, in fact, there is a big difficulty, may not be perfect, or is completely wrong, so in order to avoid this, we can choose to start writing the code, assuming that there won’t be any changes occur, but when change happens, we will take immediate action, through the way of “abstract constraints, encapsulate change”, Create abstractions to isolate the same kind of changes that occur

For example:

For example, if you’re writing an addition program, it’s easy to write, and the change hasn’t happened yet

If this time let you add a subtraction, or, and other functions, you will find, you need to modify on the original class, this is a clear breach “open closed principle”, so in the event of change, we will take immediate action, decided to refactor the code, you first create an abstract class operation, through inheritance, polymorphism and other isolated code, What kind of operations do you want to add in the future, just by adding a new subclass, that is, by making changes to your program with new code instead of changing existing code

Summary:

  • We want to know about possible changes at the beginning of development, because the longer you wait to discover them, the more expensive it becomes to abstract the code

  • Don’t abstract deliberately, rejecting immature abstractions is as important as abstraction itself

(2) Richter’s substitution principle

(1) Detailed description

Definition: Inheritance must ensure that properties held by the superclass are still true in subclasses

Richter’s substitution principle, which mainly explains the content of inheritance, makes clear when to use inheritance, or some provisions for the use of inheritance, is a supplement to the abstraction of the open and closed principle

Let’s talk about the problems of succession:

  • Inheritance is intrusive. If a subclass inherits the parent class, it must have all the attributes and methods of the parent class, reducing code flexibility
  • As the degree of coupling becomes higher, once the attributes and methods of the parent class are changed, you need to consider the changes of the child class, which may result in a lot of code refactoring

The Reefer substitution principle is simply stated: it says that a parent class is truly reused only if it can be replaced by a subclass without compromising the functionality of the program

Its core consists of the following four aspects:

  • ① A subclass can implement abstract methods of its parent class, but cannot override non-abstract methods of its parent class
  • ② Subclasses can add their own unique methods
  • ③ When a method of a subclass overrides a method of the parent class, the preconditions (that is, the method’s input parameters) of the subclass method are looser than the method of the parent class
  • ④ When a method of a subclass implements a method of the parent class (overriding/overloading or implementing an abstract method), the postcondition of the method (that is, the output/return value of the method) is stricter or equal to that of the method of the parent class

Take a look at the simple code and it becomes clear

① A subclass can implement abstract methods of its parent class, but cannot override non-abstract methods of its parent class

If the abstract method of the parent class is not implemented, it will compile an error

In fact, any good method in the parent class is to set a series of norms and default contracts in the whole inheritance system. For example, in Bird, getFlyingSpeed(double Speed) is used to obtain the flight speed of the Bird. However, as a special Bird, It can’t fly, so you need to override the inherited subclass getFlyingSpeed(double Speed) to set speed to zero, but that breaks the inheritance system

While it’s often easy to override superclass methods to do something, a potential inheritance reuse system is disrupted, and errors can be reported if overridden methods are called in the wrong places, or polymorphisms are applied more than once

Let’s look at the following example:

The parent class Father

public class Father {
    public void speaking(String content){
        System.out.println("Parent class:"+ content); }}Copy the code

Subclasses Son

public class Son extends Father {
    @Override
    public void speaking(String content) {
        System.out.println("Subclass:"+ content); }}Copy the code

Subclasses Daughter

public class Daughter extends Father{}Copy the code

The Test class Test

public class Test {
    public static void main(String[] args) {
        // Call the result of the parent class run directly
        Father father = new Father();
        father.speaking("The speaking methods are called");

        // Son subclass replaces the result of the parent class's run
        Son son = new Son();
        son.speaking("The speaking methods are called");

        // Daughter subclass replaces the result of the parent class's run
        Daughter daughter = new Daughter();
        daughter.speaking("The speaking methods are called"); }}Copy the code

Running results:

Parent class: speaking method called Subclass: speaking method called Parent class: speaking method called

② Subclasses can add their own unique methods

This sentence is easy to understand, just look at the code

The parent class Father

public class Father {
    public void speaking(String content){
        System.out.println("Parent class:"+ content); }}Copy the code

Subclasses Son

public class Son extends Father {
    public void playGuitar (a) {
        System.out.println("This is the Son playGuitar method."); }}Copy the code

The Test class Test

public class Test {
    public static void main(String[] args) {
        // Call the result of the parent class run directly
        Father father = new Father();
        father.speaking("The speaking methods are called");

        // Son subclass replaces the result of the parent class's run
        Son son = new Son();
        son.speaking("The speaking methods are called"); son.playGuitar(); }}Copy the code

Running results:

This is the Son playGuitar method

③ When a method of a subclass overrides a method of the parent class, the preconditions (that is, the method’s input parameters) of the subclass method are looser than the method of the parent class

To note here, we said is overloaded, not rewritten, below we according to the magnitude of the requirements of the substitution principle, will the parent class method parameter range set a little bit small (ArrayList), subclass methods with the same parameters to write a few bigger (List), the test result, is will only perform the superclass method, does not perform the parent class reloading after method (note: The parameter name is the same, but the type is different.

The parent class Father

public class Father {
    public void speaking(ArrayList arrayList) {
        System.out.println("Parent class:" + arrayList.get(0)); }}Copy the code

Subclasses Son

public class Son extends Father {
    public void speaking(List list) {
        System.out.println("Subclass:" + list.get(0)); }}Copy the code

The Test class Test

public class Test {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add("The speaking methods are called");

        // Call the result of the parent class run directly
        Father father = new Father();
        father.speaking(arrayList);

        // Son subclass replaces the result of the parent class's run
        Son son = newSon(); son.speaking(arrayList); }}Copy the code

Running results:

The parent: speaking method is called

If we are going to invert the scope of the parent class method parameters set a few bigger, subclass method parameter set, you will find I want to do is overloaded methods, rather than rewriting, but the parent class method was carried out, the logic is completely wrong, so this is also this one, does not meet the substitution principle on the Richter scale

The parent class Father

public class Father {
    public void speaking(List list) {
        System.out.println("Parent class:" + list.get(0)); }}Copy the code

Subclasses Son

public class Son extends Father {
    public void speaking(ArrayList arrayList) {
        System.out.println("Subclass:" + arrayList.get(0)); }}Copy the code

The Test class Test

public class Test {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add("The speaking methods are called");

        // Call the result of the parent class run directly
        Father father = new Father();
        father.speaking(arrayList);

        // Son subclass replaces the result of the parent class's run
        Son son = newSon(); son.speaking(arrayList); }}Copy the code

Running results:

Parent class: speaking method called Subclass: speaking method called

④ When a method of a subclass implements a method of the parent class (overriding/overloading or implementing an abstract method), the postcondition of the method (that is, the output/return value of the method) is stricter or equal to that of the method of the parent class

Define an abstract method in a parent class that returns a value of type List, and override it in a subclass that can return a value of type List or be more precise or strict, such as ArrayList

The parent class Father

public abstract class Father {
    public abstract List speaking(a);
}
Copy the code

Subclasses Son

public class Son extends Father {
    @Override
    public ArrayList speaking(a) {
        ArrayList arrayList = new ArrayList();
        arrayList.add("The speaking methods are called");
        returnarrayList; }}Copy the code

The Test class Test

public class Test {
    public static void main(String[] args) {
        Father father = new Son();
        System.out.println(father.speaking().get(0)); }}Copy the code

Running results:

The speaking methods are called

If, however, the subclass overrides the method by defining the return value as a narrower ArrayList, and the subclass overrides the method as a List instead, the program will be written with an error

(2) Correct the code that violates Richter’s substitution principle

Now there are several classic counter-examples on the Internet, “Kiwi is not a bird”, “whale is not a fish” and so on

Let me say, for example, if we follow the inertia and the literal meaning, if we take the Kiwi as a bird

However, the Kiwi cannot fly, and all other birds can attach a valid value to the setSpeed method, but the Kiwi will have to rewrite the setSpeed method to set flySpeed to 0, which violates the Richter’s substitution principle

If the subclass cannot completely implement the method of the parent class, or the method of the parent class has been “changed” in the subclass, such as the special setSpeed method of Kiwi here, the general choice is to disconnect the inheritance relationship between the parent class and the subclass and redesign the relationship

Such as:

Cancel the inheritance relationship between birds and kiwis and define the more general parent of birds and Kiwis, the animal class

(3) Reliance inversion

Definition:

  • (1) High-level modules should not depend on low-level modules; both should depend on their abstractions

  • ② Abstraction should not depend on detail, but detail should depend on abstraction

For example, our business layer Service (high-level module) does not rely on the data access layer Dao/Mapper (low-level module). We all access through the interface of Mapper. In this case, If the details of the data access layer change, it will not affect the business layer, but if it depends directly on the implementation, it will have a significant impact

The second point, still discussing the problem of abstraction, abstraction is the high level, concrete details is the bottom level, and this is also consistent with the previous point, formally stated a very key principle “programming for interfaces, not for reality programming”.

For example

For example, a Client Client wants to access the readBook method of the school

public class Client {
    public void read(ASchool aSchool){ System.out.println(aSchool.readBook()); }}Copy the code

However, there is a big problem with this. We are relying directly on the concrete rather than the abstract. When we want to view the readBook method of another school, we need to change the code to

public class Client {
    public void read(BSchool bSchool){ System.out.println(bSchool.readBook()); }}Copy the code

However, the open and close principle states that modifications are closed, so it clearly violates the open and close principle, which can be solved if we abstract the code out to interface access

Define the school interface ISchool (I is capitalized I just a naming convention, no special meaning)

public interface ISchool {
    String readBook(a);
}
Copy the code

Schools A and B implement this interface, respectively, and then implement the interface method

public class ASchool implements ISchool {
    @Override
    public String readBook(a) {
        return "Read Ideas for Java Programming."; }}public class BSchool implements ISchool {
    @Override
    public String readBook(a) {
        return "Read The Code Clean Way."; }}Copy the code

Client Specifies the Client class. When invoked, only the interface parameters are passed in

public class Client {
    public void read(ISchool school){ System.out.println(school.readBook()); }}Copy the code

Take a look at the test class

public class Test {
    public static void main(String[] args) {
        Client client = new Client();
        client.read(new ASchool());
        client.read(newBSchool()); }}Copy the code

The results

Read Ideas for Java Programming read Ways to Clean Code

(4) Single responsibility principle

Definition: The single responsibility principle states that a class should have only one cause for its change, or it should be split

A class should not have too much responsibility, otherwise it would have to introduce responsibility B in order to introduce responsibility A in the class, so we have to meet its high cohesion and fine granularity

Advantages:

  • Reduce class complexity. The logic of a class having one responsibility is certainly much simpler than having multiple responsibilities.
  • Improve the readability of classes. As complexity decreases, readability increases.
  • Improve system maintainability. Improved readability makes it easier to maintain.
  • Risk reduction due to change. Change is inevitable, and if the single responsibility principle is followed well, when you modify one function, you can significantly reduce the impact on other functions.

For example, university teachers are responsible for many jobs. However, instructors, lecturers and administrative teachers can all be collectively referred to as teachers. However, it is obviously unreasonable to put a large number of contents and responsibilities into one category

Such as:

Supplement: You may have seen “sheep breathing air, fish breathe water”, here I am not making a presentation, make a note, sometimes, in the case of a kind of simple, can also break single responsibility principle in the code or method level, because even if certain modifications have certain overhead, but almost negligible, but in general, we still need to follow the single responsibility principle

(5) Interface isolation principle

Definition:

  • A client should not be forced to rely on methods it does not use

  • Or — a client should not be forced to rely on methods it does not use

In fact, this principle is the core of the “open”, if there is excessive deposit within an interface method, etc, will be swollen, unexpectedly may refine interface, also is to create a dedicated interface for each class, after all, dependent on multiple private interface, than to rely on a comprehensive interface is more flexible and convenient, at the same time, the interface as a foreign “entrance”, break up, Isolating interfaces reduces the spread of problems caused by external factors

Let’s start with an example:

There’s a good student interface and implementation class, and there’s a teacher’s abstract class and its subclasses, and all the teacher can do is find the good students

Good student IGoodStudent interface

public interface IGoodStudent {
    // Excellent academic performance
    void goodGrades(a);
    // Excellent character
    void goodMoralCharacter(a);
    // Good image
    void goodLooks(a);
}
Copy the code

GoodStudent IGoodStudent interface implementation class GoodStudentImpl

public class GoodStudentImpl implements IGoodStudent {

    private String name;

    public GoodStudentImpl(String name) {
        this.name = name;
    }

    @Override
    public void goodGrades(a) {
        System.out.println("【" +this.name + "] with excellent academic performance.");
    }

    @Override
    public void goodMoralCharacter(a) {
        System.out.println("【" +this.name + "Of good moral character");
    }

    @Override
    public void goodLooks(a) {
        System.out.println("【" +this.name + "The image of] is good."); }}Copy the code

AbstractTeacher abstract class AbstractTeacher

public abstract class AbstractTeacher {
    protected IGoodStudent goodStudent;

    public AbstractTeacher(IGoodStudent goodStudent) {
        this.goodStudent = goodStudent;
    }

    public abstract void findGoodStudent(a);
}
Copy the code

The Teacher class the Teacher

public class Teacher extends AbstractTeacher {
    public Teacher(IGoodStudent goodStudent) {
        super(goodStudent);
    }

    @Override
    public void findGoodStudent(a) {
        super.goodStudent.goodGrades();
        super.goodStudent.goodMoralCharacter();
        super.goodStudent.goodLooks(); }}Copy the code

The Test class Test

public class Test {
    public static void main(String[] args) {
        IGoodStudent goodStudent = new GoodStudentImpl(",");
        AbstractTeacher teacher = newTeacher(goodStudent); teacher.findGoodStudent(); }}Copy the code

Running results:

[A wen] excellent academic performance [A Wen] good moral character [A Wen] good image

Suddenly seems to be no problem, but because each person’s subjective ideology is different, maybe everyone’s definition of “good student” and, just as far as I am concerned, for example, I know as the “self-development, putting to reassure also”, students can learn the life of truth and active learning is more valuable, as to appearance, but more belong to nonsense. The IGoodStudent interface is a bit large and inappropriate for different people with different definitions, so we split the definition of “good student” to some extent according to the interface segregation principle

Student interface for learning

public interface IGoodGradesStudent {
    // Excellent academic performance
    void goodGrades(a);
}
Copy the code

Student interface with excellent moral character

public interface IGoodMoralCharacterStudent {
    // Excellent character
    void goodMoralCharacter(a);
}
Copy the code

Good students implement multiple interfaces

public class GoodStudent implements IGoodGradesStudent.IGoodMoralCharacterStudent {

    private String name;

    public GoodStudent(String name) {
        this.name = name;
    }

    @Override
    public void goodGrades(a) {
        System.out.println("【" +this.name + "] with excellent academic performance.");
    }

    @Override
    public void goodMoralCharacter(a) {
        System.out.println("【" +this.name + "Of good moral character"); }}Copy the code

(6) Demeter’s rule

Definition: If two classes do not have to communicate directly with each other, then they should not interact directly. If one class needs to call a method of the other class, a third party can forward the call

This means that the less a class knows about the classes it depends on, the better. That is, each class should reduce the access of its members, as in the concept of encapsulation, by hiding its fields or behavior details in private

The “friend” in Demeter’s law refers to the current object itself, its member object, the object created by the current object, the method parameters of the current object, etc. These objects are associated, aggregated or combined with the current object, and can directly access the methods of these objects

Note: Please do not use Demeter’s rule too much, because it will produce too many intermediate classes, which will lead to the increase of system complexity and the structure is not clear enough

So let’s use an example

Suppose in an environment of the school, the principal as the highest position in all, certainly not directly involved in the management for teachers and students, but by a layer of a layer of management system for overall planning, the principal, can be understood as a new relationship between teachers and students, and the principal and the middle is a friend of the dean of studies, small, after all, the dean of studies, You can also communicate directly

AcademicDirector

public class AcademicDirector {

    private Principal principal;
    private Teacher teacher;
    private Student student;

    public void setPrincipal(Principal principal) {
        this.principal = principal;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public void meetTeacher(a) {
        System.out.println(teacher.getName() + "Through the Registrar." + principal.getName() + "Report to work");
    }

    public void meetStudents(a) {
        System.out.println(student.getName() + "Through the Registrar and" + principal.getName() + "Meet"); }}Copy the code

The Principal types of Principal

public class Principal {
    private String name;

    Principal(String name) {
        this.name = name;
    }

    public String getName(a) {
        returnname; }}Copy the code

The Teacher class the Teacher

public class Teacher {
    private String name;

    Teacher(String name) {
        this.name = name;
    }

    public String getName(a) {
        returnname; }}Copy the code

The Student class Student

public class Student {
    private String name;

    Student(String name) {
        this.name = name;
    }

    public String getName(a) {
        returnname; }}Copy the code

The Test class Test

public class Test {
    public static void main(String[] args) {
        AcademicDirector a = new AcademicDirector();
        a.setPrincipal(new Principal([President Zhang]));

        a.setTeacher(new Teacher([Miss Wang]));
        a.setStudent(new Student("[Alvin]")); a.meetTeacher(); a.meetStudents(); }}Copy the code

Note: Demeter’s Law is also mentioned in the Programmer’s Way – 26 decoupling and Demeter’s Law

Demeter’s rule of functions tries to minimize coupling between modules in any given program by trying to prevent you from entering an object in order to gain access to a third object’s methods.

By using Demeter’s rule of functions to decouple writing shy code, we can achieve our goal:

Minimize Coupling Between Modules

Minimize coupling between modules

(7) Principle of composite reuse

Definition: in software reuse, we should try to use association relationship such as combination or aggregation to achieve, and then consider using inheritance relationship to achieve

This point is consistent with the purpose of Richter’s substitution principle, which deals with the content about inheritance, and in essence, implements the specific specification of the open and closed principle

Why use composition/aggregation instead of inheritance

  • Inheritance breaks class encapsulation because parent classes are transparent to subclasses, whereas composition/aggregation is not
  • There is more coupling between inherited parent classes than between combined/aggregated old and new classes
  • An implementation inherited from a parent class is static and does not change at run time, whereas composite/aggregate reuse is flexible and can occur dynamically at run time

If your code violates The Richter substitution rule, you can either add a more general abstract superclass, as we mentioned earlier, or you can cancel inheritance and change to a composite/aggregate relationship

So let’s just go back a little bit

  • Inheriting what we normally call the IS-A relationship, where one class Is a kind of another class, for example, a dog Is an animal

  • Combinations/aggregations are both called HAS-A, where a role Has a responsibility or feature

For example, let’s discuss common special bicycles (i.e., variable speed bicycles). First of all, they can be divided into mountain bikes and road bikes according to their types. According to the speed collocation, they can be divided into 21-speed bicycles, 24-speed bicycles and 27-speed bicycles (simple points).

XX mountain/road car itself, though we may be called so verbally, but in fact this Is the speed of this from the relationship – a relationship, and Is – a mixed up, and if by inheritance, will bring a lot of subclasses, once you want to add modify ten-speed bicycle type and velocity type, just need to modify the source code, It violates the open and close principle, so it is changed to combinatorial relation

At the end of five

This is the end of this article, and it is another one with nearly 1W words. There will be some bottlenecks when learning a certain stage. After learning the “internal work” of similar design patterns, I suddenly find that development is not CRUD repeated, and a piece of high-quality code can make people feel more fulfilled. I will update the common design patterns in the future, and summarize them while learning. Thank you for your support.