Возможно ли, чтобы метод ввода был одним из двух возможных классов в Java? - PullRequest
1 голос
/ 13 декабря 2011

У меня есть два класса, Foo1 и Foo2, которые не связаны иерархически, поэтому экземпляр Foo1 не является экземпляром Foo2 и наоборот.

В классе, отличном от этого, у меня есть метод doStuffWithFoo, который в идеале должен принимать либо Foo1, либо Foo2 в качестве входных данных, а затем вести себя соответствующим образом. Вот основная идея, которая мне нужна:

private double[] doStuffWithFoo(Fooi foo, Bar bar1, Bar bar2, double fortyTwo) {
    Fooi changingFoo = foo;
    double[] fooBars = new double[N];
    for (int i=0; i<N; ++i) {
        double[] array = getArrayFromFoo(foo,bar1,fortyTwo);
        double fooBar = getDoubleFromArray(array);
        fooBars[i] = fooBar;
        foo = foo.getNextFoo();
    }
    return fooBars;
}

где я написал два совершенно разных метода

private double[] getArrayFromFoo(Foo1 foo, Bar bar, double fortyTwo)
private double[] getArrayFromFoo(Foo2 foo, Bar bar, double fortyTwo)

и каждый класс Foo реализует свой собственный метод getNextFoo(), который возвращает тот же тип Foo, который он принимает.

Метод getArrayFromFoo на самом деле не имеет ничего общего с классами Foo, и он принимает входные данные от класса, в котором этот метод живет, поэтому нет смысла создавать общий интерфейс для Foo s.

Я вижу, как справиться с ситуацией:

  1. Заставьте doStuffWithFoo принять универсальный объект в качестве входных данных, затем проверьте, является ли класс Foo1, Foo2 или что-то еще. Проблема этого подхода в том, что я настоятельно предпочитаю ошибки времени компиляции, а не ошибки времени выполнения.

  2. Напишите две версии doStuffWithFoo так же, как я написал две версии getArrayFromFoo. Проблема с этим подходом состоит в том, что я буду дублировать большой объем кода, который может вызвать несоответствия, когда код изменяется в какой-то момент в будущем.

Есть ли другой вариант? Идеальное решение, которое я имею в виду, - если есть способ объявить тип ввода равным Foo1 или Foo2, что похоже на подход 1, но с мгновенными ошибками. Есть ли способ сделать это в Java?

Ответы [ 4 ]

9 голосов
/ 13 декабря 2011

Попробуйте определить интерфейс:

public interface HasDoubleArray {

   public double[] getDoubleArray();
}

Затем пусть каждый Foo1 и Foo2 реализуют этот интерфейс, переопределяя метод getDoubleArray() для возврата double[] по-своему. Затем вы можете позвонить по этому номеру в своем коде направления:

private double[] doStuffWithFoo(HasDoubleArray hda) {
   double[] array = hda.getDoubleArray();
   // DO STUFF WITH array
   return array;
}  
6 голосов
/ 13 декабря 2011

Что по этому поводу:

private double[] doStuffWithFoo(Foo1 foo) {
    double[] array = getArrayFromFoo1(foo);
    doCommonStuff(array);
    return array;
}


private double[] doStuffWithFoo(Foo2 foo) {
    double[] array = getArrayFromFoo2(foo);
    doCommonStuff(array);
    return array;
}

private void doCommonStuff(double[] array) {
    // common stuff
}
1 голос
/ 13 декабря 2011

Я почти уверен, что у вас здесь есть некоторые большие проблемы с дизайном и факторингом, учитывая ответ, который вы дали на ответ «использовать интерфейс», который вы дали Кублай Хану, и это замечание, которое вы сделано:

Метод getArrayFromFoo на самом деле не имеет ничего общего с классами Foo, и он принимает данные из класса, в котором этот метод живет, поэтому нет смысла создавать общий интерфейс для Foos.

Если это действительно так, то зачем getArrayFromFoo даже принимать аргумент Foo1 или Foo2? Ну, это звучит так, как будто вы хотите посылать данные о типе (то есть делать что-то другое в зависимости от того, получили ли вы Foo1 или Foo2), но почему бы вам не сделать это, делегировав ответственность за выполнение вещи, соответствующей типу? к рассматриваемым типам?

