This article is part 1 of the "loosely connected programming" series. (Loose Pair Programming topic directory)
Today, we just need to reuse a frame (Asp.net mvc3, which includes controller and view) and record the process.
Compared with the general process of reuse, the L-type code structure has the following features:
1. If reuse is difficult, it is generally not intended to form "reusable code" before reuse ". By the way, you can write functions as exceptions.
2. From the second reuse, code reuse is formed.
This sounds easy, but requires that the person who writes the reusable code is the one who calls the reusable code. Otherwise, he does not know whether the code will be reused or the number of times it will be reused. In other words,Do not arrange for people to write reusable code.Otherwise, it is easy for no one to use it, or to use it only once, and it is hard to write a reusable code. It is also easy for everyone to keep repeating things, but he does not know. This can be avoided if you can write underlying reusable libraries while the upper-layer applications know the actual usage.
To meet business requirements, you need to compile a business that performs "add, delete, modify, and query" operations on products under the product line. The imaginary interface is as follows (actually after completion ):
Think of a business that has previously compiled a "add, delete, modify, and query" operation for a team under the department. The interface is roughly as follows:
I decided to borrow this page, and I borrowed all the code (except that the personnel list is not displayed, there is almost no difference ), and the base classes of these two things are the same (this is important ).
Product: item (parent-child relationship item): udcable (which can be customized)
Department: item (parent-child relationship item): udcable (which can be customized)
Note that in the following code, the base classes of program and team are department.
The original teams processing code is as follows:
public ActionResult Index(int? programID) { return MFCDefaultView(programID ?? _repMFC.ReadAllItems<Program>().First(i => i.Type == ItemWhattype.DepartmentTypeProgram).ID); }
public ActionResult Create(int programID, string redirectToAfterCreated, string returnUrl = null) { return Redirect("/MFC/Items/Create?fatherID=" + programID + "&what=" + SystemItemWhat.Deaprtment + "&type=" + ItemWhattype.DepartmentTypeTeam + "&returnUrl=" + HttpUtility.UrlEncode(returnUrl)); } ... }
The code to be deleted, moved between left and right, and edited is not included here. It is handled by itemscontroller, which is a public controller of their base class item. Index/create/edit/details all have common code and interfaces on the other side, but the team page is special. For example, if you do not return the index page after each creation, instead, go to the member assignment page, so all functions and pages are re-implemented here, but some of them point to the functions in itemscontroller, such as the CREATE () above ().
The new business needs are similar to this, so it is better to rebuild it together with writing another one. We can use this kind of thing in the future, from code to interface, we can use it directly.
Reconstruction order
This type of reconstruction caused by reuse has many working orders, which are roughly two:
1. First write the productscontroller, then compare the difference with teamscontroller, then extract the common part for reuse, and then implement both teams and products based on reuse.
The disadvantage is to write both sides, and the productscontroller and several corresponding views will soon be thrown away.
This method is suitable for beginners to find reusable code extraction through comparison, that is, it is less efficient and can only be used for learning.
2. Write a reusable underlying layer directly, and write productscontroller based on the underlying layer. After the call, teamscontroller is also implemented based on the underlying layer.
The disadvantage is that the bug of products may be entangled with the reuse of the underlying bug, causing debugging trouble.
3. First write an underlying layer based on teams, and then write teams as reusable code. After debugging is passed, write products directly based on the underlying layer.
The downside is that you may not be able to write reusable underlying layers only when looking at teams. You need to take more experience.
The advantage is that the debugging process is relatively simple. You can use teamscontroller as a test case to test the reusable underlying layer.
I chose method 3 this time (not because it is the best, I chose 1 when there is not much code before, but I chose 2 when I was confident ).
Only the index function is processed in the Reconstruction step (for details about the create function, refer to the next article ). 1. Write an action in itemscontroller to replace teams/index.
This new function is called itemhorizentallist (horizontal list)
public ActionResult ItemHorizentalList(int rootID, string what, string type, string returnUrl) { var root = _repMFC.ReadAt<Item>(rootID); IEnumerable<Item> subItems = root.SubItems().Where(i => i.What == what && i.Type == type); return MFCDefaultView(root, subItems, what, type, returnUrl); }
Rootid indicates the team under the program, or the product under the productline of the product line. Whats and whattypes determine the type of items to display, it is up to them to determine whether an item is a department's program, team, product line, or product.
The return mfcdefaultview is ignored for the moment. Its function is to transmit multiple parameters to the view at the same time. Previously, Asp.net controller. view () can only pass one parameter. To pass multiple parameters, you must use viewbag or viewmodel (although "viewmodel" is a design mode, you must not only write an additional viewmodel, it is also necessary to check the code in it frequently, which breaks the readability of the Code ). This mfcdefaultview can be passed in to the variable parameter list, which can be read sequentially in pagedata []. That is to say, the process of calling a view is similar to calling a function. This is what we have just encapsulated, and I will say goodbye to iewbag and viewmodel.
2. Implement the work of teams/index in the view of the new actoin.
The original view code (views/teams/index. cshtml ):
This. initializelayout ("team"); <H2> All teams </H2> <HR/> <br/> <Div class = "item-hierarchy @ mfcui. hovertwinkletriggerbodyclass ("Create-New-Team") "style =" float: Left; Min-Height: 10px; Min-width: 10px; margin-Right: 16px; "> <Div class =" item-Hierarchy-Body "> @ mfcui. imagelink ("new team", "/site/teams/create? Required mentid = "+ model. ID +" & redirecttoaftercreated = "+ httputility. urlencode ("/site/teammembers/index? Required mentid = "+ model. ID), imgurl: "/site/teams/create48.png", showtext: false) </div> @ renderpage ("index/_ teams. cshtml ", model. subitems (). where (I => I is team ). cast <team> (), false)
The <div> above shows the new button in the figure at the beginning of the article. The renderpage below shows all existing teams on the right.
Which of the following parameters need to be parameterized? All parameters with department, team,/site/teams/... must be parameterized. For details, refer to the following results.
New View (views/items/itemhorizentallist. cshtml ):
VaR root = pagedata [0] as item; var subitems = pagedata [1] As ienumerable <item>; <Div class = "item-hierarchy @ mfcui. hovertwinkletriggerbodyclass ("Create-New-Team") "style =" float: Left; margin-Right: 16px; Min-Height: 10px; Min-width: 10px; "> <Div class =" item-Hierarchy-Body "> @ mfcui. imagelink ("new", "/mfc/items/create? Rootid = "+ root. ID + "& redirecttoaftercreated =" + httputility. urlencode (redirectaftercreate), imgurl: "/mfc/items/create48.png", showtext: false) </div> @ renderpage ("itemhorizentallist/_ subitems. cshtml ", subitems, false)
A bunch of VaR mentioned above split the mfcdefaultview parameter mentioned in 1. The middle Div and the following renderpage are modified according to the parameter.
The specific parameters are determined by how to convert the operations for the team into general items.
3. Adjust the partial view called by the new view
Then, adjust _ subteams. cshtml to _ subitems. cshtml, and change _ team. cshtml to _ subitem. cshtml.
Because both product and team are derived from item, you can operate as an item.
Click it to refresh the page to see the effect. Gradually, the interface is normal:
4. Use this framework to implement the product page:
C # The code is just one line in productscontroller. CS:
public ActionResult Index(int? productLineID) { return MFCDefaultView(productLineID ?? _repMFC.ReadAllItems<ProductLine>().First(i => i.Type == ItemWhattype.ProductTypeProductLine).ID); }
There is only one view, that is, different parts are displayed between the Team and the product, and the next one is written.
Residual problems
But at last, we will encounter several key issues:
1. The team should display members, and the product should display other items (tentatively the latest release ).
The index actually does not know this, because it only receives the item, and there is no Member or release information in this base class.
2. After creating a new team, you must switch to the member assignment (/teammembers/Index). After creating a new product, you must switch to the creation and release (/products/releaseschedule ).
3. Which actions need to be written into the view, and then the renderaction (such as the teams/index) in the view; which operations do not need to be written into the view and redirecttoaction to the itemscontroller directly in the action of the controller? (This is simple. You don't need to thank the view, but the restriction is that the URL will jump )?
4. What is the relationship between these encoding methods and loose Pair programming?
The solution to these two problems will be written in the next article.