继承类上的C#单例模式

继承类上的C#单例模式,c#,design-patterns,inheritance,singleton,C#,Design Patterns,Inheritance,Singleton,我首先要为这篇文章的篇幅道歉。为了给大家节省一些时间,我的问题是我脑子里的类模式显然有缺陷,我看不到一个好的解决方案 在我正在从事的一个项目中,我需要对数据块使用运算算法,让我们称之为DataCache。有时这些算法返回的结果本身需要缓存,所以我设计了一个方案 我有一个算法基类,看起来是这样的 abstract class Algorithm<T> { protected abstract T ExecuteAlgorithmLogic(DataCache d

我首先要为这篇文章的篇幅道歉。为了给大家节省一些时间,我的问题是我脑子里的类模式显然有缺陷,我看不到一个好的解决方案

在我正在从事的一个项目中,我需要对数据块使用运算算法,让我们称之为
DataCache
。有时这些算法返回的结果本身需要缓存,所以我设计了一个方案

我有一个算法基类,看起来是这样的

abstract class Algorithm<T>
    {
        protected abstract T ExecuteAlgorithmLogic(DataCache dataCache);
        private readonly Dictionary<DataCache, WeakReference> _resultsWeak = new Dictionary<DataCache, WeakReference>();        
        private readonly Dictionary<DataCache, T> _resultsStrong = new Dictionary<DataCache, T>();

        public T ComputeResult(DataCache dataCache, bool save = false)
        {
            if (_resultsStrong.ContainsKey(dataCache))
                return _resultsStrong[dataCache];

            if (_resultsWeak.ContainsKey(dataCache))
            {
                var temp = _resultsWeak[dataCache].Target;
                if (temp != null) return (T) temp;
            }

            var result = ExecuteAlgorithmLogic(dataCache);
            _resultsWeak[dataCache] = new WeakReference(result, true);
            if (save) _resultsStrong[dataCache] = result;

            return result;
        }
}
我会定义数十个.cs文件,每个文件对应一个算法。这种方法有两个问题。首先,为了让它工作,我需要实例化我的算法并保持该实例(或者结果不会被缓存,整个点是静音的)。但我最终在每个派生类中都得到了一个难看的单例模式实现。看起来是这样的:

class ActualAgorithm : Algorithm<SomeType>
{
    protected override SomeType ExecuteAlgorithmLogic(DataCache dataCache)
    {
       //Elves and dragons be here
    }
    protected ActualAgorithm(){ }
    private static ActualAgorithm _instance;
    public static ActualAgorithm Instance
    {
        get
        {
            _instance = _instance ?? new ActualAgorithm();
            return _instance;
        }
    }
}
类实际算法:算法
{
受保护的重写SomeType ExecuteAlgorithmLogic(数据缓存数据缓存)
{
//精灵和龙在这里
}
受保护的ActualAgorithm(){}
私有静态ActualAgorithm_实例;
公共静态算法实例
{
得到
{
_实例=_实例??新的ActualAgorithm();
返回_实例;
}
}
}

因此,在每个实现中,我都必须为单例模式复制代码。其次,数十个CS文件听起来也有点过头了,因为我真正想要的只是一个函数返回一些结果,这些结果可以缓存到各种
DataCache
对象中。当然,一定有一种更聪明的方法可以做到这一点,我非常希望你能朝着正确的方向努力。

你可以让你的算法独立于它们的结果:

class Engine<T> {
  Map<AlgorithmKey, Algorithm<T>> algorithms;
  Map<AlgorithmKey, Data> algorithmsResultCache;

  T processData(Data in);
}

interface Algorithm<T> {
  boolean doesResultNeedsToBeCached();
  T processData(Data in);
}
类引擎{
Map算法;
地图算法结果缓存;
T处理数据(数据输入);
}
接口算法{
布尔值doesResultNeedsToBeCached();
T处理数据(数据输入);
}
然后,引擎负责实例化算法,这些算法只是输入为数据,输出为null或某些数据的代码片段。每个算法都可以说是否需要缓存他的结果


为了完善我的答案,我认为您应该对算法的运行方式给出一些精确的说明(是否有顺序,用户是否可以调整,我们是否提前知道将运行的算法等)。

