Я не уверен, что я буду отвечать ваш вопрос, но я надеюсь, что могу добавить немного к обсуждению. Я попытаюсь вернуться позже с более общим ответом, но в этом я сосредоточусь только на вашем примере выше.
Проблема с примером, приведенным в вопросе, состоит в том, что он основан на арифметических операциях и делает его неотъемлемо сложным, поскольку реализация данного оператора изменяется в зависимости от типов его операндов.
Я думаю, что проблема немного затмевает вопрос, например. мы могли бы потратить время, пытаясь сделать ваш пример работы вместо того, чтобы иметь дело с несколькими проблемами отправки.
Одним из способов заставить ваш код работать будет думать с другой точки зрения. Вместо того, чтобы определять абстракцию, называемую Operator
, мы могли бы признать присущий характер операндов, а именно, что они должны обеспечить реализацию любой возможной операции, которая может повлиять на них. В объектно-ориентированных терминах каждый операнд объединяет множество действий, которые могут повлиять на них.
Итак, представьте себе, что у меня есть интерфейс Operand
, который определяет все возможные операции, поддерживаемые операндом. Обратите внимание на то, что я не только определен один метод дисперсии на все возможный известном операнд, но и один метод для общего случая другого неизвестного операнда:
interface Operand {
Operand neg();
Operand add(Int that);
Operand add(Decimal that);
Operand add(Operand that);
Operand mult(Int that);
Operand mult(Decimal that);
Operand mult(Operand that);
Operand sub(Int that);
Operand sub(Decimal that);
Operand sub(Operand that);
}
Тогда сейчас считает, что у нас было две реализаций этого: Int
и Decimal
(I для простоты будет использовать десятичную матрицу вместо матрицы вашего примера).
class Int implements Operand {
final int value;
Int(int value) { this.value = value; }
public Int neg(){ return new Int(-this.value); }
public Int add(Int that) { return new Int(this.value + that.value); }
public Decimal add(Decimal that) { return new Decimal(this.value + that.value); }
public Operand add(Operand that) { return that.add(this); } //!
public Int mult(Int that) { return new Int(this.value * that.value); }
public Decimal mult(Decimal that) { return new Decimal(this.value * that.value); }
public Operand mult(Operand that) { return that.mult(this); } //!
public Int sub(Int that) { return new Int(this.value - that.value); }
public Decimal sub(Decimal that) { return new Decimal(this.value - that.value); }
public Operand sub(Operand that) { return that.neg().add(this); } //!
public String toString() { return String.valueOf(this.value); }
}
class Decimal implements Operand {
final double value;
Decimal(double value) { this.value = value; }
public Decimal neg(){ return new Decimal(-this.value); }
public Decimal add(Int that) { return new Decimal(this.value + that.value); }
public Decimal add(Decimal that) { return new Decimal(this.value + that.value); }
public Operand add(Operand that) { return that.add(this); } //!
public Decimal mult(Int that) { return new Decimal(this.value * that.value); }
public Decimal mult(Decimal that) { return new Decimal(this.value * that.value); }
public Operand mult(Operand that) { return that.mult(this); } //!
public Decimal sub(Int that) { return new Decimal(this.value - that.value); }
public Decimal sub(Decimal that) { return new Decimal(this.value - that.value); }
public Operand sub(Operand that) { return that.neg().add(this); } //!
public String toString() { return String.valueOf(this.value); }
}
Тогда я могу это сделать:
Operand a = new Int(10);
Operand b = new Int(10);
Operand c = new Decimal(10.0);
Operand d = new Int(20);
Operand x = a.mult(b).mult(c).mult(d);
Operand y = b.mult(a);
System.out.println(x); //yields 20000.0
System.out.println(y); //yields 100
Operand m = new Int(1);
Operand n = new Int(9);
Operand q = m.sub(n);
Operand t = n.sub(m);
System.out.println(q); //yields -8
System.out.println(t); //yeilds 8
Ключевыми моментами здесь являются:
- Каждая реализация операндов работает аналогично шаблону посетителя, в том смысле, что каждый operand содержит функцию отправки для каждой возможной комбинации, которую вы можете получить.
- Трудная часть - это метод, действующий на любом
Operand
. Это место, где мы используем мощность отправки посетителя, потому что мы знаем точный тип this
, но не точный тип that
, поэтому мы вынуждаем отправку, делая that.method(this)
, и проблема решена!
- Однако, поскольку мы инвертируем порядок оценки, это имеет проблему с арифметическими операциями, которые не являются коммутативными, как вычитание. Вот почему я делаю вычитание с помощью добавления (т. Е.1-9 соответствует 1 + -9). Так как сложение является коммутативным.
И у вас оно есть. Теперь я нашел решение для конкретного примера, но я не предоставил хорошее решение проблемы с несколькими отправками, которые вы изначально имели. Вот почему я сказал, что пример недостаточно хорош.
Возможно, вы могли бы предоставить лучший пример, например, тот, который находится на странице Википедии для Multiple Dispatch.
Однако, я думаю, что решение, вероятно, всегда будет, уменьшите проблему до серии отдельных рассылок, разрешенных каким-то шаблоном посетителя, как тот, который я сделал. Если у меня будет время, я постараюсь дать ему ответ позже на более общий ответ, а не только на этот конкретный пример.
Но, надеюсь, этот пост способствует дальнейшему обсуждению, и, к счастью, это шаг в направлении реального ответа.
Скомпилирован ли ваш пример? (Преобразование числа в операнд). и не могли бы вы уточнить немного больше, что проблема? Его довольно неясно сейчас, что вы ищете – n247s
Я не писал конструкторы, поэтому он не компилируется, это просто пример. Я хочу вызвать Multiplication :: eval (Integer, Matrix), когда вызываемый объект имеет статический тип. Оператор и тип времени выполнения. Умножение и его аргументы имеют тип статического типа. Операнд и типы выполнения Integer и Matrix. Существует шаблон для этого, когда метод eval имеет только один аргумент, он называется множественной отправкой. Я хочу то же самое для 2 (или более) аргументов. – biowep
Как только вы определяете 'Operator mul;', вы ограничены контрактом, определяемым 'Operator'. Поэтому, если вы не определите метод 'eval (Integer, Matrix)' в классе 'Operator' (или один из его родителей), вы не сможете его вызывать. Вместо того, чтобы объявлять его типом 'Operator', вы можете изменить его на' Multiplication'. – nickb