Overview of Design patterns

What are design patterns?

Definition: In software engineering, design pattern refers to the proposed solutions to various common (recurring) problems in software design.

In other words, design patterns are templates for problem solving, good solutions to different problems that previous bosses have encountered and solved in good ways. When we encounter these problems again, we can directly borrow from the leaders of the good solution, no longer have to think about it. Over time, these solutions were grouped together into a set of “23 common design patterns” that solved the problem well while improving the readability of the code.

The purpose of the design pattern

In the process of writing software, programmers are faced with challenges from coupling, cohesion, maintainability, extensibility, reuse, flexibility and other aspects. Design patterns are designed to make programs (software) have better:

  1. Code reuse

    The same function of the code, do not write many times, to avoid Ctrl+CV engineering

  2. readability

    Programming standardization, easy for other programmers to read and understand, for example, you use Factory in the name of the class, others naturally think of Factory mode

  3. scalability

    This is very convenient when new functionality needs to be added, also known as maintainable, which means that as functionality expands, the system does not become highly coupled and crumpled

  4. reliability

    When we add new features, it has no impact on the original features

  5. The program presents high cohesion, low coupling characteristics

Seven principles of design patterns

Design pattern principles are the principles that programmers should follow when programming, and also the principles for designing various “design patterns” (i.e. the basis for why design patterns are designed the way they are).

The seven principles commonly used in design patterns are:

  1. Single responsibility principle

  2. Interface Isolation Principle

  3. Rely on the inversion principle

  4. Richter’s substitution principle

  5. The open closed principle

  6. Demeter’s rule

  7. Principle of composite reuse

1. Principle of single responsibility

1.1 Basic Introduction

For classes, a class should only be responsible for one responsibility, such as the UserDao class, which is only responsible for the CRUD of the User table.

For example, class A is responsible for two different responsibilities: Responsibility 1, responsibility 2. When the requirements of responsibility 1 change and A changes, it may cause errors in the execution of responsibility 2. Therefore, it is necessary to decompose the granularity of class A into A1 and A2, that is, to decompose class A into A1 and A2. For example, one CommonDao is responsible for the CRUD of the user and order tables. This does not comply with the single responsibility principle. We should divide CommonDao into UserDao and OrderDao.

1.2 Application Examples

Mode 1: violate the single responsibility principle

public class SingleResponsibility1 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Vehicle vehicle = new Vehicle();
		vehicle.run("Motorcycle");
		vehicle.run("Car");
		vehicle.run("Plane"); }}// Transportation class
1 / / way
// 1. In the run method of approach 1, the single responsibility principle is violated
// 2. The solution is very simple. It can be divided into different classes according to different modes of transportation
class Vehicle {
	public void run(String vehicle) {
		System.out.println(vehicle + "Running on the highway...."); }}Copy the code

Results: It is found that all vehicles are “running on the road”, and the run method of a Vehicle class manages both running on the ground and flying in the sky, which violates the principle of single responsibility.

Method two: Decompose the class

Split a Vehicle class into RoadVehicle, AirVehicle, and WaterVehicle.

// Analysis of option 2
//1. Observe the principle of single responsibility
//2. However, this is a big change, which is to decompose the class and modify the client
//3. Improvement: Modify the Vehicle class directly with less code changes => Scheme 3
public class SingleResponsibility2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		RoadVehicle roadVehicle = new RoadVehicle();
		roadVehicle.run("Motorcycle");
		roadVehicle.run("Car");
		
		AirVehicle airVehicle = new AirVehicle();
		
		airVehicle.run("Plane"); }}class RoadVehicle {
	public void run(String vehicle) {
		System.out.println(vehicle + "Highway operation"); }}class AirVehicle {
	public void run(String vehicle) {
		System.out.println(vehicle + "Sky Running"); }}class WaterVehicle {
	public void run(String vehicle) {
		System.out.println(vehicle + "Water running"); }}Copy the code

Although this scheme conforms to the principle of single responsibility, it has been modified so much that only the method can be modified.

Plan three: Break the method down

// Analysis of mode 3
//1. This modification method does not make major changes to the original class, but only adds methods
//2. While the single responsibility principle is not observed at the class level, it is still observed at the method level
public class SingleResponsibility3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Vehicle2 vehicle2  = new Vehicle2();
		vehicle2.run("Car");
		vehicle2.runWater("Ship");
		vehicle2.runAir("Plane"); }}class Vehicle2 {
	public void run(String vehicle) {
		/ / processing
		
		System.out.println(vehicle + "Running on the highway....");
		
	}
	
	public void runAir(String vehicle) {
		System.out.println(vehicle + "Run in the sky....");
	}
	
	public void runWater(String vehicle) {
		System.out.println(vehicle + "Walking in the water...."); }}Copy the code

1.3 Summary of single responsibility principle

  • Reduce the complexity of classes so that each class has only one responsibility.

  • Improve readability and maintainability of classes

  • Reduce the risk of change

  • In general, we should adhere to the single responsibility principle, which can only be violated at the code level if the logic is simple enough; Only the number of methods in a class is small enough to maintain the single responsibility principle at the method level

2. Interface isolation principle

2.1 Basic Introduction

Interface isolation principle: a client should not rely on interfaces it does not need, that is, the dependency of one class on another should be based on the smallest interface.

Example: Look at the picture

The isolation principle works like this: Split interface Interface1 into separate interfaces (here we split it into three interfaces), and classes A and C have dependencies on the interfaces they need, respectively. That is, the interface isolation principle is adopted

If you don’t understand it, look directly at the following code ↓

2.2 Application Examples

A:

public class Segregation1 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        A a = new A();
		a.depend1(new B()); // Class A relies on class B through interfaces
		a.depend2(new B());
		a.depend3(new B());

		C c = new C();
		c.depend1(new D()); // Class C relies on class D through interfaces
		c.depend4(new D());
		c.depend5(newD()); }}/ / interface
interface Interface1 {
	void operation1(a);
	void operation2(a);
	void operation3(a);
	void operation4(a);
	void operation5(a);
}

// Class B must implement all methods in the interface
class B implements Interface1 {
	public void operation1(a) {
		System.out.println("B implements Operation1");
	}
	public void operation2(a) {
		System.out.println("B implements operation2");
	}
	public void operation3(a) {
		System.out.println("B implements OPERATION3");
	}
	public void operation4(a) {
		System.out.println("B implements Operation4");
	}
	public void operation5(a) {
		System.out.println("B implements Operation5"); }}// Class D must also implement all methods in the interface
class D implements Interface1 {
	public void operation1(a) {
		System.out.println("D implements OPERATION1");
	}
	public void operation2(a) {
		System.out.println("D implements OPERATION2");
	}
	public void operation3(a) {
		System.out.println("D implements OPERATION3");
	}
	public void operation4(a) {
		System.out.println("D implements OPERATION4");
	}
	public void operation5(a) {
		System.out.println("D implements OPERATION5"); }}class A { // However, class A relies on class B through the interface Interface1, but only the 1,2, and 3 methods are used, making method 45 redundant
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend2(Interface1 i) {
		i.operation2();
	}
	public void depend3(Interface1 i) { i.operation3(); }}class C { // class C relies on class D through the interface Interface1, but only uses methods 1,4,5, making method 23 redundant
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend4(Interface1 i) {
		i.operation4();
	}
	public void depend5(Interface1 i) { i.operation5(); }}Copy the code

Method two: improvement

  • Class A depends on class B through interface Interface1, and class C depends on class D through interface Interface1. If interface Interface1 is not the minimum interface for classes A and C, then classes B and D must implement methods they do not need

  • Split interface Interface1 into separate interfaces, with classes A and C dependent on the interfaces they need. That is, the interface isolation principle is adopted

  • Methods in interface Interface1 are split into three interfaces based on the actual situation

public class Segregation1 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// Use a handful
		A a = new A();
		a.depend1(new B()); // Class A relies on class B through interfaces
		a.depend2(new B());
		a.depend3(new B());

		C c = new C();
		c.depend1(new D()); // Class C relies on class D through interfaces
		c.depend4(new D());
		c.depend5(newD()); }}1 / / interface
