SoftCraft
разноликое программирование

Top.Mail.Ru

Динамический полиморфизм и эволюция

© 2025
Александр Легалов


Содержание


Шаг седьмой. Добавление в мультиметод новой альтернативы

В принципе альтернатива уже была реализована на втором шаге. Поэтому стоит вопрос совместного расширения, связанного с подключением ее, когда реализован мультиметод.

Добавление альтернативы в мультиметод при процедурном подходе

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


  // multmethod.c
  // Обработчик специализации для прямоугольника и круга
  void MMRC(Rectangle* r1, Circle* c2, FILE* ofst) {
    fprintf(ofst, "Rectangle - Circle Combination\n");
  }
  // Обработчик специализации для треугольника и круга
  void MMTC(Triangle* t1, Circle* c2, FILE* ofst) {
    fprintf(ofst, "Triangle - Circle Combination\n");
  }
  // Обработчик специализации для круга и прямоугольника
  void MMCR(Circle* c1, Rectangle* r2, FILE* ofst) {
    fprintf(ofst, "Circle - Rectangle Combination\n");
  }
  // Обработчик специализации для круга и треугольника
  void MMCT(Circle* c1, Triangle* t2, FILE* ofst) {
    fprintf(ofst, "Circle - Triangle Combination\n");
  }
  // Обработчик специализации для двух кругов
  void MMCC(Circle* c1, Circle* c2, FILE* ofst) {
    fprintf(ofst, "Circle - Triangle Combination\n");
  }

  //------------------------------------------------------------------------------
  void Multimethod(Figure* f1, Figure* f2, FILE* ofst) {
    switch(f1->k) {
      case RECTANGLE:
        switch(f2->k) {
          case RECTANGLE:
            MMRR((Rectangle*)f1, (Rectangle*)f2, ofst);
            break;
          case TRIANGLE:
            MMRT((Rectangle*)f1, (Triangle*)f2, ofst);
            break;
          case CIRCLE:
            MMRC((Rectangle*)f1, (Circle*)f2, ofst);
            break;
          default:
            fprintf(ofst,
                  "1st is RECTANGLE. Incorrect key of figure 2 = %d\n", f2->k);
        }
        break;
      case TRIANGLE:
        switch(f2->k) {
          case RECTANGLE:
            MMTR((Triangle*)f1, (Rectangle*)f2, ofst);
            break;
          case TRIANGLE:
            MMTT((Triangle*)f1, (Triangle*)f2, ofst);
            break;
          case CIRCLE:
            MMTC((Triangle*)f1, (Circle*)f2, ofst);
            break;
          default:
            fprintf(ofst,
                  "1st is TRIANGLE. Incorrect key of figure 2 = %d\n", f2->k);
        }
        break;
      case CIRCLE:
        switch(f2->k) {
          case RECTANGLE:
            MMCR((Circle*)f1, (Rectangle*)f2, ofst);
            break;
          case TRIANGLE:
            MMCT((Circle*)f1, (Triangle*)f2, ofst);
            break;
          case CIRCLE:
            MMCC((Circle*)f1, (Circle*)f2, ofst);
            break;
          default:
            fprintf(ofst,
                  "1st is CIRCLE. Incorrect key of figure 2 = %d\n", f2->k);
        }
        break;
      default:
        fprintf(ofst, "Incorrect key of figure 1 = %d\n", f1->k);
    }
  }

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

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

Шаг 7. Добавление альтернативы в мультиметод при процедурном подходе

