Чистый код. Создание, анализ и рефакторинг (Мартин) - страница 78


Листинг 6.5. Процедурные фигуры

>public class Square {

>  public Point topLeft;

>  public double side;

>}


>public class Rectangle {

>  public Point topLeft;

>  public double height;

>  public double width;

>}


>public class Circle {

>  public Point center;

>  public double radius;

>}


Листинг 6.5 (продолжение)

>public class Geometry {

>  public final double PI = 3.141592653589793;

>  public double area(Object shape) throws NoSuchShapeException

>  {

>    if (shape instanceof Square) {

>      Square s = (Square)shape;

>      return s.side * s.side;

>    }

>    else if (shape instanceof Rectangle) {

>      Rectangle r = (Rectangle)shape;

>      return r.height * r.width;

>    }

>    else if (shape instanceof Circle) {

>      Circle c = (Circle)shape;

>      return PI * c.radius * c.radius;

>    }

>    throw new NoSuchShapeException();

>  }

>}

Объектно-ориентированный программист недовольно поморщится и пожалуется на процедурную природу реализации — и будет прав. Но возможно, его презрительная усмешка не обоснована. Подумайте, что произойдет при включении в Geometry функции perimeter(). Классы фигур остаются неизменными! И все остальные классы, зависящие от них, тоже остаются неизменными! С другой стороны, при добавлении новой разновидности фигур мне придется изменять все функции Geometry, чтобы они могли работать с ней. Перечитайте еще раз. Обратите внимание на то, что эти два условия диаметрально противоположны.

Теперь рассмотрим объектно-ориентированное решение из листинга 6.6. Метод area() является полиморфным, класс Geometry становится лишним. Добавление новой фигуры не затрагивает ни одну из существующих функций, но при добавлении новой функции приходится изменять все фигуры![24]


Листинг 6.6. Полиморфные фигуры

>Polymorphic Shapes

>public class Square implements Shape {

>  private Point topLeft;

>  private double side;


>  public double area() {

>    return side*side;

>  }

>}

>public class Rectangle implements Shape {

>  private Point topLeft;

>  private double height;

>  private double width;


>  public double area() {

>    return height * width;

>  }

>}


>public class Circle implements Shape {

>  private Point center;

>  private double radius;

>  public final double PI = 3.141592653589793;


>  public double area() {

>    return PI * radius * radius;

>  }

>}

И снова мы наблюдаем взаимодополняющую природу этих двух определений. В этом проявляется основополагающая дихотомия между объектами и структурами данных.

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