Я понимаю желание не помещать логику в Foo1 и Foo2, которая относится к вещам, которые должным образом принадлежат классу, реализующему getArrayFromFoo, но есть другие способы обойти это - например, использовать Шаблон посетителя, чтобы Foo1 и Foo2 перезвонит вам с соответствующей вещью.

Кроме того, беспокойство, которое вы испытываете при дублировании кода, поднимает здесь еще больше красных флажков - даже если вам в конечном итоге придется писать методы, специфичные для Foo1 и Foo2, почему вы не сможете выделить дублирующиеся части в общий метод?

public interface FooVisitor<T> {
    public T visitFoo1(Foo1 foo1);
    public T visitFoo2(Foo2 foo2);
}

public interface Foo {
    public <T> T accept(FooVisitor<T> visitor);
}

public class Foo1 implements Foo {

    // ...

    public <T> T accept(FooVisitor<T> visitor) {
        return visitor.visitFoo1(this);
    }

    // ...

}

public class Foo2 implements Foo {

    // ...

    public <T> T accept(FooVisitor<T> visitor) {
        return visitor.visitFoo2(this);
    }

    // ...

}

public class ClientClass implements FooVisitor<Quux> {

    // ...

    private double[] doStuffWithFoo(Foo foo, Bar bar1, Bar bar2, double fortyTwo) {
        // do common stuff...

        // Now do the type-specific part
        Quux whatever = foo.visit(this);
    }

    public Quux visitFoo1(Foo1 foo1) {
        // do Foo1-specific stuff
    }

    public Quux visitFoo2(Foo2 foo1) {
        // do Foo2-specific stuff
    }

    // ...

}
0 голосов
/ 13 декабря 2011

Я думаю, что причиной вашей проблемы являются параметры, которые вы не выполняете проверку:

private static void doStuffWithFoo(Fooz foo, Bar b, double d) {
        Double fortyTwo;
        Bar bar;
        if(!foo.b().equals(b)||foo.d()!=d)
            throw new IllegalArgumentException();

Для этого я бы следовал способу Кублай Хана использовать один интерфейс в качестве параметра.Также я думаю, что вам не нужен параметр 2xBar.Если параметры не могут пройти тест, то я бы бросил (допустим,) IllegalArgumentException. Вы сказали, что getArrayFromFoo ничего не делает с классами, поэтому вы можете передать параметры этому методу после проверки?(если bar и double не являются экземплярами foo (или не логичны), поэтому я думаю, что JVM не может знать, что к чему относится)

class Foo implements Fooz{

    @Override
    public double d() {
        // TODO Auto-generated method stub
        return new Double(41);
    }

    @Override
    public Bar b() {
        // TODO Auto-generated method stub
        return new Bar(41);
    }

}
class Foo2 implements Fooz{

    public double d() {
        // TODO Auto-generated method stub
        return new Double(42);
    }

    @Override
    public Bar b() {
        // TODO Auto-generated method stub
        return new Bar(42);
    }



}

class Bar{
    Bar(int x){
        this.x = x;
    }
    public int x;
}

interface Fooz{
    double d();
    Bar b();

}

public class CompareFoos{

    public static void main(String[] args) {
        Foo foo = new Foo();
        Foo2 foo2 = new Foo2();
        //doStuffWithFoo(foo2, foo2.b(), foo2.d());
        doStuffWithFoo(foo2, foo.b(), foo2.d());

    }

    private static void doStuffWithFoo(Fooz foo, Bar b, double d) {
        Double fortyTwo;
        Bar bar;
        //foo creates new bar for each call thats why i didnt use !foo.b().equals(b)
        if(foo.b().x!=b.x||foo.d()!=d)
            throw new IllegalArgumentException();
        fortyTwo = d;
        bar = b;
        System.out.println(foo.getClass().getName() + " " + d + " " + b.x);
        private double[] getArrayFromFoo(foo, bar, fortyTwo)
        //more code here...

    }

}

...