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


14

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

Наприклад, у мене є Nameклас з полем name. Після того, як у мене є Nameоб'єкт, nameініціалізований на Джон Сміт, я не хочу мати можливість інстанціювати інший Nameоб'єкт також з ім'ям, як Джон Сміт, або якщо інстанціфікація має місце, я хочу, щоб посилання на початковий об'єкт було передано назад ніж новий об’єкт.

Я знаю, що одним із способів цього є статична фабрика, яка містить Mapусі поточні об'єкти Name, і фабрика перевіряє, чи немає об'єкта з ім'ям Джона Сміта, перш ніж повернути посилання на Nameоб’єкт.

Інший спосіб, з якого я міг би придумати верхню частину голови, - це статична карта в Nameкласі, і коли конструктор викликається викиданням винятку, якщо передане значення для nameвже використовується в іншому об'єкті, проте я знаю, що викидає винятки у конструкторі взагалі погана ідея .

Чи є інші способи досягнення цього?


1
Хочеш сингла

5
вам краще піти з першим: - статична фабрика
Rohit Jain

16
@MarcB оп не хоче синглтон. у нього може бути багато примірників одного класу, але ці екземпляри повинні бути ідентифікованими.

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. Так, ця книга є найкращим джерелом для більшості таких тем. І це насправді перший пункт. :)
Rohit Jain

+1 для Ефективної Java, я справді маю книгу, яка зараз сидить у моїй сумці :) Однак мені було просто цікаво щодо інших способів досягнення унікальності, крім статичного фабричного / статичного методу та винятку в конструкторі. Якщо таких немає, то я прийму це.
Арахіс

@Peanut. Звичайно. Це найкращий ресурс для копання, щоб отримати більше інформації.
Рохіт Джайн

5

Згадки про ефективну Java, схоже, додають багато довіри, тому ця відповідь спирається на:

  • Ефективний пункт Пункт 8 Java: Дотримуйтесь загального контракту, коли перестановка дорівнює
  • Ефективний пункт Java 9: ​​Завжди переосмислюйте хеш-код, коли ви перебираєте рівність
  • Ефективний пункт 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 Я намагався пояснити більше, я пропоную повну альтернативу, яка взагалі не потребує статичної фабрики, і я оскаржую вихідну точку ОП, що вони не хочуть мати більше одного разу на ім'я.
Вестон

Вагомою причиною вимагати унікальних об'єктів є те, якщо ви хочете, щоб синхронізований блок використовував об'єкт як монітор. Два об'єкти, які .equals () один одного не працюватимуть.
Лі Колдвелл

3
  • Зробіть Nameінтерфейс
  • Створіть інтерфейс NameFactoryіз методомName getByName(String)
  • Створіть реалізацію за NameFactoryдопомогою Map<String,WeakReference<Name>>всередині неї
  • synchronizeна карті за назвою всередині getByNameметоду перед тим, як створювати нові екземпляриName
  • Необов’язково використовувати статичну приватну реалізацію Nameінтерфейсу всередині вашої реалізаціїNameFactory

Цей підхід дозволить вам забезпечити:

  • NameУ будь-який час існує лише один екземпляр ,
  • У вашому класі немає витоку пам'яті "Lingerer", коли вона Nameтримається довше, ніж потрібно,
  • Дизайн залишається перевіреним з глузуючими об'єктами, оскільки ви використовуєте інтерфейси.

У вас також немає витоків пам'яті фабричним методом, якщо ви throwколи ідентичне ім'я існує замість повернення об'єкта, або якщо ви повертаєте nullоб'єкт.
Роберт Харві

@RobertHarvey Я маю на увазі витоки, які насправді не є витоками, а законними об'єктами (так звані "лінгвіри"). Проста статична карта не дозволить випустити її об'єкти цінності, а карта слабких посилань збереже їх у пам'яті лише до тих пір, поки існують інші прямі посилання.
dasblinkenlight

Ах, я бачу, що ти маєш на увазі. Це може бути одним з рідкісних випадків, коли destructorбуло б корисно.
Роберт Харві

1
Занадто складний. Це можна зробити, як @weston відповідь, переосмислити рівняння і змусити фабрику зберігати колекцію імен.
Тулен Кордова

@ user1598390 Слід зробити щось максимально простим, але не простішим. "Рішення" Вестона не вирішує проблему ОП (немає карти), а колекція імен створює тривалішу витік пам'яті (так, у Java є витоки пам'яті).
dasblinkenlight

1

ви повинні зробити конструкторів приватними та створити такі методи, як getNameInstance(String), якщо об'єкт з таким самим іменем вже існує (на основі статичного класу 'hastable for example), ви повертаєте це посилання, інакше ви створюєте новий об'єкт за допомогою свого приватного конструктора і додаєте його до хештелю


Ви повторили те, що я сказав у третьому абзаці запитання. Я шукав альтернативні шляхи.
Арахіс

Вибачте, я думаю, що ми відповіли майже одночасно, тому що я перевірив відповіді перед повідомленням. Насправді я спробував відповісти на це на StackOverflown, і чим це було перенесено.
HericDenis

1

Спробуйте виконати наступне. Ви повинні вести облік кожного створеного вами об'єкта. Для цього я використовую List. І зробив конструктор класу приватним, так що попередня перевірка може бути застосована перед створенням екземпляра

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? Ви написали псевдокод? Або будь-якою іншою мовою? Вкажіть це прямо.
Rohit Jain

Схоже на C #. Акшай, ти повинен вивчити інші колекції, крім List. Це було б добре підходити Dictionary<string,UniqueName>.
Вестон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.