.NET Framework 中的设计模式——应用策略模式为List排序

简介:

简单类型排序

编程时遇到排序在平常不过,使用.Net最常见的就是对泛型List<T>进行排序,如果T是简单数据类型排序那么很简单,直接调用List的Sort()方法就可以了,但是如果我们要排的对象复杂了怎么办,我们知道List<T> sort()最后是用快速排序实现,快速排序也好,什么排序都需要知道list中item之间的比较结果,如果是简单的int类型,直接判断即可,对实现了IComparable接口的对象,可以调用其CompareTo()实现item比较大小,下面是一个快速排序的写法

复制代码
void Sort<T>(T[] array, int left, int right, IComparer_sly<T> comparer) where T : IComparable
        {
            if (left < right)
            {
                T middle = array[(left + right) / 2];
                int i = left - 1;
                int j = right + 1;
                while (true)
                {
                    while (array[++i].CompareTo(middle) < 0) ;

                    while (array[--j].CompareTo(middle) > 0) ;

                    if (i >= j)
                        break;

                    T temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }

                Sort(array, left, i - 1, comparer);
                Sort(array, j + 1, right, comparer);
            }
        }
复制代码

 

 

 

问题

对于前两种情况固然可以实现排序,但是我们不可能要求所有待排序的对象都实现IComparable接口,就算能够保证每个对象都实现IComparable接口,如果想实现对象内多个字段排序,比如Student对象,有时候想按照姓名排序,有时候是成绩,有时候是年龄,这怎么破

按照面向对象的思想,要把变化独立出来,封装变化,对于我们排序List<T>时变化的其实就是怎么比较两个对象的大小的算法,如果我们可以把这个算法拿出来,排序就简单了很多,无论什么排序,算法都是由的,我们要封装的部分是怎样比较两个item的大小的算法,为了实现拓展性我们要遵循面向对象设计的另外一个重要原则,针对接口编程,而不是针对实现编程。

编写通用的List<T>排序方法

首先定义一个接口,里面有一个比较item大小的方法,在排序的时候作为参数传入,当然是传入它的实现类,有了这个想法,我们可以自己写个List<T>的排序方法

public interface IComparer_sly<T>
{
        int Compare(T x, T y);
}

 

 

然后为了测试,我们为List<T>加一个包装,写一个自己的Sort方法,内部也用快速排序实现。一直困惑我们的变化部分——比较大小算法,我们把它封转起来,作为参数传入

复制代码
using System;
using System.Collections.Generic;

namespace Test.Stategy
{public class ListTest<T>
    {
        public List<T> list = new List<T>();
        public void Sort(IComparer_sly<T> comparer)
        {
            T[] array = list.ToArray();
            int left = 0;
            int right = array.Length - 1;
            QuickSort(array, left, right, comparer);
            list = new List<T>(array);
        }

        private void QuickSort<S>(S[] array, int left, int right, IComparer_sly<S> comparer)
        {
            if (left < right)
            {
                S middle = array[(left + right) / 2];
                int i = left - 1;
                int j = right + 1;
                while (true)
                {
                    while (comparer.Compare(array[++i], middle) < 0) ;

                    while (comparer.Compare(array[--j], middle) > 0) ;

                    if (i >= j)
                        break;

                    S temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }

                QuickSort(array, left, i - 1, comparer);
                QuickSort(array, j + 1, right, comparer);
            }
        }
    }
}
复制代码

比如现在我们有个Student 的实体

复制代码
public class Student
    {
        public Student(int id, string name)
        {
            this.ID = id;
            this.Name = name;
        }
        public int ID { get; set; }
        public string Name { get; set; }
    }
复制代码

如果想对这个实体组成的List<T>进行排序,我们只需一个实现 IComparer_sly<Student>的类 StudentComparer,并在内部实现其比较大小方法——Compare(),同时我们可以添加递增还是递减排序的控制

复制代码
class StudentComparer : IComparer_sly<Student>
    {
        private string expression;
        private bool isAscending;
        public StudentComparer(string expression, bool isAscending)
        {
            this.expression = expression;
            this.isAscending = isAscending;
        }

        public int Compare(Student x, Student y)
        {
            object v1 = GetValue(x), v2 = GetValue(y);
            if (v1 is string || v2 is string)
            {
                string s1 = ((v1 == null) ? "" : v1.ToString().Trim());
                string s2 = ((v2 == null) ? "" : v2.ToString().Trim());
                if (s1.Length == 0 && s2.Length == 0)
                    return 0;
                else if (s2.Length == 0)
                    return -1;
                else if (s1.Length == 0)
                    return 1;
            }

            // 这里就偷懒调用系统方法,不自己实现了,其实就是比较两个任意相同类型数据大小,自己实现比较麻烦
            if (!isAscending)
                return Comparer.Default.Compare(v2, v1);
            return Comparer.Default.Compare(v1, v2);
        }

        private object GetValue(Student stu)
        {
            object v = null;
            switch (expression)
            {
                case "id":
                    v = stu.ID;
                    break;
                case "name":
                    v = stu.Name;
                    break;
                default:
                    v = null;
                    break;
            }
            return v;
        }
    }
复制代码

测试一下好不好使

