Запутался по поводу подходящего шаблона дизайна, который будет использоваться здесь - PullRequest
0 голосов
/ 28 апреля 2019

Я реализую класс, который может загружать файл из различных источников, таких как ftp, http и т. Д. Я начал со следующего интерфейса

class IInternetFileDownloader
{
public:
    IInternetFileDownloader() = default;

    virtual void Download(const std::string& urlToDownloadFrom) = 0;

};

Затем я реализовал классы, которые будут выполнять фактическую загрузку изсоответствующая конечная точка.Итак, у меня есть HttpFileDownloader.h как показано ниже

#include "IInternetFileDownloader.h"
class HttpFileDownloader : public IInternetFileDownloader
{
public:
    HttpFileDownloader() = default;

    virtual void Download(const std::string& urlToDownloadFrom)
    {
        // download implementation
    }

};

Итак, у меня есть FtpFileDownloader .h как показано ниже

#include "IInternetFileDownloader.h"
class FtpFileDownloader : public IInternetFileDownloader
{
public:
    FtpFileDownloader() = default;

    virtual void Download(const std::string& urlToDownloadFrom)
    {
        // download implementation
    }

};

Я могу вызвать соответствующий класс, как показано ниже

#include "IInternetFileDownloader.h"
#include "HttpFileDownloader.h"

int main()
{
    std::string downloadFromUrl = "http://www.example.org/xyz.zip";
    IInternetFileDownloader* download = new HttpFileDownloader();
    download->Download(downloadFromUrl);
}

Однако я не хочу создавать здесь конкретные HttpFileDownloader или FtpFileDownloader.На мой взгляд, должен быть другой класс, который может просто взять URL-адрес и, в зависимости от протокола, он может создать соответствующий класс.Таким образом, клиентскому коду (main.cpp) не нужно беспокоиться об экземпляре соответствующего класса.Я читал о шаблонах проектирования фабрики и строителя и немного запутался, какой из них лучше всего использовать в этом сценарии?

1 Ответ

1 голос
/ 28 апреля 2019

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

Кроме того, я не думаю, что вам нужны конструкторы по умолчанию в базовом классе, но вам, вероятно, нужен виртуальный деструктор в базовом классе. Таким образом, ваша фабричная функция CreateDownloader, которую я предлагаю, может вернуть указатель (или shared_ptr) на экземпляр IInternetFileDownloader, который вы удалите позже.

class IInternetFileDownloader
{
public:
    virtual ~IInternetFileDownloader() = default;
    virtual void Download(const std::string& urlToDownloadFrom) = 0;

    // parses the url to infer the protocol and construct an instance of a derived class
    static IInternetFileDownloader* CreateDownloader(const std:string& url);
};

class HttpFileDownloader : public HttpFileDownloader 
{
public:
    virtual void Download(const std::string& urlToDownloadFrom) override;
};

class FtpFileDownloader : public IInternetFileDownloader
{
public:
    virtual void Download(const std::string& urlToDownloadFrom) override;
};

И это, вероятно, подход, с которым я бы пошел.

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

class IInternetFileDownloader
{
public:
    virtual ~IInternetFileDownloader() = default;
    virtual void Download(const std::string& urlToDownloadFrom) = 0;
};

class InternetFileDownloaderFactory
{
public:
    // parses the url to infer the protocol and construct an instance of a derived class
    virtual IInternetFileDownloader* CreateDownloader(const std:string& url);
};

class InternetFileDownloaderFactoryMock : public InternetFileDownloaderFactory
{
public:
    IInternetFileDownloader* CreateDownloader(const std:string& url) override
    {
         return new MockFileDownloaderFactoryMock();
    }
};

class MockFileDownloaderFactoryMock : public IInternetFileDownloader
{
public:
    virtual void Download(const std::string& urlToDownloadFrom)
    {
       // do nothing or simulate a download
    }
};
...