interface Interface1 {
	void operation1(a);
}
2 / / interface
interface Interface2 {
	void operation2(a);
	void operation3(a);
}
3 / / interface
interface Interface3 {
	void operation4(a);
	void operation5(a);
}

class B implements Interface1.Interface2 {
	public void operation1(a) {
		System.out.println("B implements Operation1");
	}
	public void operation2(a) {
		System.out.println("B implements operation2");
	}
	public void operation3(a) {
		System.out.println("B implements OPERATION3"); }}class D implements Interface1.Interface3 {
	public void operation1(a) {
		System.out.println("D implements OPERATION1");
	}
	public void operation4(a) {
		System.out.println("D implements OPERATION4");
	}
	public void operation5(a) {
		System.out.println("D implements OPERATION5"); }}class A { // Class A relies on class B through the interface Interface1, which uses only the 1,2, and 3 methods
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend2(Interface2 i) {
		i.operation2();
	}
	public void depend3(Interface2 i) { i.operation3(); }}class C { // class C uses the interface Interface1, which relies on class D, but only uses the 1,4,5 methods
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend4(Interface3 i) {
		i.operation4();
	}
	public void depend5(Interface3 i) { i.operation5(); }}Copy the code

3. Dependency inversion principle

3.1 Basic Introduction

Dependence Inversion Principle refers to:

  1. A high-level module should not depend on a low-level module; both should depend on its abstraction

  2. Abstractions should not depend on details, details should depend on abstractions

  3. The central idea of dependency inversion is interface oriented programming

  4. The dependency inversion principle is based on the design idea that abstract things are more stable than details. An architecture based on abstraction is much more stable than one based on detail. In Java, abstraction refers to an interface or abstract class, and details are concrete implementation classes

  5. The purpose of using interfaces or abstract classes is to create specifications that do not involve any concrete operations, leaving the task of presenting the details to their implementation classes

(Dependence Inversion Principle and Dependency Injection Dependency Injection are different ideas.)

3.2 Application Examples

A:

public class DependecyInversion {
	public static void main(String[] args) {
		Person person = new Person();
		person.receive(newEmail()); }}class Email {
	public String getInfo(a) {
		return "Email message: Hello,world"; }}// Complete the function for Person to receive messages
// Mode 1 analysis
//1
//2. If the object we obtain is wechat, SMS, etc., we need to add new classes, and Perons also need to add corresponding receiving methods, which has low scalability
//3. 解决 方 法 : introduce an abstract interface IReceiver, which represents the receiver, so that the Person class depends on the interface IReceiver.
// Since Email, WeiXin, etc., belong to the receiver scope, it is ok to implement the IReceiver interface respectively, so we rely on the inversion principle
class Person {
	public void receive(Email email ) { System.out.println(email.getInfo()); }}Copy the code

Method two: use dependency inversion

public class DependecyInversion {
	public static void main(String[] args) {
		// The client does not need to change
		Person person = new Person();
		person.receive(new Email());
		person.receive(newWeiXin()); }}// Define the interface
interface IReceiver {
	public String getInfo(a);
}

class Email implements IReceiver {
	public String getInfo(a) {
		return "Email message: Hello,world"; }}// Add wechat
class WeiXin implements IReceiver {
	public String getInfo(a) {
		return "Wechat message: Hello, OK"; }}2 / / way
class Person {
	// Here we are relying on the interface
	public void receive(IReceiver receiver ) { System.out.println(receiver.getInfo()); }}Copy the code

4. Richter’s substitution principle

Richter’s substitution helps us define how to use inheritance correctly, right

4.1 Basic Introduction

  1. If for every object o1 of type T1 there is an object O2 of type T2, such that the behavior of all programs P defined by T1 does not change when all objects o1 are substituted for O2, then type T2 is a subtype of type T1. In other words, all references to a base class must be able to transparently use objects from its subclasses.
  2. When using inheritance, follow the Richter’s substitution principle and try not to override methods of the parent class in subclasses
  3. Richter’s substitution principle tells us that inheritance actually makes two classes more coupled, and can be solved by aggregation, composition, and dependency, where appropriate.

4.2 Application Examples

