2011-10-16 28 views
1

这种设计模式有很大的意义吗?我原来有一个静态类,它为每个实现的算法返回一个HashFunction有没有更好的方法来伪造静态类接口?

public delegate int HashFunction(int seed, params int[] keys); 

但后来我意识到,我想几件的元数据与每个算法一起,所以我创造了这个接口:

public interface IHashAlgorithm 
{ 
    HashFunction CalculateHash { get; } 
    NoiseFunction CalculateNoise { get; } 
    int Maximum { get; } 
    int Minimum { get; } 
} 

一个内部类实现所需的接口:

public delegate double NoiseFunction(int seed, params int[] keys); 

internal sealed class HashAlgorithm : IHashAlgorithm 
{ 
    public HashAlgorithm(HashFunction function, int min, int max) 
    { 
     CalculateHash = function; 
     Minimum = min; 
     Maximum = max; 
    } 

    public HashFunction CalculateHash { get; private set; } 

    public NoiseFunction CalculateNoise 
    { 
     get { return Noise; } 
    } 

    public int Maximum { get; private set; } 
    public int Minimum { get; private set; } 

    private double Noise(int seed, params int[] keys) 
    { 
     return ((double)CalculateHash(seed, keys) - Minimum)/ 
      ((double)Maximum - Minimum + 1); 
    } 
} 

创建并返回一种公共静态工厂类:

public static class Hashing 
{ 
    private static readonly IHashAlgorithm MurmurHash2Instance = 
     new HashAlgorithm(MurmurHash2Hash, 0, int.MaxValue); 

    private static readonly IHashAlgorithm ReSharperInstance = 
     new HashAlgorithm(ReSharperHash, int.MinValue, int.MaxValue); 

    public static IHashAlgorithm MurmurHash2 
    { 
     get { return MurmurHash2Instance; } 
    } 

    public static IHashAlgorithm ReSharper 
    { 
     get { return ReSharperInstance; } 
    } 

    private static int MurmurHash2Hash(int seed, params int[] keys) 
    { 
     //... 
    } 

    private static int ReSharperHash(int seed, params int[] keys) 
    { 
     //... 
    } 
} 

我宁愿能够在静态类每种算法实现IHashAlgorithm

public static class MurmurHash2 : IHashAlgorithm 
{ 
    public static int Hash(int seed, params int[] keys) {...} 

    //... 
} 

不幸的是C#不允许这样,所以这是我在它周围越来越尝试。

+0

我想你应该使用make一个singleton,如果你需要模拟“静态接口”,这看起来像你在做什么,但以一种奇怪的方式。 – Candide

+0

Ingenu是正确的 - 单身是唯一的方法来做到这一点。如果您想严格控制API,您当然可以选择明确的接口实现。 –

回答

2

有没有办法伪造静态类接口,并且很多时候我认为我需要一个我实际上需要常用的实例接口。你不能在C#中传递一个静态类的“实例”,没有办法给一个函数一个“静态”接口,甚至没有静态的“类”来使用它的静态方法。当你调用一个静态方法时,它总是显式的,你将你的方法“硬连接”到你调用的静态类上,这不是一件好事。

基于静态方法的可变性很难进行单元测试。取决于这种变化的类不那么灵活。想象一下,如果某个函数明确地从你的静态类中使用你的算法之一。这样的功能将明确地将其自身耦合到该特定算法。

public class SomeBusinessLogic 
{ 
    public Result HandleDocument(IDocument doc) 
    { 
     // some transformations... 

     int hash = Hashing.ReSharperHash.CalculateHash(seed, doc.Properties); 

     // some other code ... 
    } 
} 

