Может ли Алмазная проблема действительно быть решена? - PullRequest
14 голосов
/ 18 февраля 2009

Типичная проблема в ОО-программировании - проблема алмазов. У меня есть родительский класс A с двумя подклассами B и C. A имеет абстрактный метод, B и C реализуют его. Теперь у меня есть подкласс D, который наследует B и C. Теперь проблема с алмазом в том, какую реализацию использует D, одну из B или одну из C?

Люди утверждают, что в Java нет проблем с бриллиантами. У меня может быть только множественное наследование с интерфейсами, и поскольку у них нет реализации, у меня нет проблем с алмазом. Это правда? Я так не думаю. См. Ниже:

[пример снятого автомобиля]

Всегда ли проблема с бриллиантами является причиной плохого проектирования классов, и ее не нужно решать ни программисту, ни компилятору, потому что она вообще не должна существовать?


Обновление: возможно, мой пример был неудачно выбран.

Посмотри это изображение

Diamond Problem
(источник: suffolk.edu )

Конечно, вы можете сделать Person виртуальным в C ++, и, таким образом, у вас будет только один экземпляр person в памяти, но реальная проблема сохраняется IMHO. Как бы вы реализовали getDepartment () для GradTeachingFellow? Подумайте, он может быть студентом на одном факультете и преподавать на другом. Таким образом, вы можете вернуть либо один отдел, либо другой; не существует идеального решения проблемы, и тот факт, что ни одна реализация не может быть унаследована (например, ученик и учитель могут быть интерфейсами), кажется, не решает проблему для меня.

Ответы [ 17 ]

1 голос
/ 18 февраля 2009

Я не думаю, что предотвращение конкретного множественного наследования переносит проблему с компилятора на программиста. В приведенном вами примере программисту все равно необходимо будет указать компилятору, какую реализацию использовать. Компилятор не сможет угадать, что является правильным.

Для вашего класса амфибий вы могли бы добавить метод, чтобы определить, находится ли транспортное средство на воде или на суше, и использовать его, чтобы выбрать способ перемещения. Это сохранит интерфейс без параметров.

move()
{

  if (this.isOnLand())
  {
     this.moveLikeLandVehicle();
  }
  else
  {
    this.moveLikeWaterVehicle();
  }
}
0 голосов
/ 07 февраля 2011

интерфейс A { void add (); }

интерфейс B расширяет A { void add (); }

интерфейс С расширяет А { void add (); }

класс D реализует B, C {

}

Разве это не проблема с алмазом?

0 голосов
/ 26 апреля 2010

На самом деле, если Student и Teacher оба интерфейса, это действительно решит вашу проблему. Если они являются интерфейсами, то getDepartment - это просто метод, который должен появиться в вашем классе GradTeachingFellow. Тот факт, что интерфейсы Student и Teacher обеспечивают реализацию этого интерфейса, вовсе не является конфликтом. Реализация getDepartment в вашем классе GradTeachingFellow будет защищать оба интерфейса без проблем с бриллиантами.

НО, как указано в комментарии, это не решает проблему GradStudent преподавания / того, чтобы быть TA на одном факультете и быть студентом на другом. Инкапсуляция, вероятно, то, что вы хотите здесь:

public class Student {
  String getDepartment() {
    return "Economics";
  }
}

public class Teacher {
  String getDepartment() {
    return "Computer Engineering";
  }
}

public class GradStudent {
  Student learning;
  Teacher teaching;

  public String getDepartment() {
    return leraning.getDepartment()+" and "+teaching.getDepartment(); // or some such
  }

  public String getLearningDepartment() {
    return leraning.getDepartment();
  }

  public String getTeachingDepartment() {
    return teaching.getDepartment();
  }
}

Неважно, что у GradStudent концептуально не «есть» учитель и ученик - инкапсуляция по-прежнему дорога.

0 голосов
/ 18 февраля 2009

Проблема алмазов в C ++ уже решена: используйте виртуальное наследование. Или, что еще лучше, не ленитесь и не наследуйте, когда это не нужно (или неизбежно). Что касается приведенного вами примера, это можно решить, переопределив, что значит быть способным ездить по земле или по воде. Действительно ли способность передвигаться по воде определяет транспортное средство на водной основе или это просто то, что может сделать транспортное средство? Я бы предпочел подумать, что у функции move (), которую вы описали, есть какая-то логика, которая спрашивает «где я и могу ли я на самом деле двигаться здесь?» Эквивалент bool canMove() функции, которая зависит от текущего состояния и врожденных способностей автомобиля. И вам не нужно множественное наследование, чтобы решить эту проблему. Просто используйте миксин, который по-разному отвечает на вопрос в зависимости от того, что возможно, и принимает суперкласс в качестве параметра шаблона, чтобы виртуальная функция canMove была видна через цепочку наследования.

0 голосов
/ 18 февраля 2009

Я понимаю, что это конкретный случай, а не общее решение, но похоже, что вам нужна дополнительная система, чтобы определить состояние и решить, какой тип движения () будет выполнять транспортное средство.

Похоже, что в случае транспортного средства-амфибии, вызывающий объект (скажем, «дроссель») не будет иметь представления о состоянии воды / грунта, но будет промежуточным определяющим объектом, таким как «передача» в сочетании с «тягой» control "может выяснить это, а затем вызвать move () с соответствующим параметром move (колеса) или move (опора).

0 голосов
/ 18 февраля 2009

У вас может быть проблема с алмазом в C ++ (которая допускает множественное наследование), но не в Java или в C #. Нет способа наследования от двух классов. Реализация двух интерфейсов с одним и тем же объявлением метода не подразумевает в этой ситуации, так как конкретная реализация метода может быть выполнена только в классе.

0 голосов
/ 18 февраля 2009

Проблема действительно существует. В образце AmphibianVehicle-Class нужна другая информация - поверхность. Мое предпочтительное решение - добавить метод getter / setter в класс AmpibianVehicle, чтобы изменить элемент поверхности (перечисление). Реализация теперь может делать правильные вещи, и класс остается инкапсулированным.

...