The opening and closing principle of design pattern learning

The learning content is all from the Douyin account [Teacher IT Nan teaches Java] courses.

1. Principle overview

The full English name of the Open Closed Principle is Open Closed Principle, abbreviated as OCP. Its English description is: software entities (modules, classes, functions, etc.) should be open for extension, but closed for modification. We translate it into Chinese: software entities (modules, classes, methods, etc.) should be "open to extensions and closed to modifications." In human terms, when we need to add a new function, we should expand the code based on the existing code (add modules, classes, methods, etc.) rather than modify the existing code (modify modules, classes, methods, etc.) . The following is a common example in a production environment. We will demonstrate a simplified order discount strategy for an e-commerce platform.
Do you think there is something wrong with the code below?

class Order {
private double totalAmount ;
public Order ( double totalAmount ) {
this . totalAmount = totalAmount ;
}
// Calculate the amount after discount
public double getDiscountedAmount ( String discountType ) {
double discountedAmount = totalAmount ;
if ( discountType . equals ( "FESTIVAL" )) {
discountedAmount = totalAmount * 0.9 ; // Holiday discount, 10 % off }
else if ( discountType . equals ( "SEASONAL" )) {
discountedAmount = totalAmount * 0.8 ; // Seasonal discount, 20 % off
}
return discountedAmount ;
}
}

In the above code, the Order class contains a method to calculate the discount amount, which applies discounts based on different discount types. When we need to add a new discount type, we have to modify the code of the getDiscountedAmount method, which is obviously unreasonable and violates the opening and closing principle.

Code that follows the opening and closing principle: 

// Abstract discount strategy interface
interface DiscountStrategy {
double getDiscountedAmount ( double totalAmount );
}
// Festival discount strategy
class FestivalDiscountStrategy implements DiscountStrategy {
@Override
public double getDiscountedAmount ( double totalAmount ) {
return totalAmount * 0.9 ; // 10 % off
}
}
// Seasonal discount strategy
class SeasonalDiscountStrategy implements DiscountStrategy {
@Override
public double getDiscountedAmount ( double totalAmount ) {
return totalAmount * 0.8 ; // 20 % off
}
}
class Order {
private double totalAmount ;
private DiscountStrategy discountStrategy ;
public Order ( double totalAmount , DiscountStrategy discountStrategy ) {
this . totalAmount = totalAmount ;
this . discountStrategy = discountStrategy ; }

In the code that follows the open-close principle, we define an abstract discount strategy interface DiscountStrategy , and then create a strategy class that implements the interface for each discount type. The Order class uses a combination method and contains a member variable of DiscountStrategy type to set or change the discount strategy at runtime (can be through coding, configuration, dependency injection, etc.). In this way, when we need to add a new discount type, we only need to implement the DiscountStrategy interface without modifying the existing Order code. This example follows the open-closed principle.

I believe that many junior programmers will have a sudden enlightenment when they see this, or some intermediate programmers who do not pay attention to coding style will also have an epiphany. There are a lot of people writing design patterns on CSDN, but no one tells you how. Yes, this is really well written. They are all programmers. Ten or twenty dollars is really nothing. This teacher is still quite good.

2. Does modifying the code mean violating the open-close principle?

The core idea of ​​the open-closed principle is to minimize modifications to existing code to reduce the risks and impacts of modifications. In the actual development process, it is unrealistic to not modify the code at all. It is normal to modify the code when requirements change or errors are discovered in the code. However, the open-closed principle encourages us to design better code structures so that when adding new features or expanding the system, we can minimize modifications to existing code. The following is an example of a simplified logger that demonstrates modifying the code where appropriate without violating the open-closed principle. In this example, our application supports logging to both the console and a file. Let's say we need to add a new feature that adds a timestamp when outputting logs.

Original code:
interface Logger {
void log ( String message );
}
class ConsoleLogger implements Logger {
@Override
public void log ( String message ) {
System . out . println ( "Console: " + message ); }
}
class FileLogger implements Logger {
@Override
public void log ( String message ) {
System . out . println ( "File: " + message );
// The implementation of writing logs to files is omitted
}
}

In order to add timestamp functionality , we need to modify the existing ConsoleLogger and FileLogger classes. Although we need to modify the code, since this is an improvement to existing functions rather than adding new functions, this modification is acceptable and does not violate the opening and closing principle. 

Modified code:
interface Logger {
void log ( String message );
}
class ConsoleLogger implements Logger {
@Override
public void log ( String message ) {
String timestamp =
LocalDateTime . now (). format ( DateTimeFormatter . ISO_LOCAL_DATE_TIME );
System . out . println ( "Console [" + timestamp + "]: " + message );
}
}
class FileLogger implements Logger {
@Override
public void log ( String message ) {
String timestamp =
LocalDateTime . now (). format ( DateTimeFormatter . ISO_LOCAL_DATE_TIME );
String logMessage = "File [" + timestamp + "]: " + message ;
System . out . println ( logMessage );
// The implementation of writing logs to files is omitted
}
}

In this example, we just modified the existing logger class appropriately to add timestamp functionality . This modification will not affect other parts of the code, so it does not violate the opening and closing principle. In short, appropriate code modifications do not necessarily violate the opening and closing principle. The key lies in how we weigh the impact of modifications and code design. 

When we follow the open-closed principle, the purpose is to make our code easier to maintain and more reusable , while reducing the risk of introducing new defects. However, in some cases, following the open-closed principle can lead to over-design and increase the complexity of the code. Therefore, in actual development, we should balance the degree of following the open-close principle according to actual needs and expected changes. Writing code is not designed for design. Talking about design without requirements is just a rogue. In some scenarios, such as the frequency of use of the project is not high, the possibility of modification is very low, or the code is already very simple, and using design patterns may increase the difficulty of development. Increasing development costs would be more detrimental than gain.

Guess you like

Origin blog.csdn.net/cclovezbf/article/details/132016206