这个问题的最佳设计模式是什么?

时间:2009-04-17 20:34:15

标签: c# .net design-patterns

目前我正在尝试实现一个系统或一种创建类型的方法,但在创建时也会获取类型名称。

所以说我有这样的行为,可以由应用程序的用户创建:

Blur
Contrast
Sharpen
Diffuse
Invert
...

那么在通过UI创建名称时,我应该如何提取他们的名字。所以说你们每个人都有单独的UI按钮。每次创建其中一个时,都必须将它们命名为Blur01Blur02,......这些对象将以数百万的形式创建。

所以目前他们有.Name属性必须在创建时设置。但我也不想在每个类中复制代码来设置名称。

我应该使用哪种设计模式?

6 个答案:

答案 0 :(得分:2)

如果您希望每种类型都拥有自己独特的增量器,那么您将拥有重复的代码。您可以通过使用Type和int在基类中保存静态字典来避免这种情况,例如:

private static Dictionary<Type,int> nameSuffixCounters =
     new Dictionary<Type,int>();

public string CreateName()
{
    Type type = this.GetType();
    return string.Format("{0}{1}",type.Name, this.GetNextAndIncrement(type));
}

private int GetNextAndIncrement(Type type)
{
    int current;
    if (!this.nameSuffixCounters.TryGetValue(type, out current))
    {
        current = 0;
    }
    nameSuffixCounters[type] = ++current;
    return current;
}

这样的东西可以摆脱重复的代码,但它增加了在基类中使用反射的一些复杂性......

答案 1 :(得分:2)

Command Pattern

使用这种模式可以为undo/redo capability奠定基础,看起来你可能需要它。

简而言之,您可以创建一个名为“Command”的接口,从中可以派生出名为“Blur”,“Sharpen”等的子类。 Command将定义一个execute()方法,该方法将根据需要由子类实现:

public interface Command
{
  public void execute();

  public String getName();
}

public class Blur implements Command
{
  // ...

  public Blur(String name)
  {
    // ...
  }

  //...

  public void execute()
  {
    // execute the blur command
  }
}

许多UI工具包都支持这种模式 - 例如Swing。所以你可能不需要做太多工作就可以了。

答案 2 :(得分:1)

我不确定为什么(大多数)答案会提到反思 - 好的ol'多态性对此很好。获取运行时类型名称很简单 - 它只是this.GetType().Name

唯一有点棘手的部分是每班保持独特的计数器。之前已在SO上讨论过 - 但我找不到ATM链接。但是,最重要的是,您可以(ab)使用泛型或维持Dictionary<Type, int>来跟踪。

泛型版本有效,因为每个不同的类型参数都会创建一个不同的泛型类 - 所以它们每个都有不同的静态计数器:

abstract class Command<T> where T : Command<T> {
   private static int counter = 1;
   private static object syncLock = new object();

   protected Command() {
      lock (syncLock) {
         _name = this.GetType().Name + counter++.ToString();
      }
   }

   public string Name { get { return _name; } }
   private string _name;
}

class Blur : Command<Blur> { }

class Sharpen : Command<Sharpen> { }

不需要覆盖派生类中的任何内容,但您必须确保继承的Command<T>是正确的 - 或者您将共享计数器。我还使用了一个锁来同步对计数器的访问 - 假设你将从多个线程创建实例。

如果您不喜欢通用版本,那么Dictionary<Type, int>也可以正常工作。

abstract class Command {
   private static Dictionary<Type, int> counter = new Dictionary<Type, int>();
   private static object syncLock = new object();

   protected Command() {
      Type t = this.GetType();
      lock (syncLock) {
         if (!counter.ContainsKey(t)) {
            counter.Add(t, 0);
         }
         _name = t.Name + counter[t]++.ToString();
      }
   }

   public string Name { get { return _name; } }
   private string _name;
}

class Blur : Command { }
class Sharpen : Command { }

由于你将创建每个“数百万”,我可能会分析两者并检查内存使用情况。我并不特别关心通用方法 - 因为它很容易搞砸并传递错误的类型参数 - 但我怀疑它的内存使用情况要好一些。另外,如果您要创建&gt; int.MaxValue,你必须担心溢出。

所有这一切,我不确定为什么你不会只维护索引可寻址集合并通过索引引用它们 - 如Blur[0]

答案 3 :(得分:0)

我担心在一个地方无法做到这一点。你必须在每个类定义中明确地“键入”类(类型)名称。原因是基类中的任何反射都将返回基类类型,而不是具体类型...除非你覆盖每个派生类中的方法,在这种情况下你也可以将类型名称硬编码为字符串const (以避免反射命中)...

那么,

public const string TypeName = "MYTypeName";

答案 4 :(得分:0)

如果它不必是人类可读的,我会使用GUID作为对象的名称,并有一个基本字段来指示它是从中创建的基本操作。每个对象可以唯一标识为(基本名称) - (ID)。你可以有一个人类可读的描述字段但不用于查找。如果你确实需要显示一个GUID,我使用的技巧是从Base 16转换到Base 34.基本上从0123456789ABCDEF到0123456789ABCDEFGHJKLMNPQRSTUVWXZY。这会产生一个更短的字符串,更容易显示和输入。

答案 5 :(得分:-1)

您最好的选择是在virtual的基类中创建一个属性(或函数,如果您愿意)并提供一种基本名称。像...这样的东西。

public virtual string BaseName
{
    get { return GetType().Name; }
}

您的.Name媒体资源可以保持不变,或者更改为.NameSuffix,您可以将BaseNameNameSuffix合并为一个完整的名称。

最后,这可能不是解决这个问题的最好方法。命名这些对象/动作的目的是什么?它看起来像你在图像处理中使用命令模式,你要么设置一系列动作来执行,要么想要保持一系列动作来撤消。这样的系统更适合链接列表结构,您可以在某种图形中显示(如果需要),显示操作(可能是类的名称,或上面定义的BaseName)和他们将被执行的系列。

至于你需要使用名称来实例化对象,我不太清楚为什么。如果这确实是必要的,那么您只有两个选项可以是工厂模式,或者对类的名称或自定义属性值使用反射(如果需要,也可以用于提供名称)。但是,如果您打算将其实例化为“数百万”远离反思

我想这是一种迂回的说法,一切都取决于这需要如何运作。您必须提供更具体和全面的信息,以获得相应的具体和全面的答案。