Как использовать полиморфизм вместо instanceof? (И почему?) - PullRequest
12 голосов
/ 16 ноября 2010

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

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

Что значит предпочитать полиморфизм instanceof и, кстати, почему он лучше?* Редактировать: Я понимаю, что такое полиморфизм;я упускаю то, как можно было бы использовать это вместо instanceof.

Ответы [ 7 ]

11 голосов
/ 16 ноября 2010

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

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

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

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

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

3 голосов
/ 16 ноября 2010

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

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

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

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

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

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

2 голосов
/ 16 ноября 2010

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

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

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

1 голос
/ 16 ноября 2010

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

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

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

Намного лучше, не так ли?

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

0 голосов
/ 27 марта 2012

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

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

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

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.

Это не более OO, чем instanceof на мой взгляд, но опять же я не считаю instanceof изначально "не-ОО".

0 голосов
/ 16 ноября 2010

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

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

0 голосов
/ 16 ноября 2010

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

class Shape
{
    abstract void draw();
}

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

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

...