复制代码
static void Main(string[] args)
        {
            ListTest<Student> test = new ListTest<Student>();
            for (int i = 0; i < 10; i++)
            {
                Student stu = new Student(i,string.Format("N_"+(9-i)));
                test.list.Add(stu);
            }
            Console.WriteLine("元数据");
            for (int i = 0; i < test.list.Count;i++ )
            {
                Console.WriteLine(string.Format("ID:{0} , Name:{1}", test.list[i].ID, test.list[i].Name));
            }

            Console.WriteLine("Name 递增");
            test.Sort(new StudentComparer("name", true));
            for (int i = 0; i < test.list.Count; i++)
            {
                Console.WriteLine(string.Format("ID:{0} , Name:{1}", test.list[i].ID, test.list[i].Name));
            }
        }
复制代码

看看效果

Capture

 

 

.NET List的sort如何为我们排序

用ILSpy反编译可以看到在调用List<T>的sort()方法时内部调用的时 this.Sort(0, this.Count, null); 然后往里面扒,经过一系列异常处理后会调用 Array.Sort<T>(this._items, index, count, comparer); this._items是把List内容转换成数组,同样再经历一些列异常处理,调用方法 ArraySortHelper<T>.Default.Sort(array, index, length, comparer); 再往里就和我们上面写的方法大同小异了,只不过微软加了很多异常处理和算法优化。

策略模式

看清楚了上面这个例子我们就可以进入正题,说说我们的策略模式了。策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。(原文:The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)

5366d0160924ab189a9f061935fae6cd7b890b16

这个模式涉及到三个角色:

  • 环境(Context)角色:持有一个Strategy类的引用。
  • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

相信大家可以分方便的把我们上面例子中的类对应上策略模式的角色,IComparer接口是我们的抽象策略角色, ListTest<T> 类持有抽象策略的引用是环境(在Sort方法中,其实可以把接口定义为类的属性,在构造函数中赋值,不过不适合此场景,毕竟并不是所有List都需要排序,不能强制其接受一个可能会用不到的接口,当然对每个实例都需要用某个策略的场景是合适的),毫无疑问我们实现IComparer抽象策略的类就是具体策略。

使用场景

策略模式很容易理解,不过能够用它很好的理解封装变化和针对接口编程者两个面向对象设计原则,我们来看看什么时候我们会用策略模式

1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。

2、 需要在不同情况下使用不同的策略(算法),这些策略有统一接口。

3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

策略模式的优势和不足

优点:

1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。

2、 使用组合,避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。

3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。

缺点:

1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。

参考

1.http://baike.baidu.com/view/2141079.htm

2.http://www.cnblogs.com/zhenyulu/articles/82017.html

3.Head First设计模式

希望这个系列能够善始善终,写了个目录激励自己,也方便查找 .NET Framework 中的设计模式


   本文转自魏琼东博客园博客,原文链接:http://www.cnblogs.com/dolphinX/archive/2013/04/12/3005885.html,如需转载请自行联系原作者


相关文章
|
2月前
|
API C++ Windows
Visual C++运行库、.NET Framework和DirectX运行库的作用及常见问题解决方案,涵盖MSVCP140.dll丢失、0xc000007b错误等典型故障的修复方法
本文介绍Visual C++运行库、.NET Framework和DirectX运行库的作用及常见问题解决方案,涵盖MSVCP140.dll丢失、0xc000007b错误等典型故障的修复方法,提供官方下载链接与系统修复工具使用指南。
540 2
|
2月前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
276 35
|
5月前
|
C++ Windows
.NET Framework安装不成功,下载`NET Framework 3.5`文件,Microsoft Visual C++
.NET Framework常见问题及解决方案汇总,涵盖缺失组件、安装失败、错误代码等,提供多种修复方法,包括全能王DLL修复工具、微软官方运行库及命令行安装等,适用于Windows系统,解决应用程序无法运行问题。
356 3
|
2月前
|
开发框架 安全 .NET
Microsoft .NET Framework 3.5、4.5.2、4.8.1,适用于 Windows 版本的 .NET,Microsoft C Runtime等下载
.NET Framework是Windows平台的开发框架,包含CLR和FCL,支持多种语言开发桌面、Web应用。常用版本有3.5、4.5.2、4.8.1,系统可同时安装多个版本,确保软件兼容运行。
582 0
Microsoft .NET Framework 3.5、4.5.2、4.8.1,适用于 Windows 版本的 .NET,Microsoft C Runtime等下载
|
3月前
|
设计模式 人工智能 算法
基于多设计模式的状态扭转设计:策略模式与责任链模式的实战应用
接下来,我会结合实战案例,聊聊如何用「策略模式 + 责任链模式」构建灵活可扩展的状态引擎,让抽奖系统的状态管理从「混乱战场」变成「有序流水线」。
|
3月前
|
C++
提示缺少.NET Framework 3.5 安装错误:0x80070002、0x800F0950\0x80004002
.NET Framework常见问题及解决方法汇总,
475 0
|
4月前
.NET Framework 3.5离线安装包合集下载
本文介绍了如何获取和安装.NET Framework运行库离线合集包。用户可通过提供的链接下载安装包,安装过程简单,按提示逐步操作即可完成。安装时可选择所需版本,工具会自动适配架构,无需手动判断,方便高效。
1639 0
|
安全 Java
java线程之List集合并发安全问题及解决方案
java线程之List集合并发安全问题及解决方案
1348 1
|
运维 关系型数据库 Java
PolarDB产品使用问题之使用List或Range分区表时,Java代码是否需要进行改动
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。

热门文章

最新文章