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

简单类型排序

编程时遇到排序在平常不过,使用.Net最常见的就是对泛型List<T>进行排序,如果T是简单数据类型排序那么很简单

代码如下:

public List<int> SortSimpleList(List<int> list)
{
list.Sort();
return list;
}

同样对string等简单类型List<T>排序均如此,如果我们要排的对象复杂了怎么办,我们知道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 mparer_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));
}
}

看看效果

.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.)

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

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

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

使用场景

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

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

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

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

策略模式的优势和不足

优点:

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

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

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

缺点:

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

(0)

相关推荐

  • C#策略模式(Strategy Pattern)实例教程

    本文以一个简单的实例来说明C#策略模式的实现方法,分享给大家供大家参考.具体实现方法如下: 一般来说,当一个动作有多种实现方法,在实际使用时,需要根据不同情况选择某个方法执行动作,就可以考虑使用策略模式. 把动作抽象成接口,比如把玩球抽象成接口.代码如下: public interface IBall { void Play(); } 有可能是玩足球.篮球.排球等,把这些球类抽象成实现接口的类.分别如下: public class Football : IBall { public void P

  • C++设计模式之策略模式

    前言 刚刚加班回来:哎,公司规定平时加班只有10块钱的餐补:星期六和星期天加班,只给串休假:在国家规定的节假日按照3倍工资发放.那么对于这么多的计算加班费的方法,公司的OA系统是如何进行做的呢?这就要说到今天我这里总结的策略设计模式了. 策略模式 在GOF的<设计模式:可复用面向对象软件的基础>一书中对策略模式是这样说的:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.该模式使得算法可独立于使用它的客户而变化. 策略模式为了适应不同的需求,只把变化点封装了,这个变化点就是实现不同

  • 浅析.net策略模式

    对于策略模式的理解:当一个业务有多种需求时候,在某个时候需要使用不同的方式来计算结果.这时候不同的方式可以理解为不同的策略来解决同样的问题. 例如:商场收银系统计算价格,1:正常计算 2:商品打折计算,3:满300减100等方式.就可以按三种策略来处理需求. 简单的说:策略模式就是用来封装算法的,但在实践中,我们发现可以用他来封装几乎任何类型的规则,只要在分析过程中听到需要在不同的时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性. 复制代码 代码如下: using System

  • java实现策略模式使用示例

    思路如下: 使用interface来定义一个接口,在该接口中定义save()方法:根据图片格式定义不同的类,分别在这些类中使用关键字implements实现接口:创建一个实现选择的类,在该类中定义实现选择的方法,该方法返回值为对应的图片保存类:在主方法中实现接口.代码如下: 复制代码 代码如下: public interface ImageSaver {    void save();//定义save()方法} public class GIFSaver implements ImageSave

  • php设计模式 Strategy(策略模式)

    抽象策略(Strategy)角色:定义所有支持的算法的公共接口.通常是以一个接口或抽象来实现.Context使用这个接口来调用其ConcreteStrategy定义的算法. 具体策略(ConcreteStrategy)角色:以Strategy接口实现某具体算法. 环境(Context)角色:持有一个Strategy类的引用,用一个ConcreteStrategy对象来配置 核心代码 <?php interface Strategy { // 抽象策略角色,以接口实现 public functio

  • .NET 下运用策略模式(组合行为和实体的一种模式)

    我简单的理解策略模式就是把行为(方法)单独的抽象出来,并采用组合(Has-a)的方式,来组合行为和实体的一种模式.再来个官方的解释: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. 网上也有很多资源介绍这个模式,我也不从头说起了.在.

  • 关于.NET Framework中的设计模式--应用策略模式为List排序

    简单类型排序 编程时遇到排序在平常不过,使用.Net最常见的就是对泛型List<T>进行排序,如果T是简单数据类型排序那么很简单 复制代码 代码如下: public List<int> SortSimpleList(List<int> list){list.Sort();return list;} 同样对string等简单类型List<T>排序均如此,如果我们要排的对象复杂了怎么办,我们知道List<T> sort()最后是用快速排序实现,快速排

  • Python设计模式中的行为型策略模式

    目录 一.策略模式 二.应用场景 三.代码示例 一.策略模式 策略模式中,首先定义了一系列不同的算法,并把它们一一封装起来,然后在策略类中,使这些算法可以相互替换.这意味着,让一个类的行为(算法)可以在类的实例化对象运行时进行更改. 优点: 定义了一系列可重用的算法和行为. 消除了一些条件语句. 可以提供相同行为的不同实现. 缺点: Client 必须了解不同的策略行为细节. 二.应用场景 根据不同的客户属性,采用不同的折扣策略来计算订单中的商品价格: 有 1000 或以上积分的客户,每个订单享

  • .Net行为型设计模式之策略模式(Stragety)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.策略模式的代码实现 六.策略模式的实现要点: 1.策略模式的主要优点有: 2.策略模式的主要缺点有: 3.在下面的情况下可以考虑使用策略模式: 七..NET 策略模式的实现 一.动机(Motivate) 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂:而且有时候支持不使用的算法也是一个性能负担.如何在运行时根据需要透

  • Java经典设计模式之策略模式原理与用法详解

    本文实例讲述了Java经典设计模式之策略模式.分享给大家供大家参考,具体如下: 策略模式指:策略模式指将程序中可变部分抽象分离成一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 策略模式一般由下面三部分组成: 1. 抽象策略角色: 策略类,通常由一个接口或者抽象类实现. 2. 具体策略角色:包装了相关的算法和行为. 3. 环境角色:持有某一个策略类的引用,客户端调用. 策略模式设计原则: 1. 把程序中需要变化的部分抽离出来,独立于不变

  • Java设计模式之策略模式_动力节点Java学院整理

    定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换. 类型:行为类模式 类图: 策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换.在前面说过的行为类模式中,有一种模式也是关注对算法的封装--模版方法模式,对照类图可以看到,策略模式与模版方法模式的区别仅仅是多了一个单独的封装类Context,它与模版方法模式的区别在于:在模版方法模式中,调用算法的主体在抽象的父类中,而在策略模式中,调用算法的主体则是封装到了封装类Context中

  • Android编程设计模式之策略模式详解

    本文实例讲述了Android编程设计模式之策略模式.分享给大家供大家参考,具体如下: 一.介绍 在软件开发中也常常遇到这样的情况:实现某一个功能可以有多种算法或者策略,我们根据实际情况选择不同的算法或者策略来完成该功能.例如,排序算法,可以使用插入排序.归并排序.冒泡排序等. 针对这种情况,一种常规的方法是将多种算法写在一个类中.例如,需要提供多种排序算法,可以将这些算法写到一个类中,每一个方法对应一个具体的排序算法:当然,也可以将这些排序算法封装在一个统一的方法中,通过if-else-或者ca

  • Java设计模式之策略模式原理与用法实例详解

    本文实例讲述了Java设计模式之策略模式原理与用法.分享给大家供大家参考,具体如下: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化.其中JDK里面的TreeSet类和TreeMap类就用到了策略模式.这两个类是带排序的集合类,其中排序的规则就相当于策略模式里定义的一系列算法,而集合类就相当于是策略模式里的环境类,供用户使用,用只知道TreeSet和TreeMap是带排序的,至于怎么排序的,是由排序的算法决定的. 策略模式

  • Java设计模式之策略模式定义与用法详解

    本文实例讲述了Java策略模式定义与用法.分享给大家供大家参考,具体如下: 一. 定义: 定义一系列算法,把他们一个一个封装起来,并且使他们可以相互替换. 二. 优点: (1)上下文(Context)和具体策略(ConcreteStrategy)是松耦合关系,因此上下文只需要知道他要使用某一个实现  Strategy接口类的实例,但不需要知道是哪个类. (2)策略模式满足开闭原则,当增加新的具体类时,不需要修改上下文类的代码,上下文即可以引用新的具体策略的实例. 三. 实例: 下面就通过一个问题

  • PHP设计模式之策略模式原理与用法实例分析

    本文实例讲述了PHP设计模式之策略模式原理与用法.分享给大家供大家参考,具体如下: 策略模式(Strategy Pattern) 策略模式是对象的行为模式,用意是对一组算法的封装.动态的选择需要的算法并使用. 策略模式指的是程序中涉及决策控制的一种模式.策略模式功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性思想. 策略模式的三个角色: 1. 抽象策略角色 2. 具体策略角色 3. 环境角色(对抽象策略角色的引用) 实现步骤: 1. 定义抽象角色类(定义好各个实现的共同抽象方

  • PHP设计模式之策略模式(Strategy)入门与应用案例详解

    本文实例讲述了PHP设计模式之策略模式(Strategy)入门与应用.分享给大家供大家参考,具体如下: 这个策略模式,意思就是定义一系列算法,把它们一个个封装起来,并且使它们可相互替换,使用得算法的变化可独立于使用它的客户,简单来讲就是,策略模式设计帮助构建的对象不必自身包含逻辑,而是能够根据需要利用其他对象中的算法. 来看下应用场景: 1. 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为. 2. 需要在不同情况下使用不同的策略(算法),或者策略还可能

随机推荐