Design Pattern for C # Dynamic Object

I have a queue that processes objects in a while loop. They are added asynchronously somewhere .. like this:

myqueue.pushback(String value); 

And they are processed as follows:

 while(true) { String path = queue.pop(); if(process(path)) { Console.WriteLine("Good!"); } else { queue.pushback(path); } } 

Now I want to change this to support a TTL-like (time to live) flag, so the file path will be added o more than n times.

How can I do this while maintaining the signature of the bool process(String path) function? I do not want to change it.

I was thinking about saving a map or a list that counts how many times the process function returns false for the path and removes the path from the list when nth returns false. It is interesting how this can be done more dynamically, and it is desirable that the TTL automatically decrease with each new addition to the process. Hope I'm not saying trash. Perhaps using something like this

 class JobData { public string path; public short ttl; public static implicit operator String(JobData jobData) {jobData.ttl--; return jobData.path;} } 
+8
design c # design-patterns
source share
3 answers

I like the idea of ​​the JobData class, but there is already an answer demonstrating this, and the fact that you work with file paths gives you another possible advantage. Some characters are not valid in the file path, so you can choose one to use as a delimiter. The advantage here is that the queue type remains a string, so you do not have to modify any of the existing asynchronous codes. Here you can see a list of reserved path characters:

http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words

For our purposes, I will use the percent symbol (%). Then you can change your code as follows, and nothing else needs to be changed:

 const int startingTTL = 100; const string delimiter = "%"; while(true) { String[] path = queue.pop().Split(delimiter.ToCharArray()); int ttl = path.Length > 1?--int.Parse(path[1]):startingTTL; if(process(path[0])) { Console.WriteLine("Good!"); } else if (ttl > 0) { queue.pushback(string.Format("{0}{1}{2}", path[0], delimiter,ttl)); } else { Console.WriteLine("TTL expired for path: {0}" path[0]); } } 

Again, from a pure architecture point of view, a class with two properties is the best design ... but from a practical point of view, YAGNI : this parameter means that you can avoid returning and changing other asynchronous code that is queued. This code still needs to know only about strings and will work with this unmodified.

One more thing. I want to note that this is a rather complicated cycle, prone to run away with the processor core. Also, if it is a .Net queue type, and your hard loop is ahead of your asynchronous releases, to free the queue, you will throw an exception that will exit the while (true) block. You can solve both problems with the code as follows:

 while(true) { try { String[] path = queue.pop().Split(delimiter.ToCharArray()); int ttl = path.Length > 1?--int.Parse(path[1]):startingTTL; if(process(path[0])) { Console.WriteLine("Good!"); } else if (ttl > 0) { queue.pushback(string.Format("{0}{1}{2}", path[0], delimiter,ttl)); } else { Console.WriteLine("TTL expired for path: {0}" path[0]); } } catch(InvalidOperationException ex) { //Queue.Dequeue throws InvalidOperation if the queue is empty... sleep for a bit before trying again Thread.Sleep(100); } } 
+2
source share

If the limitation is that the bool process(String path) cannot be touched / changed, add functionality to myqueue . You can keep public signatures void pushback(string path) and string pop() , but internally you can track your TTL. You can either wrap the string paths in the JobData class, which is added to the internal queue, or you can use the secondary Dictionary key along the path. Maybe even something as simple as saving the last pop ed path, and if the next push is the same path, you can assume that it was a rejected / failed element. In addition, in your pop method, you can even drop a path that has been rejected too long and internally extract the next path so that the calling code blissfully does not know about the problem.

+2
source share

You can abstract / encapsulate the functionality of the "job manager". Hide the queue and implementation from the caller so that you can do whatever you want without worries. Something like that:

 public static class JobManager { private static Queue<JobData> _queue; static JobManager() { Task.Factory.StartNew(() => { StartProcessing(); }); } public static void AddJob(string value) { //TODO: validate _queue.Enqueue(new JobData(value)); } private static StartProcessing() { while (true) { if (_queue.Count > 0) { JobData data = _queue.Dequeue(); if (!process(data.Path)) { data.TTL--; if (data.TTL > 0) _queue.Enqueue(data); } } else { Thread.Sleep(1000); } } } private class JobData { public string Path { get; set; } public short TTL { get; set; } public JobData(string value) { this.Path = value; this.TTL = DEFAULT_TTL; } } } 

Then your processing loop can process the TTL value.

Edit - added a simple processing cycle. This code is not thread safe, but hopefully will give you an idea.

+1
source share

All Articles