Definition: Software entities (classes, modules, functions, etc.) should be extensible, but cannot be modified. Open for extensions and closed for changes. The key is abstraction, clearly separating the common parts of a function from the implementation details.
Here we are required to have abstract concepts when writing code. What is abstraction? Refers to the thinking process of extracting concepts from entities. It is to extract the characteristics of common essence from many objects. In the process of writing code, you need to abstract the class, just grasp the essential functions of this class and don’t always think about its specific functions in this project.
We continue to look at the principle of openness and closure, which requires clearly separating the common parts of a function from the implementation parts. Because when you initially build the architecture, you cannot predict all the changes that will occur, then this class is not unchanging. When it is implemented in each module, you find that abstract classes are suitable for this function, but not for another function. So should I go back and modify the abstract class? This is a very expensive one, and needs to be rethinked in full and adjust the specific details. If the program has not been released yet, it will be better if it is released. Once the program is released, then the abstract class will be modified, which will have a greater impact. Therefore, when you start abstracting, you must prevent such phenomena from happening and follow the principle of openness and closure. Abstract classes and interfaces are a standard. Once defined in the program, they cannot be easily modified. If the requirements change, what should I do? You can extend this interface, rewrite the method, or add new methods after inheriting it, but do not modify it.
The following are two examples to illustrate the principle of openness and closure.
1. Connect to the database as an example.
For example, different types of database connections used in programs, Access and Oracle. Direct connection is as follows:
class ConnectAccess { public string ConnectString() { string dataPath = "Database Path"; return ("Provider=.4.0;Data Source={0};Persist Security Info=True;Jet OLEDB:Database Password={1}", dataPath, "password"); } } class ConnectOracle { public string ConnectString() { return @"server=localhost;database=Namespace;uid=username;pwd=password"; } }
Call
static void Main(string[] args) { //Connect Access ConnectAccess connAccess = new ConnectAccess(); OleDbConnection accessConnection = new OleDbConnection(connAccessConnectString()); //Connect Oracle ConnectOracle connOracle = new ConnectOracle(); OracleConnection oracleConnection = new OracleConnection(connOracleConnectString()); }
In this way, we have to consider which parameters of OleDbConnection are used every time. Change it below. Abstract an interface.
interface ConnectDataBase { string ConnectString(); } class ConnectAccess : ConnectDataBase { #region ConnectDataBase Member public string ConnectString() { string dataPath = "Database Path"; return stringFormat("Provider=MicrosoftJetOLEDB0;Data Source={0};Persist Security Info=True;Jet OLEDB:Database Password={1}", dataPath, "password"); } #endregion } class ConnectOracle : ConnectDataBase { #region ConnectDataBase Member public string ConnectString() { return @"server=localhost;database=Namespace;uid=username;pwd=password"; } #endregion }
Call
static void Main(string[] args) { ConnectDataBase conn = null; //Connect Access conn = new ConnectAccess(); OleDbConnection accessConnection = new OleDbConnection(connConnectString()); //Connect Oracle conn = new ConnectOracle(); OracleConnection oracleConnection = new OracleConnection(connConnectString()); }
After the correction, just care about which class the conn is instantiated, and that's fine. However, you may see that since Oracle connection requires OracleConnection, the advantages may not be easy to see.
2. The basic type is used as a method parameter as an example.
This is the reason why general design principles emphasize that method parameters should be avoided as much as possible. Compare the following two method definitions:
//Definition 1bool Connect(string userName, string password, string wifiAddress, int port) { return false; }
//Definition 2bool Connect(Account account) { return false; }
public class Account { public string UserName { get; set; } public string Password { get; set; } public string WifiAddress { get; set; } public int Port { get; set; } }
In comparison, Definition 2 has an additional definition of Account class, and the Connect() method is obviously more stable. If the wifiAddress of the Connect() method changes, Definition 1 must modify the interface of the method. Correspondingly, all objects that call the Connect() method will be affected; Definition 2 only needs to modify the Account class. Since the interface of the Connect() method remains unchanged, the caller of the Connect() method does not need wifiAddress, such modification will not affect the caller at all, thereby reducing the impact of changes in demand.
In short, the most important thing about the principle of openness and closure is abstraction, but it does not mean that once the abstract interface and class are determined, it must not be modified. However, when we abstract, we must think comprehensively and strive to avoid modification. Once the requirements change, we only need to change when they are implemented. Of course, the needs are ever-changing. Once the abstract part needs to be changed, as long as the previous principle is strictly followed, the impact will be much smaller. Of course, if it is modified, unit testing must be carried out, and everything used must be tested correctly.