簡體   English   中英

解決此問題的最佳設計模式是什么?

[英]What's the best design pattern for this problem?

目前,我正在嘗試實現創建類型的系統或方法,但是在創建時還要獲取類型名稱。

假設我有類似的操作,可以由應用程序的用戶創建:

Blur
Contrast
Sharpen
Diffuse
Invert
...

因此,當通過UI創建它們時,應該如何提取它們的名稱。 因此,假設您每個都有單獨的UI按鈕。 每次創建其中一個時,它們都必須命名為Blur01Blur02 ……。將以數百萬個對象的形式創建這些對象。

因此,當前它們具有.Name屬性,必須在創建時進行設置。 但是我也不想在每個類中重復代碼來設置名稱。

我應該使用哪種設計模式?

如果您希望每種類型都有自己的唯一增量器,則將有重復的代碼。 通過將靜態字典保存在具有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;
}

像這樣的東西會擺脫重復的代碼,但是在基類中使用反射會增加一些復雜性……

命令模式

使用此模式將具有為撤消/重做功能奠定基礎的優勢,在某些時候您可能需要此功能。

簡要地說,您可以創建一個名為“ Command”的接口,從該接口可以派生出稱為“ Blur”,“ Sharpen”等的子類。 命令將定義一個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。 因此,您可能不需要做很多事情就可以使其工作。

我不確定為什么(大多數)答案都提到了反射-好的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 { }

由於您將為每個數據庫創建“數百萬”,因此我可能會同時分析它們並檢查內存使用情況。 我不太在乎通用方法-因為很容易搞砸並傳遞錯誤的類型參數-但我懷疑它的內存使用情況要好一些。 另外,如果要創建> int.MaxValue ,則必須擔心溢出。

話雖這么說,我不確定為什么您不只是維護一個索引可尋址的集合,並通過像Blur[0]這樣的索引來引用它們?

恐怕無法在一個地方做到這一點。.您將必須在每個類定義中顯式地“鍵入”類(類型)名稱。 原因是基類中的任何反射都將返回基類類型,而不是具體類型...除非您在每個派生類中重寫該方法,否則您最好將類型名稱硬編碼為字符串const。 (以避免反射命中)...

所以呢

public const string TypeName = "MYTypeName";

如果不需要人類可讀,則可以使用GUID作為對象的名稱,並具有一個基字段來指示創建對象的基本操作。 每個對象可以唯一地標識為(基本名稱)-(ID)。 您可能具有人類可讀的Description字段,但該字段不用於查找。 如果確實需要顯示GUID,我將使用的技巧將從Base 16轉換為Base34。基本上是從0123456789ABCDEF到0123456789ABCDEFGHJKLMNPQRSTUVWXZY。 這樣會產生一個更短的字符串,更易於顯示和鍵入。

最好的選擇是在virtual的基類中創建一個屬性(或函數,如果願意的話),並提供某種基名稱。 就像是..

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

您的.Name屬性可以保持不變,或更改為.NameSuffix類的.NameSuffix ,並且可以將BaseNameNameSuffix組合在一起以形成一個完整的名稱。

但最終,這可能不是解決此問題的最佳方法。 命名這些對象/動作的目的是什么? 在我看來,您好像在圖像處理中使用命令模式,或者正在設置要執行的一系列操作,或者想要保留要撤消的一系列操作。 這樣的系統將更適合於鏈表結構,您可以在某種圖形中顯示(如果需要),顯示動作(可能是類的名稱,或上面定義的BaseName )以及其中的序列。他們將被執行。

至於您需要使用名稱來實例化對象,我不太確定為什么。 如果確實需要這樣做,則您僅有的兩個選擇是工廠模式或在類的名稱或自定義屬性值上使用反射(如果需要,也可用於提供名稱)。 但是,如果要“以百萬計”實例化它們,請遠離反射

我想這是一種回旋的說法,這完全取決於它需要如何工作。 您必須提供更多具體而全面的信息才能獲得相應的具體而全面的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM