Какой лучший шаблон дизайна для этой проблемы? - PullRequest
2 голосов
/ 18 апреля 2009

В настоящее время я пытаюсь реализовать систему или способ создания типов, но также получаю имя типа при создании.

Скажем, у меня есть такие действия, которые могут быть созданы пользователем приложения:

Blur
Contrast
Sharpen
Diffuse
Invert
...

Итак, как мне извлечь их имя, когда они создаются через пользовательский интерфейс. Скажем, у вас есть отдельные кнопки интерфейса для каждого. Каждый раз, когда вы создаете один из них, они должны называться Blur01, Blur02, ... Эти объекты будут созданы в миллионах.

Так что в настоящее время у них есть .Name свойство, которое должно быть установлено при создании. Но я также не хочу дублировать код для установки имен внутри каждого класса.

Какой шаблон дизайна мне использовать для этого?

Ответы [ 6 ]

2 голосов
/ 18 апреля 2009

Шаблон команды ?

Использование этого шаблона будет иметь преимущество в том, что он заложит основу для возможности отменить / повторить , которая, похоже, может понадобиться в какой-то момент.

Вкратце, вы можете создать интерфейс с именем «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
  }
}

Многие инструментарии пользовательского интерфейса поставляются с поддержкой этого шаблона, например, Swing. Таким образом, вам не нужно много делать, чтобы заставить его работать.

2 голосов
/ 18 апреля 2009

Если вы хотите, чтобы у каждого типа были свои уникальные инкременты, вы получите дублированный код. Вы можете избежать этого с помощью отражения, сохранив статический словарь в базовом классе с 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 голос
/ 21 апреля 2009

Я не уверен, почему (большинство) ответов упоминают рефлексию - хороший старый полиморфизм прекрасно подходит для этого. Получить имя типа среды выполнения просто - просто 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]?

0 голосов
/ 20 апреля 2009

Если он не должен быть удобочитаемым для человека, я бы использовал GUID в качестве имени объекта и имел бы базовое поле для указания базового действия, из которого он был создан. Каждый объект может быть уникально идентифицирован как (Базовое имя) - (ID). Вы можете иметь удобочитаемое поле Описание, но оно не используется для поиска. Если вам нужно отобразить GUID, я использую трюк, который конвертируется из Base 16 в Base 34. В основном из 0123456789ABCDEF в 0123456789ABCDEFGHJKLMNPQRSTUVWXZY. В результате получается намного более короткая строка, которую легче отобразить и ввести.

0 голосов
/ 18 апреля 2009

Боюсь, что нет способа сделать это в одном месте. Вам придется явно «печатать» имя класса (типа) в каждом определении класса. Причина в том, что любое отражение в базовом классе будет возвращать тип базового класса, а не конкретный тип ... если только вы не переопределите метод в каждом производном классе, в этом случае вы также можете жестко закодировать имя типа в виде строки (чтобы избежать попадания отражения) ...

Итак, что насчет

public const string TypeName = "MYTypeName";
0 голосов
/ 18 апреля 2009

Лучше всего создать свойство (или функцию, если вы предпочитаете) в базовом классе, которое virtual и содержит своего рода базовое имя. Нечто подобное ..

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

Ваше свойство .Name может остаться прежним или измениться на что-то вроде .NameSuffix, и вы можете объединить BaseName и NameSuffix для формирования полного имени.

В конце концов, это, вероятно, не лучший способ сделать это. Какова цель именования этих объектов / действий? Мне кажется, что вы используете шаблон команды в манипуляциях с изображениями и либо настраиваете цепочку действий для выполнения, либо хотите сохранить цепочку действий для отмены. Такая система лучше подходит для структуры связанного списка, которую можно отобразить (при необходимости) на каком-либо графике, показывающем действие (вероятно, имя класса или BaseName, как определено выше) и серию в котором они будут казнены.

Что касается необходимости использовать имя для создания экземпляров объектов, я не совсем уверен, почему. Если это действительно необходимо, вашими двумя вариантами будут фабричный шаблон или использование отражения либо имени класса, либо значения пользовательского атрибута (которое также можно использовать для предоставления имени, если это необходимо). Однако, если вы собираетесь создавать их «миллионами» держитесь подальше от рефлексии .

Полагаю, это окольный способ сказать, что все зависит от того, как это должно работать. Вы должны будете предоставить более конкретную и исчерпывающую информацию для соответственно конкретного и всеобъемлющего ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...