Java: использование двух связанных объектов
Я разрабатываю игровой движок в Java.
В основе этого механизма существуют два класса Asset и Attribute, где Asset имеет список атрибутов. Большинство атрибутов не требуют ссылки на свой атрибут, что означает, что атрибуты могут и часто появляются в списках более чем одного объекта. Тем не менее, существует расширение атрибута UniqueAttribute, который является реализацией для тех, которые являются специфическими для их Asset, и использовать ссылку назад.
-
В идеале мой метод addAttribute объекта будет выглядеть примерно так, если я вырезаю другой код:
public void addAttribute(Attribute attribute){
if(attribute instanceof UniqueAttribute)
((UniqueAttribute)attribute).setAsset(this);
attributeList.add(attribute);
}
К сожалению, поскольку они живут в разных пакетах, UniqueAttribute.setAsset() должен быть общедоступным. Это оставляет метод открытым для внешних пользователей движка, с которым можно было бы взаимодействовать, и хотя я мог просто отбросить его, сказав, что использование этого метода напрямую является ошибкой - кажется довольно неряшливым.
-
Второй вариант - предоставить UniqueAttribute с помощью Asset on construction, что означает, что код в точке создания будет выглядеть примерно так:
asset.addAttribute(new UniqueAttribute(asset));
В то время как я могу добавить check-and-throwable или assert для подтверждения того, что правильный актив передан, я в основном полагаюсь на пользователя для подключения двух, что я также предпочел бы не делать.
-
Третий вариант - укусить пулю и поместить 50 Java файлов в один и тот же пакет, чтобы я мог просто использовать стандартную видимость.
Есть ли какой-то шаблон или что-то, что поможет связать эти два вместе, не подвергая проводам, или заставляя меня вкладывать все в один массивный пакет?
Неповторимое разглашение: мне всегда было неловко, что концепция подпакетов в java действительно не была расширена каким-либо значимым образом. Подпакет, поскольку речь идет о java, - это просто другой пакет, и было много случаев, когда я мог делать с более важными модификаторами видимости, связанными с этим.
Ответы
Ответ 1
Мое предположение заключалось бы в том, что Asset, Attribute и UniqueAttribute должны быть в одном пакете (возможно, вместе с несколькими другими ядрами "двигательных" классов). Затем вы можете использовать стандартную видимость пакета для UniqueAttribute.setAsset.
Вам не нужно класть все другие классы в один и тот же пакет - ваш метод Asset.addAttribute должен быть общедоступным и доступным из других пакетов, поэтому остальная часть вашего приложения может просто использовать это напрямую.
Таким образом, решение можно назвать " 3 -" в вашей категоризации.
В качестве еще нескольких общих моментов рассмотрим также:
- Если вам действительно нужна сложность обоих атрибутов и UniqueAttributes - я не уверен, что вы действительно это делаете, предварительно реализовав достаточно сложную модель игровых объектов, не требуя ничего похожего на UniqueAttribute. Если UniqueAttribute "нуждается в обратной ссылке", возможно, он пытается быть слишком умным/делать слишком много?
- Даже если вам действительно нужны оба, действительно ли вы хотите написать код, который относится к ним одинаково/как часть той же иерархии объектов? они кажутся совершенно концептуально разными, и вы в конечном итоге напишите много условного кода, если вы соберете два.....
- Существуют различные преимущества атрибутов, которые постоянно разделяются и неизменяемы - лучше всего использовать память, concurrency и тестируемость между прочим. И поскольку они, по-видимому, довольно малы, стоимость семантики копирования на запись тривиальна в тех случаях, когда она вам нужна.
Ответ 2
Я бы добавил метод обратного вызова в атрибут, который вызывается, когда экземпляр атрибута добавляется в Asset:
class Attribute {
protected void addedToAsset(Asset asset) {
// do nothing
}
}
Этот метод будет вызываться в методе addAttribute
class Asset {
public void addAttribute(Attribute attribute) {
attributeList.add(attribute);
attribute.addedToAsset(this);
}
}
И метод будет переопределен в UniqueAttribute, чтобы управлять ссылкой с Asset:
class UniqueAttribute extends Attribute {
Asset asset;
protected void addedToAsset(Asset asset) {
// manage the previous link if needed
if (this.asset != null) { ... }
this.asset = asset;
}
}
С учетом этого решения Asset и Attribute должны быть размещены в одном пакете. Но UniqueAttribute может быть в любом пакете, который вы хотите.
Ответ 3
Измените свой вариант scond
asset.addAttribute(new UniqueAttribute(asset));
вот так:
class UniqueAttribute {
Asset asset;
public UniqueAttribute(Asset asset) { this.asset = asset; asset.addAttribute(this); }
}
Сделайте аналогичный подход для не уникального атрибута. Это означает, что вместо использования addAttribute() извне используйте его только внутри конструкторов.
Другой вариант заключается в добавлении двух методов factory к Asset: createAttribute() и createUniqueAttribute();
Ответ 4
Ну, в основном вы хотите сделать 3 вещи:
- сделать метод setAsset видимым внутри пакета, содержащего класс Asset
- Скрыть метод setAsset из всех других пакетов
- не используйте подпакеты для достижения этого
Это немного проблематично: если вы объявите общедоступным этот метод в классе атрибута, то все остальные классы, включая этот пакет (пусть вызывают его AttributePackage), вы не можете помешать пользователю включать какой-либо этот пакет.
С другой стороны, вы можете сделать следующее:
- создайте интерфейс, содержащий только метод Attribute, который должен использовать пользователь, позвоните ему AttributeInterface
- make Атрибут реализует этот интерфейс
- добавить AttributeInterface в новый пакет
Пользователь, который хочет использовать класс Attribute, должен использовать его через AttributeInterface, тем временем
Asset будет использовать класс Attribute напрямую, чтобы иметь доступ ко всем методам.
Я приведу пример:
//attribute interface package
public interface AttributeInterface{
public void publicAttributeMethodClientShouldUse();
}
//attribute package
public class Attribute{
public void setAsset(Asset a);
public void publicAttributeMethodClientShouldUse();
}
Asset будет ссылаться непосредственно на атрибут, тогда как пользователь должен ссылаться на AttributeInterface. Надеюсь быть ясным.
Ответ 5
Во-первых, эти сущности кажутся настолько тесно связанными, что они помещаются в один и тот же пакет. Я бы поместил их в один и тот же пакет и сделал метод addAttribute package-private.
Имейте в виду, что с момента появления AccessibleObject в Java видимость и контроль доступа на языке стали просто косметическими... Любой, кто использует вашу библиотеку, может захватить ваши классы и сделать доступными и модифицируемыми частные методы и поля! Черт, они могут даже изменить членов final
! Поэтому не придавайте особого значения аспекту видимости и просто убедитесь, что поток вашей модели и метода имеет смысл для ваших пользователей и работает правильно.