public class Liskov {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		A a = new A();
		System.out.println("11-3 =" + a.func1(11.3));
		System.out.println("1-8 =" + a.func1(1.8));

		System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
		B b = new B();
		System.out.println("11-3 =" + b.func1(11.3));// The original intention here is to solve for 11-3
		System.out.println("1-8 =" + b.func1(1.8));/ / 1-8
		System.out.println("11 + 3 + 9 =" + b.func2(11.3)); }}/ / A class
class A {
	// Return the difference between two numbers
	public int func1(int num1, int num2) {
		returnnum1 - num2; }}// Class B inherits class A
// Added a new function: add two numbers and then add them to 9
class B extends A {
	// Here, the class A method is overridden, probably unconsciously
	public int func1(int a, int b) {
		return a + b;
	}

	public int func2(int a, int b) {
		return func1(a, b) + 9; }}Copy the code

We found an error in the subtraction function that worked well. The reason is that class B inadvertently overrides the methods of its parent class, causing the original functionality to fail. In practical programming, we often override the methods of the parent class to accomplish the new function, which is simple to write, but the overall inheritance system will be less reusable. Especially if you’re running a lot of polymorphism

The solution is: the original parent class and child class inherit a more popular base class, the original inheritance relationship is removed, replaced by dependencies, aggregation, composition, etc.

package com.atguigu.principle.liskov.improve;

public class Liskov {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		A a = new A();
		System.out.println("11-3 =" + a.func1(11.3));
		System.out.println("1-8 =" + a.func1(1.8));
        
		System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
		B b = new B();
		// Because class B no longer inherits from class A, func1 is no longer subtracted by the caller
		// The finished function will be clear
		System.out.println("11 + 3 =" + b.func1(11.3));// We want to find 11+3
		System.out.println("1 + 8 =" + b.func1(1.8));/ / 1 + 8
		System.out.println("11 + 3 + 9 =" + b.func2(11.3));
        
		// Class A related methods can still be used with combinations
		System.out.println("11-3 =" + b.func3(11.3));// The original intention here is to solve for 11-3}}// Create a more basic base class
class Base {
	// Write more basic methods and members to the Base class
}

/ / A class
class A extends Base {
	// Return the difference between two numbers
	public int func1(int num1, int num2) {
		returnnum1 - num2; }}// Class B inherits class A
// Added a new function: add two numbers and then add them to 9
class B extends Base {
	// If B needs to use class A methods, use composition relationships
	private A a = new A();
	
	// Here, the class A method is overridden, probably unconsciously
	public int func1(int a, int b) {
		return a + b;
	}

	public int func2(int a, int b) {
		return func1(a, b) + 9;
	}
	
	// We still want to use A's method
	public int func3(int a, int b) {
		return this.a.func1(a, b); }}Copy the code

5. Open and close principle

5.1 Basic Introduction

  1. The Open Closed Principle is the most fundamental and important design Principle in programming
  2. A software entity such as classes, modules, and functions should be open to extension (providers) and closed to modification (users). Build the framework with abstractions and extend the details with implementations.
  3. When software needs to change, try to do so by extending the behavior of software entities rather than by modifying existing code.
  4. The purpose of following other principles in programming and using design patterns is to follow the open closed principle.

5.2 Application Examples

public class Ocp {

	public static void main(String[] args) {
		// use it to see what the problem is
		GraphicEditor graphicEditor = new GraphicEditor();
		graphicEditor.drawShape(new Rectangle());
		graphicEditor.drawShape(new Circle());
		graphicEditor.drawShape(newTriangle()); }}// This is a class for drawing [user]
class GraphicEditor {
	// Take Shape and draw different shapes according to type
	public void drawShape(Shape s) {
		if (s.m_type == 1)
			drawRectangle(s);
		else if (s.m_type == 2)
			drawCircle(s);
		else if (s.m_type == 3)
			drawTriangle(s);
	}

	// Draw a rectangle
	public void drawRectangle(Shape r) {
		System.out.println("Draw a rectangle");
	}

	// Draw a circle
	public void drawCircle(Shape r) {
		System.out.println("Draw a circle");
	}
	
