MinUk.Dev
Modern C++ Design Pattern/Chatper 6. 어댑터 - minuk dev wiki

Modern C++ Design Pattern/Chatper 6. 어댑터

created : Tue, 07 Apr 2020 20:44:17 +0900
modified : Sat, 26 Sep 2020 23:19:03 +0900

Adapter Pattern

  • We use a example case, drawiing geometric shape.
    struct Point
    {
      int x, y;
    };

    struct Line{
      Point start, end;
    };

    struct VectorObject
    {
      virtual std::vector<Line>::iterator begin() = 0;
      virtual std::vector<Line>::iterator end() = 0;
    };

    struct VectorRectangle : VectorObject
    {
      VectorRectangle(int x, int y, int width, int height)
      {
        lines.emplace_back(Line{ Point{x, y}, Point{x + width, y} });
        lines.emplace_back(Line{ Point{x + width, y}, Point {x + width, y + height} });
        lines.emplace_back(Line{ Point{x,y}, Point{x, y + height} });
        lines.emplace_back(Line{ Point{ x, y + height }, Point {x + width, y + height} });
      }

      std::vecotr<Line>::iterator begin() override {
        return lines.begin();
      }
      std::vector<Line>::iterator end() override {
        return lines.end();
      }

    private:
      std::vector<Line> lines;
    };

    void DrawPoints(CPaintDC& dc,
      std::vector<Point>::iterator start,
      std::vector<Point>::iterator end)
    {
      for (auto i = start; i != end; ++i)
        dc.SetPixel(i->x, i->y , 0);
    }

Adapter

  • Let’s draw a few rectangles.
    vector<shared_ptr<VectorObject>> vectorObjects{
      make_shared<VectorRectangle>(10, 10, 100, 100),
      make_shared<VectorRectangle>(30, 30, 60, 60)
    }

    struct LineToPointAdapter
    {
      typedef vector<Point> Points;

      LineToPointAdapter(Line& line);

      virtual Points::iterator begin() { return points.begin(); }
      virtual Points::iterator end() { return points.end(); }
    private:
      Points points;
    };

    LineToPointAdapter::LineToPointAdapter(Line& line)
    {
      int left = min(line.start.x, line.end.x);
      int right = max(line.start.x, line.end.x);
      int top = min(line.start.y, line.end.y);
      int bottom = max(line.start.y, line.end.y);
      int dx = right - left;
      int dy = line.end.y - line.start.y;

      if (dx == 0)
      {
        for (int y = top; y <= bottom; ++y)
        {
          points.emplace_back(Point{ left, y });
        }
      }
      else if (dy == 0)
      {
        for (int x = left; x <= right; ++x)
        {
          points.emplace_back(Point{ x, top });
        }
      }
    }


    for (auto& obj : vectorObjects)
    {
      for (auto& line : *obj)
      {
        LineToPointAdapter lpo{ line };
        DrawPoints(dc, lpo.begin(), lpo.end());
      }
    }

Temporary Adapter

  • How about above method is called in every display update despite no change?
    • One of the solutions is to use cache.

      vector<Point> points;
      for (auto& o : vectorObjects)
      {
        for (auto& l : *o)
        {
          LineToPointAdapter lpo{ l };
          for (auto& p : lp)
            points.push_back(p);
        }
      }
    • Of course, this solution cause another problem. That is original vectorOjbects is changed.

      struct Point
      {
        int x, y;
        friend std:: size_t hash_value(const POint& obj)
        {
          std::size_t send = 0x825C696F;
          boost::hash_combine(seed, obj.x);
          boost::hash_combine(seed, obj.y);
          return seed;
        }
      };
      
      struct Line
      {
        Point start, end;
        friend std::size_t hash_value(const Line& obj)
        {
          std::size_t seed = 0x719E6B16;
          boost::hash_combine(seed, obj.start);
          boost::hash_combine(seed, obj.end);
          return seed;
        }
      };
      
      static map<size_t, Points> cache;
      
      virtual Points::iterator begin() { return cache[line_hash].begin(); }
      virtual Points::iterator end() { return cache[line_hash].end(); }
      
      LineToPointCachingAdapter(Line& line){
        static boost::hash<Line> hash;
        line_hash = hash(line);
        if (cache.find(line_hash) != cache.end())
          return;
      
        Points points;
      
        /* before code */
        /* remove before cached point */
        cache[line_hash] = points;
      }