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;