C # Singleton Pattern over Inherited Classes

I will start this question by apologizing for the length of the post. So that I save you a little, the problem is that the class that I stuck in my head is clearly corrupted, and I do not see a good solution.

In the project I'm working on, I need to use algorithms for working on pieces of data, call them DataCache . Sometimes these algorithms return results that must themselves be cached, and so I developed a scheme.

I have an Algorithm base class that looks like this

 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; } } 

If you call ComputeResult() and provide a DataCache , you can choose to cache the result. Also, if you're lucky, it could still be if the GC hasn't assembled it yet. The size of each DataCache is hundreds of megabytes, and before you ask, each of them has about 10 arrays that contain basic types such as int and float .

My idea here was that the actual algorithm would look something like this:

 class ActualAgorithm : Algorithm<SomeType> { protected override SomeType ExecuteAlgorithmLogic(DataCache dataCache) { //Elves be here } } 

And I would define dozens of .cs files, each for one algorithm. There are two problems with this approach. Firstly, in order for this to work, I need to create an instance of my algorithms and save this instance (or the results are not cached, and the whole point is inactive). But then I get an unsightly implementation of a singleton pattern in every derived class. It will look something like this:

 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; } } } 

Therefore, in each implementation, I would have to duplicate the code for a singleton template. And secondly, dozens of CS files also seem a little redundant, because what I'm actually after is just one function that returns some results that can be cached for various DataCache objects. Of course, there should be a more reasonable way to do this, and I would be very grateful for pushing in the right direction.

+4
source share
6 answers

You can have your algorithms regardless of their results:

 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); } 

Then you are responsible for developing algorithms that are only pieces of code, where the input is data and the output is zero or some data. Each algorithm can tell whether its result should be cached or not.

To clarify your answer, I think you should give some recommendations on how the algorithms should be executed (is there an order, is it customizable by the user, we know in advance the algorithms that will be executed, ...).

+2
source

What I had in mind with my comment was something like this:

 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(); } } 

The only problem here is that you will have an open constructor.

+2
source

I clarified my previous answer, but since it is different from the other approach I suggested, I thought I could just make another answer. First, we need to declare some interfaces:

 // 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); } 

Given these interfaces, we can determine the basic implementation of the algorithm repository:

 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 } 

Then we can also implement something for the DataRepository

 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; } } 

Finally, we implement the algorithms:

 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 } } 

To build on this, I think you could also define some special kinds of algorithms that are actually composites of other algorithms that also implement the Algorithm interface. An example would be:

 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; } } 

I would add that it is a DataPool that will allow you to reuse existing, but unused data objects to avoid allocating a large amount of memory each time you create a new Data ().

I think this set of classes can provide a good foundation for your entire architecture, with the added benefit that it does not use any singleton (always passing links to the corresponding objects). It also means that implementing dummy classes for unit tests will be quite simple.

+2
source

Can you register instances of your algorithm with a combined repository / factory of algorithms that will contain links to them? A repository can be single-point, and if you provide a repository to control the creation of an algorithm, you can use it to ensure that only one instance of each exists.

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

This suggests that having a separate class (and cs file) for each algorithm makes sense to me. You can break the convention and have several algo classes in one cs file if they are lightweight, and this simplifies management if you are worried about the number of files - there are even worse things. FWIW I just put up with the number of files ...

+1
source

First, I would suggest you rename the DataCache to something like DataInput for clarity, because it is easy to confuse it with objects that really act as caches (_resultsWeak and _resultsStrong) to store the results.

Regarding the need to store these caches in memory for future use, perhaps you should consider placing them in one of the wider areas that exist in a .NET application than an object area, application, or session, for example.

You can also use AlgorithmLocator (see the ServiceLocator template ) as a single access point to all algorithms to get rid of single-point duplication in each algorithm.

In addition, I believe that your decision will be good on a global scale. Regardless of whether it is excessive, it will largely depend on the uniformity of your algorithms. If they all have the same way of caching data, returning their results ... it will be very useful to have all this logic taken into account in one place. But we lack the context to judge.

Encapsulating the caching logic in a specific object stored in the algorithm (CachingStrategy?) Would also be an alternative to its inheritance, but perhaps a bit inconvenient, since the cache object had to access the cache before and after calculation and it would need to be able to run the calculation algorithm itself and have a hand on the results.

[Edit], if you are interested in having one .cs file for each algorithm, you can always group all the classes of algorithms related to a specific T in the same file.

+1
source

Usually, when you create a Singleton class, you do not want to inherit it. When you do this, you lose some of the kindness of the singleton pattern (and what I hear from the sample fanatics is that an angel loses wings every time you do something like that). But let's be pragmatic ... sometimes you do what you need to do.

No matter what, I don't think that combining generics and inheritance will work in this case.

You indicated that the number of algorithms will be in tens (not hundreds). Until that is the case, I will create a dictionary with the System.Type key and store references to your methods as dictionary values. In this case, I used Func<DataCache, object> as the signature of the dictionary character.

When an instance of a class first registers all available algorithms in a dictionary. At run time, when the class needs to execute an algorithm for type T, it will get type T and look for the algorithm in the dictionary.

If the code for the algorithms is relatively active, I would suggest dividing them into partial classes so that your code is readable.

 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; } } 
+1
source

Source: https://habr.com/ru/post/1332494/


All Articles