Подтвердить что ты не робот

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

Если взять следующий код:

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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 и a Circle оба реализуют метод getEdgesNumber(), вы просто вызываете его и получаете результат на основе конкретной реализации.

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

Кроме того, посмотрите то, как документы объясняют это.

Ответ 4

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

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

(при условии, что Square расширяет форму, конечно)

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

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

Ответ 5

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

class Shape
{
    abstract void draw();
}

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

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

Ответ 6

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

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

Ответ 7

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

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

В стороне, если вы после ситуации, когда c1 имеет значение null, если он не является квадратом, или установлен на p1, если он существует, существует аналогичный оператор "как", который я являюсь поклонником.

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

В моем представлении не больше OO, чем instanceof, но опять же я действительно не считаю, что instanceof является "не-OO".