Как использовать полиморфизм вместо instanceof? (И почему?)

Если мы возьмем код ниже:

Shape p1 = new Square();
Square c1;
if(p1 instanceof Square) {
  c1 = (Square) p1;
}

что значит предпочесть полиморфизм instanceof и кстати, почему это лучше?

Edit: Я понимаю, что такое полиморфизм; мне не хватает того, как его использовать, а не instanceof.

7 ответов


основное различие между if...еще... (или переключатель, или посетитель), а между полиморфизмом-модульность. Существует так называемый принцип open-closed, который в основном означает, что при добавлении новой функции в существующую программу, чем меньше изменений вы вносите в существующий код, тем лучше (потому что каждое изменение требует некоторой работы и может привести к ошибкам). Поэтому давайте сравним количество изменений:

  • добавление нового метода (например. у вас есть paint () и getArea (), давайте добавим getCircumference ()): с решением if-else вам нужно изменить только один файл - файл, который будет содержать новый метод. С полиморфизмом вы должны изменить все свои реализации класса Shape.

  • добавление нового вида фигуры (у вас есть квадрат, круг - давайте добавим треугольник): с решением if-else вам нужно просмотреть все существующие классы с if-else и добавить новую ветвь if для треугольника; с полиморфизмом все, что у вас есть, это добавить новый класс и реализовать все необходимые в нем методы.

Так что если...еще... или полиморфизм: это зависит от модульности. Если вы ожидаете, что много новых sublasses будут добавлены позже, использовать полиморфизм; если вы ожидаете, что много новых методов будут добавлены позже, если использовать...еще..., а в класс ставят только самые "базовые" методы, такие как аксессоры. Или другими словами: когда вы ожидаете иметь много если...еще... ветви, вы должны скорее использовать полиморфизм, когда вы ожидаете несколько таких ветвей, просто оставайтесь с если...еще...

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


идея в том, что вам не нужно беспокоиться о том, с какой формой вы имеете дело. Например, если Shape определяет абстрактный метод draw (), то треугольники, квадраты и все остальное, что расширяет Shape, также будут иметь тот же метод.

простым определением полиморфизма является "обработка разных типов, как будто они одинаковы", т. е. использование одного и того же интерфейса.

в идеальном мире мы не хотим беспокоиться о том, какой конкретный тип объекта мы работа только с интерфейсом более общего типа, который охватывает все сценарии использования в своем интерфейсе.

Shape p1 = new Square();
Shape p2 = new Triangle();
p1.draw();
p2.draw();

в этом коде мы непосредственно вызываем Shape.нарисуйте () на p1 и p2. Нам все равно, что делает класс реализации, только то, что определено интерфейсом (или абстрактным родительским классом).

Edit: Что касается примера в вопросе, обычно рекомендуется избегать такого типа шаблона кода путем инкапсуляции поведения, когда вероятный. Использование instanceof можно считать запахом кода, так как вам придется обновлять все ваши условные обозначения при добавлении нового класса.


рассмотрим следующее

abstract class Shape {
   public abstract int getEdgesNumber();
}

class Square extends Shape {
    public int getEdgesNumber(){
        return 4;
    }
}

class Circle extends Shape {
    public int getEdgesNumber(){
        return 1; //not so sure it counts as one but for the example is fine ^^'
    }
}     

Shape square = new Square();
int squareEdgesNumber = square.getEdgesNumber();

Shape circle = new Circle();
int circleEdgesNumber = circle.getEdgesNumber();

A Square и Circle Как использовать тег getEdgesNumber() метод, вы просто вызываете его и получаете результат, основанный на конкретной реализации.

вам не нужно знать, имеете ли вы дело с Square или Circle, вы просто вызываете метод, который вам нужен, и полагаетесь на базовую реализацию объекта.

кроме того, посмотрите на как это объясняют врачи.


это не очень сильный пример, но вот как будет выглядеть ваш код.

Square c1 = new Square();
Shape p1 = c1;

(учитывая, что квадрат расширяет форму, конечно)

гораздо лучше, не так ли?

Что касается "почему это лучше", другие ответы дают некоторые важные моменты.


полиморфизм позволяет изменять поведение чего-либо в зависимости от типа. Не уверен, как объяснить это с вашим примером, так как вы могли бы просто назначить его квадрату сразу, если важно, что это квадрат по какой-то причине. Иногда вам нужно подкласс, поскольку он может иметь дополнительное поведение и т. д., Но рассмотрим этот пример:

class Shape
{
    abstract void draw();
}

class Square extends Shape
{
    void draw()
    {
        // square drawing goes here
    }
}

метод draw вот пример полиморфизма, поскольку у нас есть форма базового класса, которая говорит, что все формы теперь, как рисовать себя, но только квадрат умеет рисовать квадрат.


Я предполагаю, что "форма" - это интерфейс, а" квадрат " - реализация этого интерфейса.

теперь, если вам нужно вызвать метод, объявленный для интерфейса Shape (типичным примером является Shape.getArea ()), вам все равно, квадрат это или что-то еще, и вызовите эту функцию.


некоторые люди считаю, что есть время и место для instanceof, и что шаблон посетителя не всегда полная и соответствующая замена для него. Некоторые шипят и хмурятся. Промыть, повторить.

Я думаю, что ваш пример может быть улучшен, пытаясь что-то сделать значимую (как рисовать, или считать стороны, ЕТК), потому что общее соображение ООП фундаментально избежало бы ситуации вы иллюстрируете в вашем образец. Например, дизайн ООП либо объявит c1 как Shape, а не Square, или просто использовать p1 переменной.

в стороне, если вы после ситуации, когда c1 равен нулю, если это не квадрат, или установлен в p1, Если это так, существует аналогичный оператор "as", который я поклонник.

Shape p1  = (Math.random()>0.5) ? new Square() : new Circle();
Square c1 = p1 as Square;
// c1 is null or not, depending on p1's type.

это не больше, чем instanceof на мой взгляд, но опять же я не считаю instanceof быть inately "non-OO".