那么,这是怎么回事?

  1. 该类从不显式声明它依赖于散列。你需要知道它的实现来推理。在这种情况下,它可能不是很重要,但如果其中一个哈希算法非常慢,该怎么办?或者如果它需要磁盘上的一些外部文件?或者,如果它连接到一些外部哈希服务?当您调用HandleDocument函数时,它可能会意外失败。

  2. 如果您想对特定文档使用其他散列算法,则无需更改代码即可执行此操作。

  3. 当你单元测试它时,你需要测试文档处理逻辑和哈希逻辑(它应该已经通过自己的单元测试进行测试)。如果您的测试将输出Result与来自某个资源的某个值进行比较,并且它包含散列值,那么当您将该函数更改为另一个散列算法时,此函数的所有单元测试都将中断。

什么是更好的方法?提取一个抽象你的哈希函数的接口,并在需要做哈希时明确地询问它。因此,只要它们是无状态的,您仍然可以将算法实现为某种单例,但客户端代码无论如何都无法与散列细节进行耦合。谁知道,你可能有一天会发现你需要一些参数化的散列算法,并且你可以在每次需要时创建一个新的算法实例。

我使用你的接口有点改变风格:

public interface IHashAlgorithm 
{ 
    int CalculateHash(int seed, params int[] keys); 
    int CalculateNoise(int seed, params int[] keys); 
    int Maximum { get; } 
    int Minimum { get; } 
} 

public static class StatelessHashAlgorithms 
{ 
    private static readonly IHashAlgorithm MurmurHash2Instance = 
     new HashAlgorithm(MurmurHash2Hash, 0, int.MaxValue); 

    private static readonly IHashAlgorithm ReSharperInstance = 
     new HashAlgorithm(ReSharperHash, int.MinValue, int.MaxValue); 

    public static IHashAlgorithm MurmurHash2 
    { 
     get { return MurmurHash2Instance; } 
    } 

    public static IHashAlgorithm ReSharper 
    { 
     get { return ReSharperInstance; } 
    } 

    private static int MurmurHash2Hash(int seed, params int[] keys) 
    { 
     //... 
    } 

    private static int ReSharperHash(int seed, params int[] keys) 
    { 
     //... 
    } 
} 

public class SomeCustomHashing : IHashAlgorithm 
{ 
    public SomeCustomHashing(parameters) 
    { 
     //parameters define how hashing behaves 
    } 

    // ... implement IHashAlgorithm here 
} 

所有的客户端代码应该要求散列接口明确,当它需要一个,它被称为依赖注入,并且可以在一个类来完成级别或方法级别。然后该类的调用者或创建者将负责提供散列算法。

public class SomeBusinessLogic 
{ 
    // injection in constructor 
    public SomeBusinessLogic(IHashingAlgorithm hashing) 
    { 
     // put hashing in a field of the class 
    } 

    // OR injection in method itself, if hashing is only used in this method 
    public Result HandleDocument(IDocument doc, IHashingAlgorithm hashing) 
    { 
     // some transformations... 

     int hash = hashing.CalculateHash(seed, doc.Properties); 

     // some other code ... 
    } 
} 

它解决了上述问题:

  1. 类明确声明它取决于散列。提供特定哈希算法的人知道对性能,资源,连接,异常等有何期望。

  2. 您可以配置针对业务逻辑或每个文档的每个实例使用哪种哈希算法。

  3. 当你单元测试它时,你可以提供哈希的模拟实现,例如总是返回0(并且检查该方法是否将期望值传递给哈希算法)。这是你分别检查哈希和分别检查文件处理。

所以底线是,如果你有一些行为变化 - 使用标准的实例接口。它们后面的代码可以是静态的也可以是非静态的,没关系。重要的是,使用变量行为的地方将保持灵活性,可扩展性和单元可测试性。

P.S. “你的领域是什么”还有一个方面。如果你正在编写一些业务应用程序,并且你在这里和那里调用静态的Math.Sqrt(...)--这很好,因为没有其他行为。但是,如果你正在编写一些数学库,并且你有几个不同的算法或准确度的平方根实现,你可能想把它们包装到一个接口中,并作为接口的实例传递,以便能够扩展。

相关问题