Я вообще не знаю эту игру, но просто следую тому, что вы здесь говорите:
Возможно, многоуровневая иерархия решит ваши проблемы. Например, если и у Артефактов, и у Земель есть общее поведение, вместо того, чтобы говорить, что Артефакт расширяет Перманент, а Земля расширяет Перманент, вы можете создать другой класс, назовем его Свойством, а затем сказать, что Свойство расширяет Перманент, Артефакт расширяет Свойство, Земля расширяет Свойство. (Я имею в виду «собственность» в смысле «реальная собственность» и «личная собственность», а не собственность объекта. В любом случае.) Тогда любые свойства или данные, общие как для Артефакта, так и для Земли, могут находиться в Собственности.
Но если есть поведение, общее для Артефакта и Земли, которое не применимо к Существу, и другое поведение, общее для Артефакта и Существа, которое не применимо к Земле, этот подход не будет работать.
Класс Java не может расширять более одного класса, но, конечно, он может реализовывать любое количество классов. Таким образом, вы можете создать интерфейсы для общего поведения для любого заданного набора объектов, например, интерфейс Movable, который будет реализован с помощью Artifact и Creature, и интерфейс Property, который будет реализован с помощью Artifact и Land и т. Д. Но, как я уверен, что вы понимаете, это наследует только объявления, а не код. Таким образом, в конечном итоге вам придется реализовывать один и тот же код несколько раз.
Одна вещь, которую я иногда делал, - это создание другого класса для реализации поведения, а затем «настоящий» класс просто перенаправляет все в служебный класс. Так что если - и снова я не знаю игру, поэтому я просто придумываю примеры - и Артефакту, и Земле нужна функция «купить», вы можете создать интерфейс Property, который расширяют и Артефакт, и Землю, создать Класс PropertyImplementation, который включает в себя фактический код, и тогда у вас будет что-то вроде:
public class PropertyImplementation
{
public static void buy(Property property, Buyer buyer, int gold)
{
buyer.purse-=gold;
property.owner=buyer;
... etc, whatever ...
}
}
public interface Property
{
public static void buy(Property property, Buyer buyer, int gold);
}
public class Land extends Permanent implements Property
{
public void buy(Buyer buyer, int gold)
{
PropertyImplementation.buy(this, buyer, gold);
}
... other stuff ...
}
public class Artifact extends Permanent implements Property
{
public void buy(Buyer buyer, int gold)
{
PropertyImplementation.buy(this, buyer, gold);
}
... other stuff ...
}
Менее сложный, но иногда довольно практичный подход - просто генерировать ошибки при совершении неприменимых вызовов. Как:
public class Permanent
{
public void buy(Buyer buyer, int gold)
throws InapplicableException
{
buyer.purse-=gold;
this.owner=buyer;
... etc ...
}
}
public class Plainsman extends Permanent
{
// override
public void buy(Buyer buy, int gold)
throws InapplicableException
{
throw new InapplicableException("You can't buy a Plainsman! That would be slavery!");
}
}