.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,如需转载请自行联系原作者


相关文章
|
5天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
9天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
30 2
|
17天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
13 1
|
18天前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
25天前
|
开发框架 缓存 监控
NET Framework 到 .NET 5/6 的迁移是重大的升级
本文详细介绍了从 .NET Framework 4.8 迁移到 .NET 5/6 的过程,通过具体案例分析了迁移策略与最佳实践,包括技术栈评估、代码迁移、依赖项更新及数据库访问层的调整,强调了分阶段迁移、保持代码可维护性及性能监控的重要性。
46 3
|
30天前
|
设计模式 PHP 开发者
PHP中的设计模式:桥接模式的解析与应用
在软件开发的浩瀚海洋中,设计模式如同灯塔一般,为开发者们指引方向。本文将深入探讨PHP中的一种重要设计模式——桥接模式。桥接模式巧妙地将抽象与实现分离,通过封装一个抽象的接口,使得实现和抽象可以独立变化。本文将阐述桥接模式的定义、结构、优缺点及其应用场景,并通过具体的PHP示例代码展示如何在实际项目中灵活运用这一设计模式。让我们一起走进桥接模式的世界,感受它的魅力所在。
|
28天前
|
设计模式 测试技术 持续交付
架构视角下的NHibernate:设计模式与企业级应用考量
【10月更文挑战第13天】随着软件开发向更复杂、更大规模的应用转变,数据访问层的设计变得尤为重要。NHibernate作为一个成熟的对象关系映射(ORM)框架,为企业级.NET应用程序提供了强大的支持。本文旨在为有一定经验的开发者提供一个全面的指南,介绍如何在架构层面有效地使用NHibernate,并结合领域驱动设计(DDD)原则来构建既强大又易于维护的数据层。
37 2
|
30天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文通过游泳运动员的案例,介绍策略模式及其在Kotlin中的改良应用,利用高阶函数简化代码结构,提高灵活性。
30 3
|
30天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文介绍策略模式在Kotlin中的应用,通过游泳运动员的例子,展示如何使用接口和高阶函数实现策略模式,使代码更简洁、灵活。
28 2
|
1月前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
61 3

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    53
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    37
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    61
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    56
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    40
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    49
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    105
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    75