Способы обеспечения уникальных экземпляров класса?

14

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

Например, у меня есть Nameкласс с полем name. После того, как у меня есть Nameобъект с nameинициализацией для Джона Смита, я не хочу иметь возможность создавать экземпляр другого Nameобъекта также с именем, как у Джона Смита, или, если экземпляр имеет место, я хочу, чтобы ссылка на оригинальный объект передавалась обратно. чем новый объект.

Я знаю, что один из способов сделать это - иметь статическую фабрику, которая содержит Mapвсе текущие объекты Name, и фабрика проверяет, что объект с Джоном Смитом в качестве имени еще не существует, перед передачей ссылки на Nameобъект.

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

Есть ли другие способы достижения этого?

арахис
источник
1
Вы хотите синглтон
5
вам лучше пойти с первым: - статическая фабрика
Rohit Jain
16
@MarcB op не хочет синглтона. у него может быть много экземпляров одного и того же класса, но эти экземпляры должны быть идентифицируемыми.
1
@MarcB Мне известен шаблон синглтона, но я подумал, что это обеспечивает только один экземпляр класса? Я хочу несколько экземпляров, разные значения. Извините, если вопрос не проясняет. редактировать: только видел первый комментарий перед публикацией.
Арахис
2
I'm aware that one way of doing this is to have a static factory that holds a Map...Так почему бы вам не сделать это таким образом?
FrustratedWithFormsDesigner

Ответы:

13

На самом деле вы уже ответили на свой вопрос. Ваш первый путь должен быть более эффективным здесь. Использование static factoryвсегда предпочтительнее, чем constructorвезде, где вы думаете. Таким образом, вы можете избежать использования Constructorв этом случае, иначе вы бы имели, throw some exceptionесли экземпляр уже существует с данным именем.

Таким образом, вы можете создать статический метод фабрики: - getInstanceWithName(name)который получит уже доступный экземпляр с этим именем, и если он не существует, он создаст новый экземпляр и сделает вашconstructor приватный, как это обычно следует делать при работе с static factories,

Кроме того, для этого вам нужно поддерживать статичность Listили Mapвсе созданные уникальные экземпляры в вашем Factoryклассе.

РЕДАКТИРОВАТЬ : -

Вы должны обязательно пройти - Эффективная Java - Пункт # 1: Рассмотрите Статические фабрики по Конструкторам . Вы не можете получить лучшее объяснение, чем эта книга.

Рохит Джайн
источник
1
Это то, о чем уже подумал ОП.
BGurung
+1 за ссылку, которая явно решает некоторые проблемы ОП.
Роберт Харви,
@RobertHarvey. Да, эта книга - лучший источник для большинства подобных тем. И это самый первый пункт на самом деле. :)
Рохит Джейн
+1 за Эффективную Java, у меня действительно есть книга, которая сейчас лежит в моей сумке :) Однако мне просто было любопытно узнать о других способах достижения уникальности, кроме статического фабричного / статического метода и исключения в конструкторе. Если их нет, я приму это.
арахис
@Peanut. Конечно. Это лучший ресурс для получения дополнительной информации.
Рохит Джейн
5

Упоминания об эффективной Java кажется, добавляют много правдоподобности, поэтому этот ответ опирается на:

  • Действующий пункт 8 Java: подчиняться генеральному контракту, когда переопределение равно
  • Эффективный элемент Java 9: ​​всегда переопределять hashCode, когда вы переопределяете равно
  • Эффективный элемент Java 15: минимизируйте изменчивость

Я хотел бы сделать шаг назад и спросить, почему вас волнует, существует ли более одного экземпляра этого объекта имени.

Мне редко нужно делать такой вид пула объектов. Я думаю, что ОП делает это, чтобы они могли просто сравнивать свои Nameобъекты ==. Или используйте Nameобъекты внутри HashMapили аналогичные в качестве ключа.

Если это так, то это можно решить путем правильной реализацииequals() .

Вот так:

public final class Name {
  private final String name;

  public Name(String name) {
    if (name == null) {
      name = ""; //or exception if you like
    }
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Name)) {
      return false;
    }
    Name other = (Name) o;
    return other.name.equals(name);
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }
}

После этого верно следующее:

Name a = new Name("weston");
Name b = new Name("weston");
assert(a.equals(b)); //same but:
assert(a!=b); //not the same instance
//test out the Name instances in a hashmap:
HashMap<Name,Object> map = new HashMap<Name,Object>();
Object detailsIn = new Object();
map.put(a,detailsIn);
Object detailsOut = map.get(b);
assert(detailsIn==detailsOut); //the map returned the same details object
//even though we put with `a` and got with `b` thanks to our correct equals implementation

