What do you think about this piece of code? How would you design the builder pattern in this situation?
#ifndef CPP_URL #define CPP_URL #include<iostream> #include<vector> #include<map> using std::string; enum protocol { HTTP, HTTPS }; static string map(protocol scheme) { switch(scheme) { case HTTP: return "http"; default: return "https"; } } static int defaultPort(protocol scheme) { switch (scheme) { case HTTP: return 80; default: return 440; } } class url final { private: using list = std::vector<std::string>; using pairs = std::map<std::string, std::string>; const list paths; const pairs queries; const protocol scheme; const string domain; const string fragment; const string username; const string password; mutable int port; public: class builder { friend url; private: protocol scheme; pairs queries; list paths; string domain; string fragment; string username; string password; int port = -1; public: builder& setScheme(protocol); builder& setFragment(const string&); builder& setDomain(const string&); builder& setUsername(const string&); builder& setPassword(const string&); builder& setPort(int port); builder& addQuery(const string&, const string&); builder& addPath(const string&); url& build() const; }; explicit url(const url::builder&); string build() const; }; using builder = url::builder; inline builder& builder::setScheme(protocol scheme) { this->scheme = scheme; return *this; } inline builder& builder::setUsername(const string& username) { this->username = username; return *this; } inline builder& builder::setPassword(const string& password) { this->password = password; return *this; } inline builder& builder::setFragment(const string& fragment) { this->fragment = fragment; return *this; } inline builder& builder::setDomain(const string& domain) { this->domain = domain; return *this; } inline builder& builder::setPort(int port) { this->port = port; return *this; } inline builder& builder::addQuery(const string& key, const string& value) { queries.insert(std::pair<string, string>(key, value)); return *this; } inline builder& builder::addPath(const string& path) { paths.insert(paths.begin(), path); return *this; } inline url& builder::build() const { return *(new url(*this)); } inline url::url(const builder& object) : paths(object.paths) , queries(object.queries) , scheme(object.scheme) , domain(object.domain) , fragment(object.fragment) , username(object.username) , password(object.password) , port(object.port) {} string url::build() const { string url = map(scheme).append("://"); if (!username.empty()) { url.append(username); if (!password.empty()) { url.append(":").append(password); } url.append("@"); } url.append(domain); if (port == -1) port = defaultPort(scheme); url.append(":").append(std::to_string(port)); for (string path : paths) { url.append("/"); url.append(path); } auto it = queries.begin(); if (it != queries.end()) { url.append("?").append(it->first) .append("=").append(it->second); for(it++; it != queries.end(); it++) { url.append("&").append(it->first) .append("=").append(it->second); } } if (!fragment.empty()) { url.append("#").append(fragment); } return url; }; #endif
This is how we can use that:
url url = url::builder() .setScheme(HTTPS) .setDomain(YOUTUBE_DOMAIN) .addPath(WATCH_PATH) .addQuery(CATEGORY, ITEM) .build(); std::cout<<url.build()<<std::endl;