Идиома PIMPL (pointer to implementation)

      Комментарии к записи Идиома PIMPL (pointer to implementation) отключены

Помечено: , , ,

В этой теме 0 ответов, 1 участник, последнее обновление  Васильев Владимир Сергеевич 2 мес., 3 нед. назад.

  • Автор
    Сообщения
  • #3617

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

    файл raw_socket.h:

    #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:

    #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:

    #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

    Паттерн PIMPL позволяет скрыть от пользователя класс с платформно-зависимым кодом. В нашем случае этот класс — network::raw_socket::impl. Весь платформно-зависимый код невидим для пользователя. Пользователь класса network::raw_socket не сможет напрямую вызвать системные сокетные функции, так как включение системных заголовочных файлов происходит внутри cpp-файла. Паттерн позволяет локализовать весь платформно-зависимый код в одном файле и дает гарантию, что ни ваш пользователь, ни вы сами, не сможете работать напрямую с платформно-зависимым кодом. Таким образом вы получаете полную изоляцию и локализацию платформно-зависимого кода в одном файле. Для того, чтобы выполнить переезд на новую операционную систему, или даже на другой тип платформы, вам достаточно просто переписать этот самый impl. Весь остальной код платформно-независим.

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

Для ответа в этой теме необходимо авторизоваться.