Я предполагаю вашу цель, но таким образом вы можете использовать Nameкласс в хеш-картах и ​​т. Д., И они не обязательно должны быть точно такими же.

Уэстон
источник
Пример, пожалуйста.
Роберт Харви
@RobertHarvey пример правильной реализации?
Уэстон
Похоже, вы предоставили один. Но я не понимаю, чем это отличается от простой проверки свойства Name на равенство в методе статической фабрики.
Роберт Харви,
1
@RobertHarvey Я пытался объяснить больше, я предлагаю полную альтернативу, которая вообще не требует статической фабрики, и я оспариваю отправную точку OP, чтобы они не хотели иметь более одного экземпляра на имя.
Уэстон
Действительная причина требовать уникальных объектов - если вы хотите, чтобы синхронизированный блок использовал объект в качестве монитора. Два объекта, которые .equals () друг с другом не будут работать.
Ли Колдуэлл
3
  • Сделать Nameинтерфейс
  • Создать интерфейс NameFactoryс методомName getByName(String)
  • Создать реализацию NameFactoryс Map<String,WeakReference<Name>>внутренней частью
  • synchronizeна карте по имени внутри getByNameметода, прежде чем делать новые экземплярыName
  • При желании, используйте статическую частную реализацию Nameинтерфейса внутри вашей реализацииNameFactory

Этот подход позволит вам убедиться, что:

  • Только один экземпляр Nameсуществует в любое время,
  • В вашем классе не возникает утечка памяти "Lingerer", когда Names остаются вокруг дольше, чем нужно,
  • Дизайн остается тестируемым с макетированными объектами, потому что вы используете интерфейсы.
dasblinkenlight
источник
У вас нет утечек памяти при использовании фабричного метода, если вы throwиспользуете идентичное имя вместо возврата объекта или если вы возвращаете nullобъект.
Роберт Харви,
@RobertHarvey Я имею в виду утечки, которые на самом деле являются не утечками, а законными объектами (так называемыми "задерживающимися"). Простая статическая карта предотвратит освобождение объектов-значений, в то время как карта слабых ссылок будет хранить их в памяти только до тех пор, пока существуют другие живые ссылки.
dasblinkenlight
Ах, я понимаю, что вы имеете в виду. Это может быть одним из редких случаев, когда destructorбудет удобно.
Роберт Харви
1
Слишком сложный. Это можно сделать как @weston answer, переопределить equals и заставить фабрику хранить коллекцию имен.
Тулаинс Кордова
@ user1598390 Нужно сделать вещь максимально простой, но не проще. «Решение» Уэстона не решает проблему ОП (карта отсутствует), а набор имен создает утечку памяти (да, в Java есть утечки памяти).
dasblinkenlight
1

вы должны сделать конструкторы частными и создать методы, например getNameInstance(String), если объект с таким именем уже существует (например, на основе hastable статического класса), вы возвращаете эту ссылку, в противном случае вы создаете новый объект с помощью вашего частного конструктора и добавляете его в хеш-таблицу

HericDenis
источник
Вы повторили то, что я сказал в третьем абзаце вопроса. Я был после альтернативных способов.
Арахис
Извините, я думаю, что мы ответили почти одновременно, потому что я проверил ответы перед публикацией. На самом деле, я попытался ответить на него на StackOverflown и чем он был перенесен.
HericDenis
1

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

class UniqueName
    {
        private string Name;
        public int ID;
        private static int Count=0;
        static List<UniqueName> lt=new List<UniqueName>();

        private UniqueName(string Name)
        {
            this.Name = Name;
            ID = ++Count;
        }

        public static UniqueName GetUniqueueInstance(string Name)
        {
            foreach (UniqueName un in lt)
            {
                if ( string.Compare( un.Name,Name,StringComparison.InvariantCultureIgnoreCase)==0)
                    return un;
            }

            UniqueName temp=new UniqueName(Name);
            lt.Add(temp);
            return temp;
        }
    }
Акшай
источник
Это не Java? Вы написали псевдокод? Или на любом другом языке? Пожалуйста, укажите это явно.
Рохит Джейн
Похоже на C #. Акшай, ты должен изучить другие коллекции, кроме List. Это было бы хорошо для Dictionary<string,UniqueName>.
Уэстон