Правильный способ получения отдельных подтипов для сотрудничества, если у каждого есть уникальные свойства - PullRequest
1 голос
/ 11 апреля 2020

Я думаю, что это скорее концептуальная проблема объектно-ориентированного программирования , чем техническая, у меня есть несколько идей, которые я мог бы использовать, но каждая из них больше похожа на хакерский обходной путь, чем на легко расширяемые решения. Я пытаюсь найти более или менее приемлемый способ решения этой проблемы.

Допустим, у меня есть интерфейс Scalar

public interface Scalar {

    public Scalar add(Scalar addend);

    ...

с двумя конкретными реализациями: RealScalar который представляет десятичные значения и RationalScalar, который представляет дроби

public class RealScalar implements Scalar {

    private double value; 

    public RealScalar(double input)
    {  
        value=input;
    }
    ...
public class RationalScalar implements Scalar {

    private int numerator,denominator;

    public RationalScalar(int numerator, int denominator)
    {  
        this.numerator=numerator;
        this.denominator=denominator;
    }
    ...

Я использую фабричный класс ScalarFactory для создания новых скаляров как способ сделать реализацию безразличной к какому типу Scalar в настоящее время упоминается.

Если каждый подтип имеет свойства (и методы), уникальные для этого подтипа (в данном случае value для RealScalar и numerator, denominator для RationalScalar) и компилятор java не может заранее знать, каким будет тип времени выполнения Scalar, в результате чего реализованные методы (объявленные в родительском интерфейсе), такие как:

    @Override
    public RealScalar multi(Scalar multiplicand)
    {
    if (getClass()!=multiplicand.getClass())
            multiplicand=multiplicand.toReal();

    return new RealScalar(value*(
        (RealScalar)multiplicand).getValue());
    }

, приведут к компиляции ошибки времени, так как toReal - это метод дочернего класса RationalScalars:

./RealScalar.java:22: error: cannot find symbol
            multiplicand=multiplicand.toReal();
                                     ^
  symbol:   method toReal()
  location: variable multiplicand of type Scalar

Как правильно заставить эти подклассы взаимодействовать?

РЕДАКТИРОВАТЬ: Я действительно получил это работает, добавив функция преобразования в интерфейс, который принял тип класса в качестве аргумента, а затем реализовал это в каждом из подтипов. Хотя это работает, я все еще не уверен, является ли это концептуально правильным. С одной стороны, возможность преобразования в другие типы значений - это то, что вы хотели бы получить от объекта, предназначенного для математической сущности, с другой - это делает подтипы слегка взаимозависимыми.

Возможные решения

1) включить метод convert в подтипы, который принимает скаляр и создает скаляр дочернего типа.

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

2) сделать значение списком обобщенных c числовых типов в интерфейсе

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

3) хранилище для RationalScalar в виде десятичной дроби и преобразуется только в дробь для отображения.

Определенно не то направление, которое я хочу go, учитывая, как трудно сохранить десятичное число в формате, который легко преобразовать в дробь, которая не в форме 2345883/4232569

...