Java: применение двухсвязных объектов - PullRequest
10 голосов
/ 20 сентября 2011

Я разрабатываю игровой движок на Java.

В основе этого механизма лежат два класса Asset и Attribute, где у Asset есть список Attributes. Большинству Атрибутов не требуется ссылка на свой Атрибут, это означает, что Атрибуты могут и часто появляются в списках более чем одного Актива. Однако существует расширение Attribute, называемое UniqueAttribute, которое является реализацией для тех, которые являются специфическими для их Актива и используют обратную ссылку.

  1. В идеале, метод addAttribute моего Актива выглядел бы примерно так, если бы я вырезал другой код:

    public void addAttribute(Attribute attribute){
      if(attribute instanceof UniqueAttribute)
        ((UniqueAttribute)attribute).setAsset(this);
      attributeList.add(attribute);
    }
    

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

  2. Второй вариант - предоставить UniqueAttribute Asset on construction, что означает, что код в точке создания будет выглядеть примерно так:

    asset.addAttribute(new UniqueAttribute(asset));
    

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

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

Есть какой-то шаблон или что-то, что поможет связать эти два вместе, не подвергая проводам, или заставляя меня собрать все в одну массивную упаковку?

Неактуальные слова: мне всегда не нравилось, что концепция подпакетов в java не была расширена каким-либо значимым образом. Подпакет, с точки зрения java, - это просто другой пакет, и я мог бы сделать много раз с помощью модификаторов видимости, непосредственно связанных с этим.

Ответы [ 5 ]

2 голосов
/ 20 сентября 2011

Я бы добавил метод обратного вызова в атрибуте, который вызывается при добавлении экземпляра атрибута в актив:

class Attribute {
    protected void addedToAsset(Asset asset) {
        // do nothing
    }
}

Этот метод вызывается в методе addAttribute

class Asset {
    public void addAttribute(Attribute attribute) {
       attributeList.add(attribute);
       attribute.addedToAsset(this);
    }

}

И метод будет переопределен в UniqueAttribute для управления ссылкой с активом:

class UniqueAttribute extends Attribute {
    Asset asset;
    protected void addedToAsset(Asset asset) {
        // manage the previous link if needed
        if (this.asset != null) { ... }
        this.asset = asset;
    }
} 

В этом решении Актив и Атрибут должны быть помещены в один пакет. Но UniqueAttribute может быть в любом пакете, который вы хотите.

2 голосов
/ 20 сентября 2011

Мое предложение будет заключаться в том, что Asset, Attribute и UniqueAttribute должны быть в одном пакете (возможно, вместе с несколькими другими базовыми классами "движка"). Затем вы можете использовать стандартную видимость пакета для UniqueAttribute.setAsset.

Вам не нужно помещать все другие классы в один и тот же пакет - ваш метод Asset.addAttribute должен быть общедоступным и доступным из других пакетов, чтобы остальная часть вашего приложения могла просто использовать это напрямую.

Таким образом, решение можно назвать " 3- " в вашей классификации.

В качестве некоторых более общих моментов также рассмотрим:

  • Если вам действительно нужна сложность как Attributes, так и UniqueAttributes - я не уверен, что вы действительно это делаете, предварительно реализовав достаточно сложную модель игровых объектов, не требуя ничего, что выглядело как UniqueAttribute. Если UniqueAttribute «нужна обратная ссылка», возможно, он пытается быть слишком умным / делать слишком много?
  • Даже если вам нужны оба варианта, действительно ли вы хотите написать код, который обрабатывает их одинаково / как часть одной и той же иерархии объектов? они кажутся совершенно концептуально разными, и вы в конечном итоге напишите много условного кода, если вы сопоставите их .....
  • Существуют и другие преимущества, связанные с тем, что атрибуты постоянно совместно используются и являются неизменяемыми - среди прочего это лучше для использования памяти, параллелизма и тестируемости. И поскольку они, по-видимому, довольно малы, стоимость семантики копирования при записи является тривиальной в тех случаях, когда она вам нужна.
1 голос
/ 20 сентября 2011

Измените параметр scond

asset.addAttribute(new UniqueAttribute(asset)); 

следующим образом:

class UniqueAttribute {
Asset asset;
     public UniqueAttribute(Asset asset) { this.asset = asset; asset.addAttribute(this); }
}

Используйте аналогичный подход для неуникального атрибута.Это означает, что вместо использования addAttribute () извне, используйте его только внутри конструкторов.

Другой вариант - добавить в Asset два фабричных метода: createAttribute () и createUniqueAttribute ();

0 голосов
/ 20 сентября 2011

Во-первых, эти сущности do кажутся настолько тесно связанными, что помещаются в одну и ту же упаковку.Я бы поместил их в один и тот же пакет и сделал бы метод addAttribute package-private.

Имейте в виду, что с момента появления AccessibleObject в Java видимость и управление доступом в языке стали просто косметическими.Любой, кто использует вашу библиотеку, может захватить ваши классы и сделать приватные методы и поля доступными и изменяемыми!Черт, они могут даже изменять final участников!Поэтому не придавайте особого значения аспекту видимости, а просто убедитесь, что модель и поток методов имеют смысл для ваших пользователей и работают правильно.

0 голосов
/ 20 сентября 2011

Ну, в общем, вы хотите сделать 3 вещи:

  1. сделать метод setAsset видимым внутри пакета, содержащего класс Asset
  2. скрыть метод setAsset от всех других пакетов
  3. не используйте подпакеты для достижения этого

Это немного проблематично: если вы объявите public этот метод в классе Attribute для всех других классов, включая этот пакет (назовем его AttributePackage), вы не сможете запретить пользователю включать где-то этот пакет.

С другой стороны, вы можете сделать следующее:

  1. создать интерфейс, содержащий только метод Attribute, который должен использовать пользователь, назовем его AttributeInterface
  2. заставит Атрибут реализовать этот интерфейс
  3. добавить 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();
}

Актив будет ссылаться непосредственно на Атрибут, а пользователь должен ссылаться на Атрибут. Надеюсь, что будет ясно.

...