C++11 适配器模式示例

本例来自于Design Pattern in Modern C++一书的适配器模式一节。但是作者的代码是基于MFC绘图的。MFC绑定Windows平台,在Mac和Linux平台上无法使用。本人使用OpenCV重写绘图部分。运行成功。
但是有点值得注意的是,本文中Drawer的draw方法不是一个回调方法。所以本例中的LineToCacheAdapter类,其实作用不大,MFC在CDialog类或者CView类隐藏后再次显示时,可能会调用OnPaint回调方法重绘窗体。所以缓存效果很明显,就是减少无必要的Points计算。
但是本例使用的OpenCV,没有找到重绘窗体的回调。所以不需要刷新窗体。基本上缓存意义不大,除非你画了两条一模一样的线。我的意思是起点和终点相同。
例子的思路其实也很简单。
把原先不能接收线对象的方法,改用LineToPointAdapter进行适配,适配成点集合,这样就可以画点了。
程序目录结构如下,


image.png

test/CMakeLists.txt

cmake_minimum_required(VERSION 2.6)
project(adapter_caching)

set(CMAKE_CXX_STANDARD 20)
add_definitions(-g)


find_package(Boost REQUIRED COMPONENTS
    system
    filesystem
    serialization
    program_options
    thread
    )

find_package(OpenCV REQUIRED )
find_package(glog REQUIRED)

include_directories(${Boost_INCLUDE_DIRS} /usr/local/include /usr/local/include/opencv4 /usr/local/iODBC/include /opt/snowflake/snowflakeodbc/include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../ ${CMAKE_CURRENT_SOURCE_DIR}/../include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/)

LINK_DIRECTORIES(/usr/local/lib /usr/local/iODBC/lib /opt/snowflake/snowflakeodbc/lib/universal)

file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/death_handler/impl/*.cpp) 
foreach( sourcefile ${APP_SOURCES} )
        file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${sourcefile})
    
        string(FIND "${filename}"  "test.cpp" "TEMP")
    if( NOT "${TEMP}" STREQUAL "-1" )
        string(REPLACE ".cpp" "" file ${filename})
        add_executable(${file}  ${APP_SOURCES})
        target_link_libraries(${file} ${Boost_LIBRARIES} ${OpenCV_LIBS})
        target_link_libraries(${file} glog::glog ssl crypto libgtest.a libgmock.a iodbc iodbcinst libnanodbc.a pthread)
    endif()
endforeach( sourcefile ${APP_SOURCES})

test/rect_draw_test.cpp

#include "death_handler/death_handler.h"
#include 
#include "rect_draw_cache.hpp"

#include 
#include 



int main(int argc, char** argv) {
    FLAGS_log_dir = "./";
    FLAGS_alsologtostderr = true;
    // 日志级别 INFO, WARNING, ERROR, FATAL 的值分别为0、1、2、3
    FLAGS_minloglevel = 0;

    Debug::DeathHandler dh;

    google::InitGoogleLogging("./logs.log");
    testing::InitGoogleTest(&argc, argv);
    int ret = RUN_ALL_TESTS();
    return ret;
}

GTEST_TEST(MultitonTests, Multiton) {
    DrawableObjects objs {
        std::make_shared(10,10,100,100),
        std::make_shared(30,30,60,60)
    };
    Drawer d{objs};
    d.draw();
    d.wait_to_dispose();
}

include/geometry.h

#ifndef _FREDRIC_GEOMETRY_H_
#define _FREDRIC_GEOMETRY_H_

#include 

struct Point {
    int x, y;

    friend std::size_t hash_value(Point const& obj) {
        std::size_t seed = 0x725C686F;
        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(Line const& obj) {
        std::size_t seed = 0x719E6B16;
        boost::hash_combine(seed, obj.start);
        boost::hash_combine(seed, obj.end);
        return seed;
    }
};

struct VectorObject {
    virtual std::vector::iterator begin() = 0;
    virtual std::vector::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::vector::iterator begin() {
        return lines.begin();
    }

    std::vector::iterator end() {
        return lines.end();
    }

private:
    std::vector lines;
};
#endif

include/rect_draw_cache.hpp

#ifndef _FREDRIC_RECT_DRAW_CACHE_HPP_
#define _FREDRIC_RECT_DRAW_CACHE_HPP_

#include "geometry.h"
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

std::string const window_name = "Draw Points";
using DrawableObjects = std::vector>;
using Points = std::vector;
int const EXIT_KEY = 27;

struct LineToPointAdapter {
    LineToPointAdapter(Line& line){
        static int count = 0;
        LOG(INFO) << ++count << ": Generating points for line (no caching)\n";
         // no interpolation
        int left = std::min(line.start.x, line.end.x);
        int right = std::max(line.start.x, line.end.x);
        int top = std::min(line.start.y, line.end.y);
        int bottom = std::max(line.start.y, line.end.y);
        int dx = right - left;
        int dy = bottom - top;

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

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

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

private:
    Points points;
};


struct LineToPointCacheAdapter {
    LineToPointCacheAdapter(Line& line){
        // functional objects
        boost::hash hash;
        line_hash = hash(line);
        // find it, don't need to calculate it again
        if(cache.find(line_hash) != cache.end()) {
            return;
        }

        static int count = 0;
        LOG(INFO) << ++count << ": Generating points for line (caching)\n";

        Points points;
         // no interpolation
        int left = std::min(line.start.x, line.end.x);
        int right = std::max(line.start.x, line.end.x);
        int top = std::min(line.start.y, line.end.y);
        int bottom = std::max(line.start.y, line.end.y);
        int dx = right - left;
        int dy = bottom - top;

        // only vertical or horizontal lines
        if (dx == 0){
            // vertical
            for (int y = top; y <= bottom; ++y){
                points.emplace_back(Point{ left,y });
            }
            // horizontal
        } else if (dy == 0) {
            for (int x = left; x <= right; ++x) {
                points.emplace_back(Point{ x, top });
            }
        }
        // not find, cache it 
        cache[line_hash] = points;
    }

    Points::iterator begin() {
        return cache[line_hash].begin();
    }

    Points::iterator end() {
        return cache[line_hash].end();
    }

private:
    std::size_t line_hash;
    static std::map cache;

};

std::map LineToPointCacheAdapter::cache {};

template 
struct Drawer {
    Drawer(DrawableObjects const& vector_objs_): vector_objs{vector_objs_} {
        cv::namedWindow(window_name, cv::WINDOW_NORMAL);
        cv::resizeWindow(window_name, 200, 150);
        img_ = cv::Mat::zeros(cv::Size(200, 150), CV_8UC1);
        img_.setTo(cv::Scalar(255));
        cv::imshow(window_name, img_);
        cv::setWindowProperty(window_name, cv::WND_PROP_TOPMOST, 1);
    }

    void wait_to_dispose() {
        while(EXIT_KEY != cv::waitKey(1000)) {
        }
    }

    void draw() {
        for(auto& o: vector_objs) {
            for(auto& l: *o) {
                AdapterType lpo{l};
                draw_points(lpo.begin(), lpo.end());
            }
        }
    }

private:
    DrawableObjects vector_objs;
    cv::Mat img_;

    void draw_points(Points::iterator start, Points::iterator end) {
        for(auto i=start; i!=end; ++i) {
            cv::circle(img_, cv::Point(i->x, i->y), 3, cv::Scalar(0), cv::FILLED, cv::LINE_8);
        }
        cv::imshow(window_name, img_);
    }
};
#endif

程序输出如下,


image.png

image.png

你可能感兴趣的:(C++11 适配器模式示例)