First, let's look at an example in which book: If you want to build a bridge on a 1-meter-wide Creek, you will just put a wooden board on it. If you want to build this bridge on a wider River, you need to calculate the wood materials and prices. If you want the help of others, you need to add more drawings or something to help others understand your thoughts. Now you need to build bridges on the great river. You need to have an overall plan, including various aspects, such as possible future charges and profit distribution.
Here we will talk about the three-tier approach, which is actually aimed at building bridges above the great river. For a 1-meter-wide stream, it may not be used at all. But now I can't take a Yangtze River Bridge as an example. So here I will use this simple stream to talk about how to build a bridge. The reason why we talk so much nonsense is to prevent some people from reading this article, "what is so troublesome .." In fact, this is not a specific example, but a layered idea. It is very important to understand this.
The following is a common example of "User Logon. This example is very simple, but the sparrow is small. From data access to business rules to the interface.
This article consists of two parts. If you only want to study the object-oriented idea and are familiar with the implementation, you can skip the first part.
Part 1
Create a blank solution.
Then:
① "Add"-"New Project"-"Other Projects"-"Enterprise template project"-"C # generate blocks"-"Data Access" (data layer, (layer D)
② "Add"-"New Project"-"Other Projects"-"Enterprise template project"-"C # generate blocks"-"Business Rules" (business layer, hereinafter referred to as layer C)
③ "Add"-"New Project"-"Other Projects"-"enterprise-level template project"-"C # generate blocks"-"Web User Interface" (interface layer, (U layer)
④ Right-click "solution"-"project dependency" and set u to depend on D, C, and C to depend on D.
⑤ Add references D and C to U, and add references d to C.
So far, a three-tier shelf has been established.
What I said above is very specific and "stupid". People who know me think I am nonsense. In fact, during this time, I strongly felt that many people actually did not understand this simple process. Although there is no objection to creating two "Empty Projects" and one "asp net Web Application Project", it can also be used as a three-layer framework, many people think that these "enterprise-level template Projects" are actually empty projects, which is a misunderstanding. Yes, you can see from the solution resource manager that there is nothing in the enterprise-level template project. But you can open the project file in notepad and see the difference ?? You can't see some things behind the scenes, but the system is ready. That is to say, if you "using system data sqlclineit" in a class in the C layer or use a sqlconnection object, no errors will occur during compilation, however, some "policy warnings" will be generated in the "task list" to warn you not to put things that should be placed on layer d in the C layer (although the program is correct, but the readability and maintainability are discounted.) This function cannot be provided to empty projects.
We know that building a bridge requires bricks. We should first prepare bricks and then build the bridge. However, in order to explain the sequence and consistency, it is simple. We should first build a bridge and reproduce the bricks during the construction process, so that there will be no more "things not needed by the bridge ". In practice, you should prepare bricks first.
The U layer is actually a bridge, the C layer is a brick, and the D layer is a raw material (stone, sand ). This also explains why the U layer references and depends on the D Layer (instead of the U-to-C and C-to-D layers), because the bridge requires both bricks and stones.
We will create a login aspx in the u layer (Here we insert a sentence, I do not like to change the webform1 aspx automatically generated by the system to login or index or directly delete it, I usually keep it for test code, and wait until the entire system is frozen and then remove it .) Add one Textbox (ID = txt), one dropdownlist (ID = DDL), and one button (ID = BTN ). Dropdownlist is used to select the user name, button is the submit button, and textbox is used to enter the password.
The code to be added is divided into two parts:
1. DDL initialization during page_load.
2. Click processing of BTN. 1:
Private void page_load (Object sender, system. eventargs E)
{
If (! Ispostback)
{
This. DDL. Sort Se = datamanager. getonecolunm ("user", "uid"); // explanation 1
This. DDL. databind ();
}
}
2:
Private void btn_click (Object sender, system. eventargs E)
{
String uid = This. DDL. selectedvalue;
String psw=this.txt. text;
If (psw = "")
MessageBox ("blank password !");
Else
{
User theuser;
Try
{
TheUser = new User (uid); // Example 2
}
Catch (Exception e)
{
MessageBox (e. Message); // 2
Return;
}
If (theUser. CheckPsw (psw) // Example 3
{
TheUser. SetSessions ();
Response. Redirect ("..."); // Login successful!
}
Else
{
MessageBox ("Incorrect password !");
}
}
}
1. DataManager is a class in layer D that provides common data operations. The GetOneColunm (string Table, string Colunm) method returns a DataTable with only one column. The value is the Colunm column named Table in the database.
Public class DataManager
{
Public DataManager ()
{
}
Public static DataTable GetOneColunm (string Table, string Colunm)
{
// The related code is omitted here. Returns the specified column in the specified table.
}
}
In fact, this example shows how to directly access layer D by bypassing layer C on the U layer, because the structure is logically simple, obtaining the user name is not part of the business logic in the real society (only the interface is required, because this step is not required if two TextBox is used here)
2. Define an instance of the User class. The User class can be defined as follows:
Public class User
{
Public User (string uid)
{
If (DataManager. IsIn ("user", "uid = '" + uid + "'"))
Throw "the user does not exist ";
Else
// Other User () initialization;
}
Public bool CheckPsw (string psw)
{
If (DataManager. IsIn ("user", "uid = '" + uid + "' and psw = '" + psw + "'"))
Return true;
Else
Return false;
}
}
Note that a throw is used in the user class constructor to throw the exception that the user does not exist, and MessageBox (e. Message) is used in the following catch; to pop up the "user does not exist" error. This is actually to demonstrate a method for passing information between layers. Exceptions are also a means. Although there can be other methods, such as return values, reference parameters directly use a method to obtain information about whether the user exists. It is not necessary to put it in the constructor. I just did this to demonstrate the transfer process, later we will discuss the application of this method in some special cases in Layered mode to solve some problems. This class uses a static method IsIn (string Table, string str) of the DataManager class. In fact, this method actually runs the "select * from Table where str" command"
This SQL statement returns false when null is returned; otherwise, true is returned. A simple method. The call of layer C to layer D is demonstrated here.
By the way, because in. net, the project name will be the namespace of the project by default, you can change the namespace in all automatically generated code to "solution name" so that the three layers can be freely called. You can also use the space names of other layers in the calling area. For example, in the above Login. aspx. cs, using2 is required, and in User. cs, using is required.
Description 3: The User password check here also uses a method of the User class CheckPsw (), and this method uses IsIn.
We noticed that the MessageBox () method is used to pop up the dialog box on the U layer page. In fact, this method is written in PageBase. in cs, another file on the U layer inherits the Page class, And the Login class inherits it. This method actually implements Response. write ("<script> alert (\" "+ msg +" \ ") </script>") is encapsulated.
So far, the login is complete, and the implementation of the example is also complete. But I only talked about "ran" and didn't talk about "why ". Let's start with "why ".
Part 2
For comparison, we use a non-object-oriented, non-hierarchical Asp-style Aspx same logon for comparison. I will not write the specific Asp code, but I will log on to it all. Let's take a look at what happened to both of them (this is unfortunate, but it often happens ):
1. The Project Manager suddenly said, "You don't need SqlServer, but you need to change to Access ). Let's take A look at what happens on the two sides: Layer 3 (A), and change the connection in the DataManager class (in actual situations, it is very likely to actually change its base class, it does not need to be changed), Web. replace the string in config. Asp-style Web is also required. in config, you also need to change the connection. The number of modifications is almost the same in this specific "Creek" example. In the "bridge" example, B should slightly change the point, but it will not be much more. But! Please note that when we modify the code, the main time and effort are not spent on the "change" action, it is spent on "where to change" and "looking for a place to change. On the bridge, it takes a lot of time for B to find and replace most files. A is only in the data layer, and the other two layers do not need to be modified. From this perspective, we think of the two principles:
A) The data layer must be able to ensure that database changes (any structural changes or type changes) are not transparent to other layers. That is, how the database changes. Other layers should never be changed to even one line of code! (Web. config is the configuration of the entire application. Although it physically exists in the folder of the U layer, I prefer to think that it is independent of any layer, so I will not count it here)
B) The smaller the data layer, the better (without this principle, we place everything on the data layer, of course, database changes have no impact on the outside-because there is almost nothing outside-but this is obviously not feasible ). As we have mentioned earlier, most of the time is spent on "Searching". If you are smaller, it is easier to find it.
2. The customer suddenly proposed that the B/S version should be changed to the C/S version. For (B), this is a blue ray !! He has to re-do all his work (or almost all of his work), although he has a lot of code to use, however, he will have to keep using previous code in "copy-paste" for a short period of time. (A) What happened ?? If you are careful, you will find that (a) You need to create a project "Windows User Interface" (like the previous one, add references and project dependencies) and drag several controls to the project, set the control name to txt, DDL, and BTN, and then copy the click code and pageload code in the past (actually ...) You do not need to modify a line of code !!!! Of course, this is an extreme example (both win and Web have Textbox, dropdownlist, and buttoncontrols, And the MessageBox () method defined in pagebase () the same name as the method in win ...) However, even with so many coincidences, we can still believe that on the bridge, (a) will do much less work than (B. From this perspective, we think of two similar principles:
A) the interface layer should ensure that the content of other layers does not need to be modified for any changes to the interface (no matter the specific example changes DDL to another Textbox, or change B/S to C/S)
B) The smaller the interface layer, the better (the same as above .)
3. Apart from the interface layer and data layer (if there are only three layers in your solution), the rest is the content of the logic layer. Therefore, in contrast to the previous one, we can draw a conclusion:
A) the logical layer should be modified without the impact of database and interface changes.
B) The larger the logical layer, the better (because the smaller the other two layers, the better ...)
With the most basic principles, we should discuss how to layer the principles:
1. What layer should PageBase. cs be placed on? According to the above principle, it should be placed in the C layer. But in fact, I used to put it on the U layer, or another (4th layers, general-purpose bottom layer, lower than the data layer) layer. Where should I put it? My first practice was on the C layer, because we should put it in C according to the above principle, however, after a while I got used to the "four-layer", I put it on the general-purpose underlying layer (hereinafter referred to as layer B, which is also placed as the SqlHelper class originally in layer D, etc, including all the "general" classes in the original three layers, which means that other systems can also be used without modification, this layer usually uses the company or group name as the namespace instead of the solution name. When creating a solution for a new project, you can "add an existing project ", simple addition and continuous accumulation play a major role in improving efficiency and code reuse .) However, if there are only three layers, I am inclined to put PageBase ON THE U layer. This is mainly because of a recent article devoted to object-oriented analysis and design. However, I added one rule in the previous section: "If a class exists only for some special implementation of a layer, it must be placed in this layer. For example, PageBase exists for the special implementation of the u layer (B/S implementation), and SqlHelper is for the special IMPLEMENTATION OF THE D Layer (SqlServer database) and exist. Therefore, they must be placed on the U and D layers respectively (if this one is not added, they should be placed on the C layer first, because the larger the C layer, the better, in addition, database and interface changes do not need to be changed to these two classes-although they may be useless due to changes, they still do not need to be modified)
2. Oldjacky once talked to me about a problem: the Datagrid allows the delete operation. However, if only the last record is left, the delete operation is not allowed! Should the deletion be placed on layer C, layer D, or layer U? I think we should consider the following from another perspective:
A) This "Not Allowed" is "not allowed by Business Rules" (for example, the data in the table indicates the current employee in the store, and the deletion indicates that the employee leaves the store-may take the goods or something, add indicates that the employee is returned. When there is only one clerk at the counter, it is clear that he cannot leave for delivery.) in this case, the "delete prohibited" operation should be performed on the C layer.
B) this kind of "not allowed" is "not allowed by the Program Implementation" (for example, if it is null, it will cause other places such as tostring () method to generate "the object reference is not set to the instance of the object ......" Or the subjective desire of the program designer or project manager to "disallow" the program to reduce the workload or simplify the program ). At this time, the "delete prohibited" can be placed on the u layer (such as the tostring mentioned above) or the D Layer (such as violation of database constraints)
3. Careful users may find that in the previous login example, users can get three pop-up errors: "Empty Password" "Wrong Password" "user does not exist ", the first two of them are made in the U layer, but "user does not exist" is made in the C layer (I am referring to this string) or is the bridge established at the beginning, I used the "xistream bridge" here to explain the "Great River Bridge", so I intentionally turned a useless circle here, just like how many years it would take to compute the wooden board on the creek, in fact, it does not make any sense to the creek. It is only added to explain the Needs of the bridge. After all, the bridge needs such consideration. I assume that "the user does not have a prompt to pop up" is a business logic requirement, the "prompt is not required if no password is entered" is not required by the business rules (for example, a blank password can be allowed in the actual business, but the project manager does not agree that the password is required) in this login example, there is actually no problem at all, but in large projects, if this is not required by business rules, it should not be placed at the business layer. If it is a business rule, it must be placed at the business layer. It facilitates the connection between the business model and the real entity, and also facilitates the business logic to better express the characteristics of the real entity.
So far, I once again summarized our final principles:
1. If a class exists only for some special implementation of a layer, it must be placed in this layer.
2. The data layer should be as small as possible while ensuring that database changes are invisible to other layers.
3. The interface layer should be as small as possible without affecting the business logic layer.
4. If a class is not required by business rules, it should not be placed at the business layer, and vice versa.
5. The logical layer should be as big as possible without affecting the database or interface changes.
If there is a conflict between the above five points, when finding a balance point, the first is higher than the latter. For example, rule 1 is more likely to be used when 1 and 3 conflict.
Part 2 ends
One thing should be the scope of "programming code habits" and "Object-Oriented". However, there is some relationship with layering, so let's also talk about it. "If your code is translated into Chinese and necessary punctuation marks are added, other people who do not understand the program may still feel confused, so you may not be able to score well ". For example, the previous btn click:
{
The string username is the value selected from the drop-down list;
The string password is the input box value;
If the password is empty
Dialog Box (empty password !);
Otherwise
{
User;
Try
{
This user is a new user (User Name );
}
Capture (error)
{
Dialog Box (error message );
Return;
}
If this user checks the password)
{
This user sets the status;
Response relocation ("...");
}
Otherwise
{
Dialog Box (incorrect password)
}
}
It is better for people who do not understand the code to understand what it is.
Finally, the "delete" example of oldjacky's DataGrid deletion is obviously in the D layer, but it may not be allowed in C or U. If there is nothing to say in U, if in C, therefore, a reasonable implementation method of this "Not Allowed" is to throw this situation in the C layer. When the throw is caught in the U layer, the delete operation is prohibited. In this way, when the two layers are blocked for a reason at the same time, the source of this prohibition can be seen from the code at a glance. Similar to the previous two pop-up errors.