动态c#对象的设计模式
本文关键字:设计模式 对象 动态 | 更新日期: 2023-09-27 18:19:18
我有一个在while循环中处理对象的队列。它们是在某处异步添加的。这样的:
myqueue.pushback(String value);
它们被这样处理:
while(true)
{
String path = queue.pop();
if(process(path))
{
Console.WriteLine("Good!");
}
else
{
queue.pushback(path);
}
}
现在,问题是我想修改它以支持类似于ttl的(生存时间)标志,这样文件路径将被添加0次以上。
我怎么能做到这一点,同时保持bool process(String path)
函数签名?我不想修改。
我考虑持有一个映射,或者一个列表,该列表计算进程函数为路径返回false的次数,并在第n次返回false时将路径从列表中删除。我想知道如何更动态地完成这一点,最好是我希望TTL在每次向进程添加新内容时自动减少自己。我希望我不是在说垃圾话。也许可以这样写
class JobData
{
public string path;
public short ttl;
public static implicit operator String(JobData jobData) {jobData.ttl--; return jobData.path;}
}
我喜欢JobData类的想法,但是已经有一个答案证明了这一点,而且您正在使用文件路径这一事实为您提供了另一个可能的优势。某些字符在文件路径中无效,因此您可以选择一个字符作为分隔符。这里的优点是队列类型仍然是字符串,因此您不必修改任何现有的异步代码。您可以在这里看到保留路径字符的列表:
http://en.wikipedia.org/wiki/Filename Reserved_characters_and_words
出于我们的目的,我将使用百分比(%)字符。然后,您可以按如下方式修改代码,无需更改其他内容:
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]);
}
}
同样,从纯粹的体系结构的角度来看,具有两个属性的类是更好的设计…但是从实用的角度来看,YAGNI:这个选项意味着您可以避免返回并更改其他推入队列的异步代码。该代码仍然只需要知道字符串,并且可以在未修改的情况下工作。
还有一点。我想指出的是,这是一个相当紧密的循环,很容易跑掉cpu核心。此外,如果这是. net队列类型,并且紧循环先于异步生成清空队列,则将抛出异常,该异常将从while(true)块中跳出。你可以用这样的代码来解决这两个问题:
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);
}
}
如果约束是不能触摸/更改bool process(String path)
,则将功能放入myqueue
。您可以保留void pushback(string path)
和string pop()
的公共签名,但是在内部您可以跟踪TTL。您可以将字符串路径包装在一个类似JobData
的类中,并添加到内部队列中,或者您可以使用一个由path键接的辅助Dictionary
。也许甚至是保存最后一个pop
ed路径,如果随后的push
是相同的路径,你可以假设它是一个被拒绝/失败的项目。此外,在您的pop
方法中,您甚至可以丢弃已被拒绝太多次的路径,并在内部获取下一个路径,以便调用代码幸福地不知道这个问题。
您可以抽象/封装"作业管理器"的功能。对调用者隐藏队列和实现,这样您就可以做任何您想做的事情,而无需调用者关心。像这样:
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;
}
}
}
然后你的处理循环可以处理TTL值。
编辑-添加一个简单的处理循环。这段代码不是线程安全的,但应该给你一个想法。