	// Draw a triangle
	public void drawTriangle(Shape r) {
		System.out.println("Draw a triangle"); }}//Shape, base class
class Shape {
	int m_type;
}

class Rectangle extends Shape {
	Rectangle() {
		super.m_type = 1; }}class Circle extends Shape {
	Circle() {
		super.m_type = 2; }}// Add a triangle
class Triangle extends Shape {
	Triangle() {
		super.m_type = 3; }}Copy the code

The disadvantage of this method is that it violates the OCP principle of design mode. For example, we need to add a new graphic type triangle, which has many modifications

Improvement: create Shape class into abstract class, and provide an abstract draw method, let subclasses to achieve, so that we have a new type of graphics, just let the new graphics class inherit Shape, and draw method can be achieved, the use of the code does not need to modify, meet the open and closed principle

public class Ocp {
	public static void main(String[] args) {
		// use it to see what the problem is
		GraphicEditor graphicEditor = new GraphicEditor();
		graphicEditor.drawShape(new Rectangle());
		graphicEditor.drawShape(new Circle());
		graphicEditor.drawShape(new Triangle());
		graphicEditor.drawShape(newOtherGraphic()); }}// This is a class for drawing [user]
class GraphicEditor {
	// Receive Shape and call draw
	public void drawShape(Shape s) { s.draw(); }}//Shape, base class
abstract class Shape {
	int m_type;
	
	public abstract void draw(a);// Abstract methods
}

class Rectangle extends Shape {
	Rectangle() {
		super.m_type = 1;
	}

	@Override
	public void draw(a) {
		// TODO Auto-generated method stub
		System.out.println("Draw a rectangle"); }}class Circle extends Shape {
	Circle() {
		super.m_type = 2;
	}
	@Override
	public void draw(a) {
		// TODO Auto-generated method stub
		System.out.println("Draw a circle"); }}// Add a triangle
class Triangle extends Shape {
	Triangle() {
		super.m_type = 3;
	}
	@Override
	public void draw(a) {
		// TODO Auto-generated method stub
		System.out.println("Draw a triangle"); }}// Add a graphic
class OtherGraphic extends Shape {
	OtherGraphic() {
		super.m_type = 4;
	}

	@Override
	public void draw(a) {
		// TODO Auto-generated method stub
		System.out.println("Draw other shapes"); }}Copy the code

Demeter’s Rule

6.1 Basic Introduction

  1. One object should have minimal knowledge of other objects
  2. The closer the relationship between classes is, the greater the coupling degree is
  3. The Demeter Principle, also known as the least known Principle, states that a class knows as little as possible about the classes it depends on. That is, for dependent classes, no matter how complex, try to encapsulate the logic inside the class. Do not disclose any information except for the provided public method
  4. There’s a simpler definition of Demeter’s rule: Only communicate with direct friends
  5. Direct friends: Every object is coupled to other objects, and as long as there is a coupling between two objects, we say they are friends. There are many ways of coupling: dependency, association, composition, aggregation, etc. Classes that appear in member variables, method parameters, and method return values are direct friends, while classes that appear in local variables are not direct friends. That is, unfamiliar classes are best kept from appearing inside the class as local variables.

6.2 Application Examples

There is a school, subordinate to each college and headquarters, is required to print out the school headquarters staff ID and college staff ID, programming to achieve the above functions, see the code demonstration

/ / the client
public class Demeter1 {

	public static void main(String[] args) {
		// Create a SchoolManager object
		SchoolManager schoolManager = new SchoolManager();
		// Output the staff ID of the school and staff information of the school headquarters
		schoolManager.printAllEmployee(newCollegeManager()); }}// School headquarters staff class
class Employee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}

	public String getId(a) {
		returnid; }}// Staff of the college
class CollegeEmployee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}

	public String getId(a) {
		returnid; }}// School of Management staff management
class CollegeManager {
	// Return all staff of the college
	public List<CollegeEmployee> getAllEmployee(a) {
		List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
		for (int i = 0; i < 10; i++) { // Here we add 10 employees to the list
			CollegeEmployee emp = new CollegeEmployee();
			emp.setId("College Employee ID =" + i);
			list.add(emp);
		}
		returnlist; }}// School management

