Code Refactoring - Obtaining Encapsulation DELPHI Coding Example Code refactoring is a way to obtain a good structure. Through refactoring, we can improve the quality of the code and increase the degree of code reuse while keeping the functions unchanged. Here is a concrete example of how to improve code quality and gain encapsulation. (The example uses DELPHI) Code function: Set a filter for the data set (TClientDataSet). The user can select the field to be filtered in a TComboBox, and then enter the value to be filtered in a Tedit box. As shown in Figure 1: The most common method is to hard-code the field names in our data set in the Items attribute of TComboBox, and then add a lot of case or if statements to the code to determine the fields selected by the user to set filters for the data set. device. ...... case ComboBox1.ItemIndex of0: ClientDataSet.Filtered := False; ClientDataSet.Filter := ' F_CODE = ''' + Edit2.Text + ''''; ClientDataSet.Filtered := True;1: ClientDataSet.Filtered := False; ClientDataSet.Filter := ' F_CHINESE_NAME = ''' + Edit2.Text + ''''; ClientDataSet.Filtered := True;... end; Or use... if ComboBox1.Text = 'Material Code' then begin ClientDataSet.Filtered := False; ClientDataSet.Filter := ' F_CODE = ''' + Edit2.Text + ''''; ClientDataSet.Filtered := True;endelse if ComboBox1.Text = 'name' thenbegin ClientDataSet.Filtered := False; ClientDataSet.Filter := ' F_CHINESE_NAME = ''' + Edit2.Text + ''''; ClientDataSet.Filtered := True;end... This code also implements setting filters for the data set through hard coding. The function meets the needs, but the above code is inflexible. If the data set has many fields, the coder is required to enter the fields one by one in Items, and the order must be checked when writing the case. Otherwise, the set filter will be wrong and it will be easy for developers to introduce BUG. It is also painful to maintain a large number of if statements when using if statements, and it does not support demand changes. When the user requests to change the Chinese display name of the data set field, he must also remember to change the hard-coded data in TComboBox. Items. If If you forget, BUG will be introduced. So in the first reconstruction, I tried to dynamically load the data in TComboBox. Items, and at the same time, in order to achieve comparison when the user selects after loading. I added a private FFields: array[0..20, 0..2] of string; field to this query FORM to save the field information data in the data set. At the same time, a process of loading data is implemented: PRocedure TFrmSPARealStorageQuery.GetQueryFields;var i, iFieldsCount: Integer;begin iFieldsCount := 0; with DBGride1.DataSource.DataSet do begin for i := 0 to Fields.Count - 1 do if Fields[ i].Visible then begin FFields[iFieldsCount, 0] := Fields[i].FieldName; FFields[iFieldsCount, 1] := Fields[i].DisplayLabel; Inc(iFieldsCount); end; ComboBox1.Items.Clear; for i := 0 to iFieldsCount - 1 do ComboBox1.Items.Add (FFields[i, 1]); end;end; This enables dynamic loading of field information at runtime. So my filter settings look like this. if ComboBox1.Text <> '' thenbeginClientDataSet.Filtered := False; ClientDataSet.Filter := FFields[ComboBox1.ItemIndex, 0] + '''' + Edit2.Text + ''''; ClientDataSet.Filtered := True; end; This method undoubtedly increases the flexibility of the code and increases the reusability of the code, because the code is well isolated from changing data. Therefore, it can be said to be convenient as long as you add the private field FFields: array[0..20, 0..2] of string in another FORM that also implements this function and use the above process of dynamically loading data set fields. Reuse is achieved. But this reuse is not very good because we have not achieved good encapsulation. This leads to duplicate code scattered everywhere in your program (you often use COPY to obtain the reuse of this function, because the above code does not have good encapsulation). If one day you want to modify the data loading function you will have to find somewhere to copy the function - and you will have to modify the code scattered in other places. So I refactored it again and further encapsulated the code. The code is as follows: unit uDataSetFieldsInfo; // Description: The unit includes the TDataSetFieldsInfo class, which encapsulates the acquisition of data set sub-segment information. // And provides a method interface for displaying information in the combobox list display field and obtaining the corresponding subsection name // Created : wuchhao // Date : 2003.5interfaceuses Classes, DBClient, StdCtrls; type TDataSetFieldsInfo = class private FFieldsList: TStrings; public constructor Create ; destructor Destroy; override; procedure GetDataSetFields(Source: TClientDataSet); procedure ShowFieldsInfo(Target: TComboBox); function GetFieldsNameByDisplayLabel(DisplayLabel: string): string; end;implementation{ TDataSetFieldsInfo }constructor TDataSetFieldsInfo.Create;begin FFieldsList := TStringList.Create;end;destructor TDataSetFieldsInfo.Destroy;begin FFieldsList.Free; inherited;end;procedure TDataSetFieldsInfo.GetDataSetFields(Source: TClientDataSet);var i: Integer;begin FFieldsList.Clear; with Source do begin for i := 0 to Fields.Count - 1 do if Fields[i].Visible then begin FFieldsList.Add(Fields[i].DisplayLabel); FFieldsList.Add(Fields[i].FieldName); end; end;end;function TDataSetFieldsInfo.GetFieldsNameByDisplayLabel( DisplayLabel: string): string;var index: Integer;begin Result: = ''; index := FFieldsList.IndexOf(DisplayLabel); if index <> -1 then Result := FFieldsList.Strings[index+1] ;end;procedure TDataSetFieldsInfo.ShowFieldsInfo(Target: TComboBox);var i: Integer;begin Target.Items.Clear; i:=0; while i < FFieldsList .Count do begin Target.Items.Add(FFieldsList.Strings[i]); i:= i+ 2; end;end;end. The unit uDataSetFieldsInfo encapsulates the data and methods related to realizing the functions described in this article, and encapsulates them in a class, thus realizing the Open - Close principle in object-oriented design. The class becomes a black box, so it can be easily reused (black-box reuse) without worrying about code duplication. At the same time, because it encapsulates function-related information, the responsibilities of the class are clearly defined (single responsibility), and it has sufficient granularity and good encapsulation. TdataSetFieldsInfo effectively isolates the combo box from changing data, ultimately improving the degree of code reuse while reducing the responsibilities of the FORM class and the amount of hard-coding magic numbers. Here is the new code: First declare a reference to the TdataSetFieldsInfo class in the FORM. ...Call when FORM is created: FFieldsInfo := TDataSetFieldsInfo.Create;FFieldsInfo.GetDataSetFields(cdMaster);FFieldsInfo.ShowFieldsInfo(ComboBox1); At this time, my filter settings become: if ComboBox1.Text <> '' thenbeginClientDataSet.Filtered := False; ClientDataSet.Filter := FFieldsInfo.GetFieldsNameByDisplayLabel(ComboBox1.Text) + '''' + Edit2.Text + ''''; ClientDataSet.Filtered := True;end; Obtain the corresponding subsection by calling the interface procedure of the FfieldsInfo object name. This article is a simple example of refactoring code. I think the class I implemented above can be written in many ways and have better algorithms. This is just to provide an idea about refactoring the code. In order to improve the quality of our code and its maintainability and scalability, we will explore ideas on OOD programming.