Добавление альтернативы в мультиметод при ОО подходе

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


  // figura.h - rласс, обобщающающий все фигуры.
  class Figure {
    public:
    // идентификация, порождение и ввод фигуры из потока
    virtual void In(std::ifstream &ifst) = 0;  // ввод
    virtual void Out(std::ofstream &ofst) = 0;     // вывод
    virtual void Out() = 0;                        // вывод
    virtual std::string Multimethod(Figure& fig2) = 0; // мультиметод
    virtual std::string FirstRectangle(Rectangle& rect) = 0;
    virtual std::string FirstTriangle(Triangle& trian) = 0;
    virtual std::string FirstCircle(Circle& circ) = 0;
  };

  // rectangle.h - прямоугольник
  class Rectangle: public Figure {
    int x, y; // ширина, высота
    public:
    // переопределяем интерфейс класса
    virtual void In(std::ifstream &ifst);  // ввод
    virtual void Out(std::ofstream &ofst);    // вывод
    virtual std::string Multimethod(Figure& fig2); // мультиметод
    virtual std::string FirstRectangle(Rectangle& rect);
    virtual std::string FirstTriangle(Triangle& trian);
    virtual std::string FirstCircle(Circle& circ);
    Rectangle(): x{0}, y{0} {} // создание без инициализации.
  };

  // triangle.h -треугольник
  class Triangle: public Figure {
    int a, b, c; // стороны
    public:
    // переопределяем интерфейс класса
    void In(std::ifstream &ifst);  // ввод
    virtual void Out(std::ofstream &ofst);    // вывод
    virtual std::string Multimethod(Figure& fig2); // мультиметод
    virtual std::string FirstRectangle(Rectangle& rect);
    virtual std::string FirstTriangle(Triangle& trian);
    virtual std::string FirstCircle(Circle& circ);
    Triangle(): a{0}, b{0}, c{0} {} // создание без инициализации.
  };

  // circle.h - круг
  class Circle: public Figure {
    int r; // радиус
    public:
    // переопределяем интерфейс класса
    void In(std::ifstream &ifst);  // ввод данных из потока
    virtual void Out(std::ofstream &ofst);    // вывод
    virtual std::string Multimethod(Figure& fig2); // мультиметод
    virtual std::string FirstRectangle(Rectangle& rect);
    virtual std::string FirstTriangle(Triangle& trian);
    virtual std::string FirstCircle(Circle& circ);
    Circle(): r{0} {} // создание без инициализации.
  };

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


  // multimethod-new-spec.cpp
  // Обработчик круга и прямоугольника
  std::string  Rectangle::FirstCircle(Circle& circle) {
    return "Circle -  Rectangle Combination\n";
  }
  // Обработчик круга и треугольника
  std::string Triangle::FirstCircle(Circle& circle) {
    return "Circle - Triangle Combination\n";
  }
  // Мультиметод, определяющий вход в круг
  std::string Circle::Multimethod(Figure& fig2) {
    return fig2.FirstCircle(reinterpret_cast<Circle&>(*this));
  }
  std::string Circle::FirstRectangle(Rectangle& rect) {
    return "Rectangle - Circle Combination\n";
  }
  std::string Circle::FirstTriangle(Triangle& trian) {
    return "Triangle - Circle Combination\n";
  }
  std::string Circle::FirstCircle(Circle& circle) {
    return "Circle - Circle Combination\n";
  }

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

Шаг 7. Добавление альтернативы в мультиметод при ОО подходе

Добавление альтернативы в мультиметод при ПП подходе

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


  // Обработчик специализации для прямоугольника и круга
  void Multimethod<Figure.rect* r1, Figure.circ* c2>(FILE* ofst) {
    fprintf(ofst, "Rectangle - Circle Combination\n");
  }
  // Обработчик специализации для треугольника и круга
  void Multimethod<Figure.trian* r1, Figure.circ* c2>(FILE* ofst) {
    fprintf(ofst, "Triangle - Circle Combination\n");
  }
  // Обработчик специализации для круга и прямоугольника
  void Multimethod<Figure.circ* c1, Figure.rect* r2>(FILE* ofst) {
    fprintf(ofst, "Circle - Rectangle Combination\n");
  }
  // Обработчик специализации для круга и треугольника
  void Multimethod<Figure.circ* c1, Figure.trian* t2>(FILE* ofst) {
    fprintf(ofst, "Circle - Triangle Combination\n");
  }
  // Обработчик специализации для двух кругов
  void Multimethod<Figure.circ* c1, Figure.circ* c2>(FILE* ofst) {
    fprintf(ofst, "Circle - Circle Combination\n");
  }

Использование старых артефактов и добавление необходимых обработчиков специализаций отражено на схеме.

Шаг 7. Добавление альтернативы в мультиметод при ПП подходе

Выводы

Расширение мультиметода при добавлении новой альтернативы требует изменения его централизованной реализации при процедурном подходе и модификации классов в случае ОО стиля. При использовании процедурно-параметрической парадигмы нужно только добавить недостающие обработчики альтернатив.


Содержание