Employee and CollegeManager are the direct friends of SchoolManager
//CollegeEmployee is not a direct friend but a stranger class, which violates Demeter's law
class SchoolManager {
	// Go back to the school headquarters
	public List<Employee> getAllEmployee(a) {
		List<Employee> list = new ArrayList<Employee>();
		
		for (int i = 0; i < 5; i++) { // Here we add 5 employees to the list
			Employee emp = new Employee();
			emp.setId("School headquarters employee ID =" + i);
			list.add(emp);
		}
		return list;
	}

	// This method completes the output of school headquarters and school staff information (ID).
	void printAllEmployee(CollegeManager sub) {
		
		// Analyze the problem
		//1. CollegeEmployee here is not a direct friend of SchoolManager
		//2. CollegeEmployee appears in SchoolManager as a local variable
		//3. Demeter's Law is violated
		
		// Get the college staff
		List<CollegeEmployee> list1 = sub.getAllEmployee();
		System.out.println("------------ College staff ------------");
		for (CollegeEmployee e : list1) {
			System.out.println(e.getId());
		}
		// Obtain the school headquarters staff
		List<Employee> list2 = this.getAllEmployee();
		System.out.println("------------ School Headquarters staff ------------");
		for(Employee e : list2) { System.out.println(e.getId()); }}}Copy the code
  • The CollegeEmployee class is not a direct friend of SchoolManager.
  • Demeter’s rule is to avoid such indirect friend coupling in classes
  • Improve the code according to Demeter’s law.
/ / the client
public class Demeter1 {
	public static void main(String[] args) {
		System.out.println("~~~ improvement using Demeter's rule ~~");
		// Create a SchoolManager object
		SchoolManager schoolManager = new SchoolManager();
		// Output the staff ID of the school and staff information of the school headquarters
		schoolManager.printAllEmployee(newCollegeManager()); }}// School headquarters staff class
class Employee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}
	public String getId(a) {
		returnid; }}// Staff of the college
class CollegeEmployee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}
	public String getId(a) {
		returnid; }}// School of Management staff management
class CollegeManager {
	// Return all staff of the college
	public List<CollegeEmployee> getAllEmployee(a) {
		List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
		for (int i = 0; i < 10; i++) { // Here we add 10 employees to the list
			CollegeEmployee emp = new CollegeEmployee();
			emp.setId("College Employee ID =" + i);
			list.add(emp);
		}
		return list;
	}
	
	// Output information about college staff
	public void printEmployee(a) {
		// Get the college staff
		List<CollegeEmployee> list1 = getAllEmployee();
		System.out.println("------------ College staff ------------");
		for(CollegeEmployee e : list1) { System.out.println(e.getId()); }}}// School management
Employee and CollegeManager are the direct friends of SchoolManager
//CollegeEmployee is not a direct friend but a stranger class, which violates Demeter's law
class SchoolManager {
	// Go back to the school headquarters
	public List<Employee> getAllEmployee(a) {
		List<Employee> list = new ArrayList<Employee>();
		
		for (int i = 0; i < 5; i++) { // Here we add 5 employees to the list
			Employee emp = new Employee();
			emp.setId("School headquarters employee ID =" + i);
			list.add(emp);
		}
		return list;
	}

	// This method completes the output of school headquarters and school staff information (ID).
	void printAllEmployee(CollegeManager sub) {
		
		// Analyze the problem
		//1. Package the output college staff method to CollegeManager
		sub.printEmployee();
	
		// Obtain the school headquarters staff
		List<Employee> list2 = this.getAllEmployee();
		System.out.println("------------ School Headquarters staff ------------");
		for(Employee e : list2) { System.out.println(e.getId()); }}}Copy the code

7. Principle of composite reuse

7.1 Basic Introduction

The principle of composite reuse is to use composition/aggregation rather than inheritance whenever possible.

8. Core ideas of design principles

  • Identify changes that may need to be made in your application and isolate them from code that doesn’t need to be changed.
  • Program for interfaces, not implementations.
  • Strive for loose coupling design between interacting objects

Reference: silicon valley is the Java design patterns: www.bilibili.com/video/BV1G4…