![]() |
Pimpl idiom — pointer to implementation | ||
Предположим, что вам необходимо воспользоваться внешним API, но при этом вы не хотите позволить платформно-зависимому коду расползаться по проекту и не хотите чтобы у пользователя была возможность им воспользоваться напрямую. В этом случае с вашей задачей прекрасно справится идиома Pimpl. | ||
raw_socket.h |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <memory> #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> namespace network { enum protocol { tcp, udp }; class raw_socket : public boost::noncopyable { public: raw_socket(protocol proto); void connect(std::string const& host, unsigned int port); std::size_t send(void const* data, std::size_t size); std::size_t receive(void* data, std::size_t size); private: class impl; typedef std::auto_ptr<impl> impl_ptr; impl_ptr pimpl; }; typedef boost::shared_ptr<raw_socket> socket_ptr; } // namespace network |
raw_socket.cpp |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include "raw_socket.h" #include "raw_socket.inl" namespace network { raw_socket::raw_socket(protocol proto) : pimpl(new impl(proto)) { } void raw_socket::connect(std::string const& host, unsigned int port) { pimpl->connect(host, port); } std::size_t raw_socket::send(void const* data, std::size_t size) { return pimpl->send(data, size); } std::size_t raw_socket::receive(void* data, std::size_t size) { return pimpl->receive(data, size); } } // namespace network |
raw_socket.inl |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <windows.h> #include <wsock2.h> namespace network { class raw_socket::impl { public: impl(protocol proto); ~impl(); void connect(std::string const& host, unsigned int port); std::size_t send(void const* data, std::size_t size); std::size_t receive(void* data, std::size_t size); private: SOCKET sck; }; raw_socket::impl::impl(protocol proto) { sck = socket(/* ... */); } raw_socket::impl::~impl() { closesocket(sck); } void raw_socket::impl::connect(std::string const& host, unsigned int port) { // ... ::connect(/* ... */); } std::size_t raw_socket::impl::send(void const* data, std::size_t size) { return static_cast<std::size_t>(::send(sck, data, size)); } std::size_t raw_socket::impl::receive(void* data, std::size_t size) { return static_cast<std::size_t>(::recv(sck, data, size)); } } // namespace network |
main.cpp |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 | #include "raw_socket.h" int main() { network::socket_ptr sck(new network::raw_socket(network::tcp)); sck->connect("www.microsoft.com", 80); sck->send(/* ... */); sck->receive(/* ... */); return 0; } |
Паттерн позволяет скрыть от пользователя класс с платформно-зависимым кодом. В нашем случае этот класс — network::raw_socket::impl. Весь платформно-зависимый код невидим для пользователя. Пользователь класса network::raw_socket не сможет напрямую вызвать системные сокетные функции, так как включение системных заголовочных файлов происходит внутри cpp-файла. Паттерн позволяет локализовать весь платформно-зависимый код в одном файле и дает гарантию, что ни ваш пользователь, ни вы сами, не сможете работать напрямую с платформно-зависимым кодом. Таким образом вы получаете полную изоляцию и локализацию платформно-зависимого кода в одном файле. Для того, чтобы выполнить переезд на новую операционную систему, или даже на другой тип платформы, вам достаточно просто переписать этот самый impl. Весь остальной код платформно-независим. | ||
За impl-ом обычно скрывают реализацию работы с сокетами, файлами, объектами синхронизации, и прочими общепринятыми системными сущностями. Основная ценность паттерна заключается в том, что он позволяет работать объектно с платформно-зависимым кодом платформно-независимым образом. |
|
Статистика |
|