

Real-World Refactoring: .Sort() vs.OrderBy()
source link: https://www.tuicool.com/articles/hit/jQ3qieU
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.


Over the weekend, I was updating my TableTreeView which uses a combination of C#, JavaScript, and SignalR for the dynamic loading of TreeViewItems.
The way the TableTreeView works is you pass in a List<T>
and the TableTreeView wraps code around the list.
Think of each TableTreeItem as a header record (master) wrapping the single entity (detail) where it's similar to a master/detail relationship.
public class TableTreeItem<T> where T: class { public int Id { get;set; } public int Order { get;set; } public T Item { get; set; } public int? ParentId { get;set; } public int Level { get;set; } public bool HasChildren { get; set; } public bool IsRoot { get; set; } public bool Expanded { get; set; } }
While I was examining and refactoring the C# code, I noticed the sorting routine was hard-coded. It was sorting the list by ID using the LINQ method OrderBy().
Yuck. Ughh. Pfffffftt.
Why do you think this bothered me?
What if I wanted to sort by something different? Maybe title or even item type (folder or item)? Maybe both?
What's Wrong With The Code?
The current code base uses a fluent API (similar to LINQ), but there isn't anything available for the sorting.
This was the line of code causing me to have a tick.
var records = _results.OrderBy(e => e.Id);
If it's only going to be used once, there isn't a problem.
If I'm using this TableTreeView for multiple projects (which I am), you want this to be easy for the developer (and that includes me!).
Based on the SOLID principles, this looks like a job for "O" ( Open/Closed principle ). This means Open for extension, Closed for modification. If we wanted to keep this code, it would violate the Open/Closed Principle because we would have to go into the class and change the LINQ's OrderBy extension.
It would be easier to pass in some kind of sort routine.
Now, since this is a tool I'm using heavily in numerous places throughout a personal project, this type of sorting with the OrderBy wasn't going to work for me. It HAS to be sortable on a number of levels.
So, how can we sort out this mess (pun intended) and make it more extensible?
Time To Refactor!
The first thing was to make it easier to pass in a sort routine.
One way is to use LINQ. We can pass in a predicate like using a Repository pattern with a Where clause similar to:
public IEnumerable<Project> GetByCustomer(Expression<Func<Project, bool>> projectFilter) { return Find(projectFilter); }
We could pass in a sort predicate.
var treeView = new MvcTableTree<ProductCategory>(items) .SortBy(treeViewItem => treeViewItem.Item.Title) .Build();
Our MvcTableTree code would have this method.
private Func<TableTreeItem<T>, string> _sortRoutine; public MvcTableTree<T> SortBy(Func<TableTreeItem<T>, string> sortRoutine) { _sortRoutine = sortRoutine; return this; }
When the time comes to "Build()" our TreeView, our sort inside our Build method would be as easy as:
var records = _results.OrderBy(_sortRoutine);
Now we have an open way to sort our TreeViewItems.
Compound Sorting
Once I put this code in place, I continued to work on the project.
Not even an hour later, I found a place in the project where I needed a deeper way to sort the TreeViewItems.
I had to sort the Item by two keys, HasChildren (descending) and Title. The reason to sort by HasChildren as descending was to display the folders first and then sort the titles.
The single predicate for the LINQ OrderBy would not work.
Of course, we could always do a workaround by passing in a concatenated string of 0 for an item and greater than 0 for a folder and concatenate the title on the end like this:
var treeView = new MvcTableTree<ProductCategory>(items) .SortBy(treeViewItem => (treeViewItem.HasChildren ? 1.ToString() : 0.ToString())+treeViewItem.Item.Title) .Build();
but this just feels too hacky.
We need a better way.
You Comparer? No, IComparer
One of the overloads of the .Sort()
method includes an IComparer interface.
This IComparer interface has one method signature called Compare.
The best part about this interface is you can write as many sorting objects as you need by creating a Bridge pattern and passing them in to perform any type of sorting imaginable.
First, we need to modify our Builder pattern to accept a different kind of Sort parameter.
var treeView = new MvcTableTree<ProductCategory>(items) .SortBy(new SortByHierarchy<ProductCategory>()) .Build();
Our new .SortBy()
method takes a different parameter in our MvcTableTree code.
private IComparer<TableTreeItem<T>> _sortFunc; public MvcTableTree<T> SortBy(IComparer<TableTreeItem<T>> sortRoutine) { _sortFunc = sortRoutine; return this; }
All we need to do now is create our Sort class called SortByHierarchy()
.
public class SortByHierarchy<T> : IComparer<TableTreeItem<T>> where T: class { public int Compare(TableTreeItem<T> x, TableTreeItem<T> y) { var cat1 = x.Item as ProductCategory; var cat2 = y.Item as ProductCategory; return cat1.ProductCategoryId.CompareTo(cat2.ProductCategoryId); } }
If you need to pass in a different sorting class, your Compare method would be different when building your MvcTableTree.
Conclusion
There are many ways to write sorting code, but I feel this technique embodies the "O" in SOLID.
Don't get me wrong, the .OrderBy()
clause in LINQ has it's place, yet it doesn't feel open enough.
With this solution, you are free to create any type of sort algorithm using a class and being able to pass it into the code without digging into the MvcTableTree class to modify it.
Open for Extension, Closed for Modification.
Was this an easy refactoring? Is there a better way to use LINQ's .OrderBy()
? Could you extend .ThenBy()
? Thoughts? Post your comments below and let's discuss.
Recommend
-
153
VIM Php Refactoring Toolbox PHP Refactoring Toolbox for VIM Rename Local Variable Rename Class Variable Rename Method Extract Use Extract Const Extract Class Property Extr...
-
174
js2-refactor.el A JavaScript refactoring library for emacs. This is a collection of small refactoring functions to further the idea of a JavaScript IDE in Emacs that started with js2-mode. Change in 0.8.0 Add
-
129
982 lines added, 20 309 lines removed — original PR...
-
4
Json Function Documentation • Changelog Lets you use where, limit, select, orderBy, and more in JSON data.
-
5
Real-World Refactoring: Available Cars Are all of those if..then statements really necessary? In today's post, we look at refactoring a long list of if..then statements into an easy two-liner. Written by Jonathan "JD" Dan...
-
6
How to get records orderBy in Laravel relationship 160 views 2 months ago Laravel When you implement Lara...
-
9
Real-World Refactoring: Complex LINQ-ing LINQ is so powerful, you can create a one-liner LINQ statement to create a report. In today's post, we put LINQ to the test by refactoring a user's piece of code Written by Jonath...
-
4
「译」LINQ: Building an IQueryable Provider - Part VIII: OrderBy2016-02-27 | 刘文俊英文原文是Matt Warren发表在MSDN Blogs的系列文...
-
3
Mysql index及 orderby 优化 前言:优化是指通过建立合理的索引来更快地查询出结果,由于优化会增加索引文件,所以会增加额外的磁盘消耗;而且在增删改的时候,由于数据发生变化,索引也会相应的发生变化,所以会增加额外的消耗。 单列索引...
-
9
Learn how to use the orderBy pipe to manipulate data in templates to display them in the format we want.Angular is a component-based framework that lets us create interactive web frontends for users by composing components together. In addi...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK