Это - хорошая практика для использования объектов области в качестве ключей?

Хорошая практика, должен использовать объекты области в качестве ключей для карт (или "получить" методы), или лучше просто использовать идентификатор объекта области?

Более просто объяснить с примером. Скажем, у меня есть класс Человека, класс Клуба и класс Членства (который соединяет другие два). Т.е.

public class Person {
    private int id; // primary key
    private String name;
}

public class Club {
    private String name; // primary key
}

public class Membership {
    private Person person;
    private Club club;
    private Date expires;
}

Или что-то как этот. Теперь, я хочу добавить метод getMembership в Клуб. Вопрос, должен этот метод брать объект Человека:

public Membership getMembership(Person person);

или, идентификатор человека:

public Membership getMembership(int personId);

Который является самым идиоматичным, который является самым удобным, который наиболее подходит?

Править: Много очень хороших ответов. Я пошел с не представлением идентификатора, потому что "Человек" (как Вы, возможно, поняли, мой реальный домен, не имеет никакого отношения к людям и клубам...) экземпляры легко доступны, но на данный момент это внутренне хранится в HashMap, хешированном на идентификаторе - но по крайней мере я выставляю его правильно в интерфейсе.

17
задан waxwing 24 June 2010 в 08:42
поделиться

13 ответов

Не используйте человека id, это просто плохая идея по всем упомянутым причинам. Вы заперетесь в дизайне. Приведу пример.

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

Но что, если в будущем вы захотите расширить понятие членства до «членства в семье», для чего вы создадите таблицу «Семья» и класс «Семья». В хорошем OO-стиле вы извлекаете интерфейс Family и Person с именем Member. Пока оба класса правильно реализуют методы equals и hashCode, никакого другого кода трогать не придется. Лично я бы сразу определил интерфейс Member.

public interface Member {
}

public class Person implements Member {
    private int id; // primary key
    private String name;
}

public class Family implements Member {
   private int id;
   private String name;
}

public class Club {
    private String name; // primary key
}

public class Membership {
   private Member member;
   private Club club;
   private Date expires;
}

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

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

5
ответ дан 30 November 2019 в 13:45
поделиться

Ого. Сделайте резервную копию здесь. Метод getMembership () не входит в клуб . Он принадлежит к набору всех членств, которые вы еще не реализовали.

1
ответ дан 30 November 2019 в 13:45
поделиться

Предположим, что это идентификатор базы данных или что-то, что используется только для индексации (а не что-то вроде SSN), тогда в идеальной системе наличие идентификатора является деталью реализации.

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

Конечно, я бы убедился, что реализовал hashCode и equals () и хорошо задокументировал, что они означают.

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

4
ответ дан 30 November 2019 в 13:45
поделиться

Если у вас уже есть объект, нет причин вытаскивать идентификатор, чтобы получить хеш-ключ.

Пока идентификаторы всегда уникальны, реализуйте hashCode () для возврата идентификатора и также реализуйте equals ().

Скорее всего, каждый раз, когда вам понадобится Членство, у вас уже будет Человек, поэтому код и путаница сохранятся позже.

0
ответ дан 30 November 2019 в 13:45
поделиться

Прежде всего, я бы поместил любые геттеры такого рода в DAO (а не в модель). Затем я бы использовал сам объект в качестве параметра, а то, что происходит внутри метода, является деталью реализации.

0
ответ дан 30 November 2019 в 13:45
поделиться

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

Если окажется, что на самом деле требуются данные от сущности Person, то DAO или фабрика для этого человека не потребуются.

Это можно легко вызвать, если ваш язык и / или ORM позволяют использовать прокси-объекты (и если у вас есть удобный способ создания этих прокси).

Но давайте будем честными. Если вы спрашиваете о членстве человека, скорее всего, у вас уже есть этот экземпляр Person в памяти, когда вы вызываете этот метод.

Далее, в «стране инфраструктуры» есть также понятие о деталях реализации, которое Ури уже упоминал, когда я писал этот ответ (черт возьми, это было быстро, братан!). Чтобы быть конкретным, что, если бы вы решили, что эта концепция «Человек» внезапно имеет составной первичный ключ / идентификатор в базовой базе данных ... Вы бы теперь использовали класс идентификатора? Может быть, использовать тот прокси, о котором мы говорили?

TL; DR версия

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

4
ответ дан 30 November 2019 в 13:45
поделиться

Я обычно предпочитаю меньше значит больше.Чем меньше информации требуется для вызова вашего метода, тем лучше. Если вы знаете ID, требуйте только ID.

Если хотите, предоставьте дополнительные перегрузки, которые принимают дополнительные параметры, такие как весь класс.

0
ответ дан 30 November 2019 в 13:45
поделиться