我的评论是这样的:

abstract class BaseClass<K,T> where T : BaseClass<K,T>, new()
{
    private static T _instance;
    public static T Instance
    {
        get
        {
            _instance = _instance ?? new T();
            return _instance;
        }
    }
}

class ActualClass : BaseClass<int, ActualClass>
{
    public ActualClass() {}
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(ActualClass.Instance.GetType().ToString());
        Console.ReadLine();
    }
}
抽象类基类,其中T:BaseClass,new()
{
私有静态T_实例;
公共静态T实例
{
得到
{
_实例=_实例??新的T();
返回_实例;
}
}
}
类实际类:基类
{
公共实际类(){}
}
班级计划
{
静态void Main(字符串[]参数)
{
WriteLine(ActualClass.Instance.GetType().ToString());
Console.ReadLine();
}
}

这里唯一的问题是,您将拥有一个公共构造函数。

您能否将您的算法实例注册到一个组合的算法库/工厂中,该库/工厂将保留对它们的引用?存储库可以是一个单实例,如果您让存储库控制算法实例化,您可以使用它来确保每个实例只存在一个实例

public class AlgorithmRepository
{
    //... use boilerplate singleton code

    public void CreateAlgorithm(Algorithms algorithm)
    {
        //... add to some internal hash or map, checking that it hasn't been created already
        //... Algorithms is just an enum telling it which to create (clunky factory
        //    implementation)
    }

    public void ComputeResult(Algorithms algorithm, DataCache datacache)
    {
        // Can lazy load algoirthms here and make CreateAlgorithm private ..
        CreateAlgorithm(algorithm);
        //... compute and return.
    }
}

这就是说,为每个算法提供一个单独的类(和cs文件)对我来说是有意义的。如果一个cs文件中有多个algo类是轻量级的,你可以打破惯例,在一个cs文件中有多个algo类。如果你担心文件的数量,那么管理起来就更容易了——还有更糟糕的事情要做。FWIW我刚刚忍受了文件的数量…

我改进了我以前的答案,但由于它与我提出的另一种方法大不相同,我想我可能会做出另一个答案。首先,我们需要声明一些接口:

// Where to find cached data
interface DataRepository {      
  void cacheData(Key k, Data d);
  Data retrieveData(Key k, Data d);
};

// If by any chance we need an algorithm somewhere
interface AlgorithmRepository {
  Algorithm getAlgorithm(Key k);
}

// The algorithm that process data
interface Algorithm {
  void processData(Data in, Data out);
}
给定这些接口,我们可以为算法库定义一些基本实现:

class BaseAlgorithmRepository {
  // The algorithm dictionnary
  Map<Key, Algorithm> algorithms;

  // On init, we'll build our repository using this function
  void setAlgorithmForKey(Key k, Algorithm a) {
    algorithms.put(k, a);
  }

  // ... implement the other function of the interface
}
在此基础上,我认为您还可以定义一些特殊类型的算法,这些算法实际上是其他算法的组合,但也可以实现算法接口。例如:

class AlgorithmChain extends BaseAlgorithm {
  List<Algorithms> chain;

  void processData(Data in, Data out) {
    Data currentIn = in;
    foreach (Algorithm a : chain) {
      Data currentOut = new Data();
      a.processData(currentIn, currentOut);
      currentIn = currentOut;
    }
    out = currentOut;
  }
}
class AlgorithmChain扩展了BaseAlgorithm{
列表链;
void processData(数据输入、数据输出){
数据currentIn=in;
foreach(算法a:链){
Data currentOut=新数据();
a、 processData(currentIn,currentOut);
电流输入=电流输出;
}
out=电流输出;
}
}
我在此添加了一个数据池,它允许您重用现有但未使用的数据对象,以避免每次创建新数据()时分配大量内存


我认为这组类可以为整个体系结构提供一个良好的基础,另外一个好处是它不使用任何单例(总是向相关对象传递引用)。这也意味着为单元测试实现虚拟类将相当容易。

首先,为了更清晰,我建议您将DataCache重命名为类似DataInput的名称,因为很容易将其与真正用作缓存(resultsbright和resultsbright)来存储结果的对象混淆

关于这些缓存需要保留在内存中以供将来使用,也许您应该考虑将它们放置在.NET应用程序中比对象范围、应用程序或会话中更广泛的范围之一。 您还可以使用AlgorithmLocator(参见模式)作为所有算法的单一访问点,以消除每个算法中的单例逻辑重复

除此之外,我发现您的解决方案在全球范围内都是不错的。它是否过份,基本上取决于算法的同质性。如果它们都有相同的缓存数据和返回结果的方法。。。拥有所有这些逻辑将是一个巨大的好处
class DataRepository {
  AlgorithmRepository algorithmRepository;
  Map<Key, Data> cache;

  void cacheData(Key k, Data d) {
    cache.put(k, d);
  }

  Data retrieveData(Key k, Data in) {
    Data d = cache.get(k);
    if (d==null) {
      // Data not found in the cache, then we try to produce it ourself
      Data d = new Data();
      Algorithm a = algorithmRepository.getAlgorithm(k);
      a.processData(in, d);

      // This is optional, you could simply throw an exception to say that the
      // data has not been cached and thus, the algorithm succession did not 
      // produce the necessary data. So instead of the above, you could simply:
      // throw new DataNotCached(k);
      // and thus halt the whole processing
    }
    return d;
  }
}
abstract class BaseAlgorithm {
  DataRepository repository;
}

class SampleNoCacheAlgorithm extends BaseAlgorithm {
  void processData(Data in, Data out) {
    // do something with in to compute out
  }
}

class SampleCacheProducerAlgorithm extends BaseAlgorithm {
  static Key KEY = "SampleCacheProducerAlgorithm.myKey";

  void processData(Data in, Data out) {
    // do something with in to compute out
    // then call repository.cacheData(KEY, out);
  }
}

class SampleCacheConsumerAlgorithm extends BaseAlgorithm {
  void processData(Data in, Data out) {
    // Data tmp = repository.retrieveData(SampleCacheProducerAlgorithm.KEY, in);
    // do something with in and tmp to compute out
  }
}
class AlgorithmChain extends BaseAlgorithm {
  List<Algorithms> chain;

  void processData(Data in, Data out) {
    Data currentIn = in;
    foreach (Algorithm a : chain) {
      Data currentOut = new Data();
      a.processData(currentIn, currentOut);
      currentIn = currentOut;
    }
    out = currentOut;
  }
}
public sealed partial class Algorithm<T>
{
    private static object ExecuteForSomeType(DataCache dataCache)
    {
        return new SomeType();
    }
}

public sealed partial class Algorithm<T>
{
    private static object ExecuteForSomeOtherType(DataCache dataCache)
    {
        return new SomeOtherType();
    }
}

public sealed partial class Algorithm<T>
{
    private readonly Dictionary<System.Type, Func<DataCache, object>> _algorithms = new Dictionary<System.Type, Func<DataCache, object>>();
    private readonly Dictionary<DataCache, WeakReference> _resultsWeak = new Dictionary<DataCache, WeakReference>();
    private readonly Dictionary<DataCache, T> _resultsStrong = new Dictionary<DataCache, T>();

    private Algorithm() { }
    private static Algorithm<T> _instance;
    public static Algorithm<T> Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Algorithm<T>();
                _instance._algorithms.Add(typeof(SomeType), ExecuteForSomeType);
                _instance._algorithms.Add(typeof(SomeOtherType), ExecuteForSomeOtherType);
            }
            return _instance;
        }
    }

    public T ComputeResult(DataCache dataCache, bool save = false)
    {
        T returnValue = (T)(new object());
        if (_resultsStrong.ContainsKey(dataCache))
        {
            returnValue = _resultsStrong[dataCache];
            return returnValue;
        }
        if (_resultsWeak.ContainsKey(dataCache))
        {
            returnValue = (T)_resultsWeak[dataCache].Target;
            if (returnValue != null) return returnValue;
        }

        returnValue = (T)_algorithms[returnValue.GetType()](dataCache);
        _resultsWeak[dataCache] = new WeakReference(returnValue, true);
        if (save) _resultsStrong[dataCache] = returnValue;

        return returnValue;
    }
}