简体   繁体   English

动态C#对象的设计模式

[英]Design pattern for dynamic C# object

I have a queue that processes objects in a while loop. 我有一个在while循环中处理对象的队列。 They are added asynchronously somewhere.. like this: 它们被异步添加到某处..像这样:

myqueue.pushback(String value);

And they are processed like this: 它们的处理方式如下:

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

Now, the thing is that I'd like to modify this to support a TTL-like (time to live) flag, so the file path would be added o more than n times. 现在,我要修改它以支持类似TTL(生存时间)的标志,因此文件路径将被添加n次以上。

How could I do this, while keeping the bool process(String path) function signature? 在保留bool process(String path)函数签名的同时,我该怎么做? I don't want to modify that. 我不想修改它。

I thought about holding a map, or a list that counts how many times the process function returned false for a path and drop the path from the list at the n-th return of false. 我考虑过要持有一张地图或一个列表,该列表计算过程函数为一条路径返回false的次数,并在第n次返回false时将其从列表中删除。 I wonder how can this be done more dynamically, and preferably I'd like the TTL to automatically decrement itself at each new addition to the process. 我想知道如何才能更动态地完成此操作,并且最好让TTL在每次添加新的过程时自动将其自身递减。 I hope I am not talking trash. 我希望我不是在说垃圾。 Maybe using something like this 也许使用这样的东西

class JobData
{
   public string path;
   public short ttl;

   public  static implicit operator String(JobData jobData) {jobData.ttl--; return jobData.path;}
}

I like the idea of a JobData class, but there's already an answer demonstrating that, and the fact that you're working with file paths give you another possible advantage. 我喜欢JobData类的想法,但是已经有一个答案可以证明这一点,而使用文件路径这一事实为您提供了另一个可能的优势。 Certain characters are not valid in file paths, and so you could choose one to use as a delimiter. 某些字符在文件路径中无效,因此您可以选择一个字符用作分隔符。 The advantage here is that the queue type remains a string, and so you would not have to modify any of your existing asynchronous code. 这样做的好处是队列类型仍然是字符串,因此您不必修改任何现有的异步代码。 You can see a list of reserved path characters here: 您可以在此处查看保留路径字符的列表:

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

For our purposes, I'll use the percent (%) character. 为了我们的目的,我将使用百分比(%)字符。 Then you can modify your code as follows, and nothing else needs to change: 然后,您可以按以下方式修改代码,而无需进行其他任何更改:

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 standpoint, a class with two properties is a better design... but from a practical standpoint, YAGNI : this option means you can avoid going back and changing other asynchronous code that pushes into the queue. 同样,从纯体系结构的角度来看,具有两个属性的类是一种更好的设计...但是从实际的角度来看, YAGNI :此选项意味着您可以避免返回并更改其他推入队列的异步代码。 That code still only needs to know about the strings, and will work with this unmodified. 该代码仍然只需要了解字符串,并且可以在未修改的情况下使用。

One more thing. 还有一件事。 I want to point out that this is a fairly tight loop, prone to running away with a cpu core. 我想指出的是,这是一个相当紧密的循环,容易被cpu核心所破坏。 Additionally, if this is the .Net queue type and your tight loop gets ahead of your asynchronous produces to empty the queue, you'll throw an exception, which would break out of the while(true) block. 此外,如果这是.Net队列类型,并且紧密循环领先于异步生产以清空队列,则将引发异常,该异常会超出while(true)块。 You can solve both issues with code like this: 您可以使用以下代码解决这两个问题:

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

If the constraint is that bool process(String path) cannot be touched/changed then put the functionality into myqueue . 如果约束是不能触摸/更改bool process(String path)则将功能放入myqueue You can keep its public signatures of void pushback(string path) and string pop() , but internally you can track your TTL. 您可以保留其void pushback(string path)string pop()公共签名,但在内部可以跟踪TTL。 You can either wrap the string paths in a JobData -like class that gets added to the internal queue, or you can have a secondary Dictionary keyed by path. 您可以将字符串路径包装在类似JobData的类中,该类将添加到内部队列中,或者您可以通过路径键入第二个Dictionary Perhaps even something as simple as saving the last pop ed path and if the subsequent push is the same path you can assume it was a rejected/failed item. 甚至可以像保存上一条pop路径一样简单,并且如果后续的push是相同的路径,则可以认为它是被拒绝/失败的项目。 Also, in your pop method you can even discard a path that has been rejected too many time and internally fetch the next path so the calling code is blissfully unaware of the issue. 同样,在您的pop方法中,您甚至可以丢弃已被拒绝太多时间的路径,并在内部获取下一个路径,因此调用代码非常高兴地没有意识到该问题。

You could abstract/encapsulate the functionality of the "job manager". 您可以抽象/封装“作业管理器”的功能。 Hide the queue and implementation from the caller so you can do whatever you want without the callers caring. 对呼叫者隐藏队列和实现,这样您就可以在不关心呼叫者的情况下做任何想做的事情。 Something like this: 像这样:

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 handle the TTL value. 然后,您的处理循环可以处理TTL值。

Edit - Added a simple processing loop. 编辑-添加了一个简单的处理循环。 This code isn't thread safe, but should hopefully give you an idea. 该代码不是线程安全的,但希望可以给您一个思路。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM