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