继承类上的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;
}
}