What is the principle of opening and closing
Closed for modification, open for extension
When we add a function, it should expand on the existing functionality, rather than modify (modification module, class, method) on the existing functionality
Examples of violation of the principle of opening and closing
Here, when there is a monitoring example when an error occurs or qps reach a certain threshold of early warning
public class the Alert { / ** * Alarm rules can be stored in their own set * / Private AlertRule rule; // notify Class Private the Notification Notification; public the Alert (AlertRule rule, the Notification Notification) { the this .rule = rule; the this .notification = Notification; } public void Check (String API, Long RequestCount, Long errorCount, Long durationOfSeconds) { Long TPS = RequestCount / durationOfSeconds; //tps exceeds the threshold IF (tps> rule.getMatchedRule (API) .getMaxTps ()) { // trigger alert notification.notify (NotificationEmergencyLevel.URGENCY, "..." ); } // error count exceeds the threshold IF ( errorCount> rule.getMatchedRule (API) .getMaxErrorCount ()) { // alert is triggered notification.notify (NotificationEmergencyLevel.SEVERE, "..." ); } } }
If you need to increase demand much more than the threshold requested time
public class the Alert { / ** * Alarm rules can be stored in their own set * / Private AlertRule rule; // notify Class Private the Notification Notification; public the Alert (AlertRule rule, the Notification Notification) { the this .rule = rule; the this .notification = Notification; } // parameter changes an increase timeOutCount public void Check (String API, Long RequestCount, Long errorCount, Long timeOutCount Long durationOfSeconds) { LongRequestCount = TPS / durationOfSeconds; // TPS exceeds a threshold value IF (TPS> rule.getMatchedRule (API) .getMaxTps ()) { // alert is triggered notification.notify (NotificationEmergencyLevel.URGENCY, "..." ); } / / number of errors exceeds the threshold IF (errorCount> rule.getMatchedRule (API) .getMaxErrorCount ()) { // trigger alert notification.notify (NotificationEmergencyLevel.SEVERE, "..." ); } // change two: adding interface timeout processing logic Long timeoutTps = timeoutCount / durationOfSeconds; IF (timeoutTps> rule.getMatchedRule(api).getMaxTimeoutTps()) { notification.notify(NotificationEmergencyLevel.URGENCY, "..."); } } }
Parameter changes the modifications involve changing the way we check and the original caller's
Based on the principle of opening and closing design
1. The first step is to encapsulate the class parameters
2. The second step if encapsulated by a Handle
public class the Alert { // rule storage triggering an alert Private List <AlertHandler> = alertHandlers new new the ArrayList <> (); public void addAlertHandler (AlertHandler alertHandler) { the this .alertHandlers.add (alertHandler); } public void Check (ApiStatInfo apiStatInfo) { for (AlertHandler Handler: alertHandlers) { handler.check (apiStatInfo); } } } / ** * parameter for packaging * / public class ApiStatInfo { // constructor omitted / getter / setter method Private String API; Private Long RequestCount; Private Long errorCount; Private Long durationOfSeconds; } / ** * abstract parent class code reuse without repeated definition of each class member variables * / public abstract class AlertHandler { protected AlertRule rule; protected the Notification Notification; public AlertHandler (AlertRule rule, the Notification Notification) { the this .rule = rule; the this .notification = Notification; } public abstract void check(ApiStatInfo apiStatInfo); } /** * 进行tps校验通知的预警 */ public class TpsAlertHandler extends AlertHandler { public TpsAlertHandler(AlertRule rule, Notification notification) { super(rule, notification); } @Override public void check(ApiStatInfo apiStatInfo) { long tps = apiStatInfo.getRequestCount()/ apiStatInfo.getDurationOfSeconds(); if (tps > rule.getMatchedRule(apiStatInfo.getApi()).getMaxTps()) { notification.notify(NotificationEmergencyLevel.URGENCY, "..."); } } } /** * 基于错误数校验通知的预警 */ public class ErrorAlertHandler extends AlertHandler { public ErrorAlertHandler(AlertRule rule, Notification notification){ super(rule, notification); } @Override public void check(ApiStatInfo apiStatInfo) { if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()).getMaxErrorCount()) { notification.notify(NotificationEmergencyLevel.SEVERE, "..."); } } }
Use of external unified front of the class talked about the exposure of the package
public class the ApplicationContext { Private AlertRule alertRule; Private the Notification Notification; Private the Alert Alert; / ** * initialize Alert * / public void initializeBeans () { alertRule = new new AlertRule ( / * parameter is omitted. * / ); // omit some initialization Code Notification = new new the Notification ( / * . omitted parameters * / ); // omit some initialization code Alert = new new the Alert (); alert.addAlertHandler (new TpsAlertHandler(alertRule, notification)); alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification)); } public Alert getAlert() { return alert; } // 饿汉式单例 private static final ApplicationContext instance = new ApplicationContext(); private ApplicationContext() { instance.initializeBeans(); } public static ApplicationContext getInstance() { return instance; } } public class Demo { public static void main (String [] args) { ApiStatInfo apiStatInfo = new new ApiStatInfo (); // ... provided omitted Code apiStatInfo data values ApplicationContext.getInstance () getAlert () Check (apiStatInfo);.. } }
When we need to be modified to increase the timeout notification
public class the Alert { // Code} unmodified ... public class ApiStatInfo { // constructor omitted / getter / setter method Private String API; Private Long RequestCount; Private Long errorCount; Private Long durationOfSeconds; Private Long timeoutCount; // change a : Add a new field } public abstract class AlertHandler { // code does not change ...} public class TpsAlertHandler the extends AlertHandler { // code does not change ...} public class ErrorAlertHandler the extends AlertHandler { // code does not change ...} // two changes: Add new Handler public class TimeoutAlertHandler the extends AlertHandler { // omitted codes ...} public class the ApplicationContext { Private AlertRule alertRule; Private the Notification Notification; Private the Alert Alert; public void initializeBeans () { alertRule = new new AlertRule ( / * parameter is omitted. * / ); // omit some initialization code = Notification new new the Notification ( / * parameter is omitted. * / ); // omit some initialization code Alert = new new the Alert (); alert.addAlertHandler ( new new TpsAlertHandler (alertRule, Notification)); alert.addAlertHandler ( new new ErrorAlertHandler (alertRule, Notification)); // three changes: Register Handler alert.addAlertHandler ( new new TimeoutAlertHandler (alertRule, Notification)); } // ... No other changes to the code omitted ... } public class Demo { public static void main (String [ ] args) { ApiStatInfo ApiStatInfo = new new ApiStatInfo (); // ... apiStatInfo set of field codes omitted apiStatInfo.setTimeoutCount (289); // change IV: Set value tiemoutCount ApplicationContext.getInstance () getAlert () Check (apiStatInfo);.. }
The above changes may be wondering a few points are in violation of the principle of modified closed
In fact, just above Expanding on the original function did not affect the original logic
Although a change and change is a modification of three real-time opening and closing principle is not absolute. Although it is modified in order to inject new extension points, some modifications can not be avoided
How to achieve "open for extension but closed for modification."
We should always have the expansion of consciousness, consciousness abstraction, encapsulation consciousness. When writing code, we have to take the time to think about what the future may this code needs to change, how to design code structure, extension points to stay well in advance, so that when future needs change, no change in the overall structure of the code , so that with minimal changes to the code, the new code will be inserted into the flexible extension point. Many design principles, design, design patterns, all to improve the scalability of the code as the ultimate goal. In particular, the 23 classic design patterns, mostly in order to solve the scalability problem code and summed up, are based on the principle of opening and closing as the guiding principle. The method most commonly used to increase the scalability of the code are: polymorphism, dependency injection, rather than for programming, and most of the design patterns (for example, decorative, strategy, template, chain of responsibility, status) based on the interface.
For example, we want to send a message by kafa
We need to change the interface by the late abstract rocketMq only need to add the corresponding implementation class
// 这一部分体现了抽象意识 public interface MessageQueue { //... } public class KafkaMessageQueue implements MessageQueue { //... } public class RocketMQMessageQueue implements MessageQueue {//...} public interface MessageFormatter { //... } public class JsonMessageFormatter implements MessageFormatter {//...} public class MessageFormatter implements MessageFormatter {//} ... public class Demo { Private the MessageQueue MsgQueue; // for programming interfaces rather than based public Demo (the MessageQueue MsgQueue) { // dependency injection the this .msgQueue = MsgQueue; } // msgFormatter: polymorphic, dependency injection public void the sendNotification (the Notification Notification, MessageFormatter msgFormatter) { // ... } }