Если вы действительно занимаетесь объектно-ориентированным дизайном, то вам нужна идея сокрытия информации. Как только вы начнете вешать внутренние типы полей объекта person в общедоступном интерфейсе методов объекта членства, вы начнете заставлять внешних разработчиков (пользователей) ваших объектов начать изучать все виды информации о том, что такое объект person и как он хранится, и какой у него идентификатор.

Еще лучше, поскольку человек может иметь членство, почему бы вам просто не повесить метод getMemberships на класс person. Кажется гораздо более логичным спросить человека, какое у него членство, чем спрашивать «членство», к каким клубам может принадлежать данное лицо ...

Править - поскольку OP обновился, чтобы указать, что это сам членство, которое его интересует, а не только как связь между Человеком и Клубом, я обновляю свой ответ.

Короче говоря, класс «Клуб», который вы определяете, теперь вы просите вести себя как «состав клуба». Клуб имеет состав , это не состав . Список может иметь несколько функций, в том числе способы поиска лиц, принадлежащих к клубу. Помимо поиска человека по его идентификатору клуба, вы можете захотеть найти его по SSN, имени, дате вступления и т. Д. Мне это говорит о том, что в классе «Club» есть метод getRoster (), который возвращает структуру данных, которая может искать всех участников клуба. Назовите это коллекцией.Тогда возникает вопрос: можете ли вы использовать методы в уже существующих классах коллекций для удовлетворения потребностей, которые вы определили до сих пор, или вам нужно создать собственный подкласс коллекции, чтобы предоставить соответствующий метод для поиска записи членства.

Поскольку ваша иерархия классов, скорее всего, поддерживается базой данных, и вы, вероятно, беретесь за загрузку информации из базы данных, и не обязательно хотите получать всю коллекцию только для того, чтобы получить одно членство, вы можете создать новый класс. Этот класс можно было бы назвать, как я уже сказал, «Ростер». Вы получите его экземпляр в результате вызова getRoster () класса «club». Вы должны добавить к классу методы «поиска» на основе любых критериев поиска, которые вы хотите, чтобы была «общедоступная» информация о человеке… имя, clubID, personID, SSN и т. Д.

Мой первоначальный ответ применим только в том случае, если «членство» - это чисто отношение, указывающее, к каким клубам принадлежат люди.

3
ответ дан 30 November 2019 в 13:45
поделиться

Если нет значительного преимущества, полученного в другом месте, можно сказать, что ключи в map должны иметь однозначные значения, если это вообще возможно. Тем не менее, обращая внимание на equals () и hashCode (), вы можете заставить любой объект работать как ключ, но equals () и hashCode () - не очень приятные вещи, на которые нужно обращать внимание. Вы будете счастливее, если будете использовать идентификаторы в качестве ключей.

0
ответ дан 30 November 2019 в 13:45
поделиться

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

Если у меня есть удостоверение личности, сколько работы потребуется, чтобы получить человека? Это может быть «легко», но для этого нужно обратиться к хранилищу данных, а это медленно ...

Если у меня есть объект Person, сколько работы потребуется для получения идентификатора? Простой доступ к членам. Быстро и доступно.

1
ответ дан 30 November 2019 в 13:45
поделиться

На самом деле, я бы назвал его по id, но немного рефакторинг исходного дизайна:

public class Person {
    private int id; // primary key
    private String name;
}

public class Club {
    private String name; // primary key
    private Collection<Membership> memberships;
    public Membership getMembershipByPersonId(int id);
}

public class Membership {
    private Date expires;
    private Person person;
}

или

public class Person {
    private int id; // primary key
    private String name;
    private Membership membership;
    public Membership getMembership();
}

public class Club {
    private String name; // primary key
    private Collection<Person> persons;
    public Person getPersonById(int id);
}

public class Membership {
    private Date expires;
}
0
ответ дан 30 November 2019 в 13:45
поделиться

IMO, я думаю, это очень сильно зависит от потока приложения - есть ли у вас Person , доступный, когда вы хотите получить членство Детали? Если да, выберите:
public Membership getMembership (Person person);

Кроме того, я не вижу причин, по которым Club не может отслеживать членство на основе Person ID , а не фактический объект - я думаю, это означает, что вам не нужно реализовывать методы hashCode () и equals () . (Хотя это всегда хорошая практика).

Как сказал Ури, вы должны задокументировать замедление, что два объекта Person равны, если их ID равны.

1
ответ дан 30 November 2019 в 13:45
поделиться

Как описано другими: используйте объект.

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

Переход на long или BigNumber оказался непростым делом, потому что люди стали очень изобретательными с именами. Некоторые использовали

int tranNum

, некоторые использовали

int transactionNumber

, некоторые использовали

int trannNum

(с орфографическими ошибками).

Некоторые люди стали действительно изобретательными ...

Это был беспорядок, и разбираться с ним было кошмаром. В итоге я перебрал весь код вручную и преобразовал его в объект TransactionNumber.

По возможности скрывайте детали.

1
ответ дан 30 November 2019 в 13:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: