Создание URL-адрес с застройщика образец


Что вы думаете об этот кусок кода? Как бы Вы дизайн шаблона застройщика в данной ситуации?

#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

Это как мы можем использовать это:

   url url = url::builder()
    .setScheme(HTTPS)
    .setDomain(YOUTUBE_DOMAIN)
    .addPath(WATCH_PATH)
    .addQuery(CATEGORY, ITEM)
    .build();
  std::cout<<url.build()<<std::endl;


208
6
задан 3 апреля 2018 в 03:04 Источник Поделиться
Комментарии
1 ответ

Содержать необходимые заголовки

Вы используете std::string подробно, но не включают <string>. Это позволяло работать (любой стандартный заголовок разрешено действовать как будто это включает любой или все остальные), но это не требуется для работы (а не для меня).

Используйте правильные по умолчанию

Прямо сейчас, код по умолчанию HTTPS на порт 440, но нормальный по умолчанию для HTTPS-порт 443. Я, как правило, включать порт в результате, если и только если это явно указано. В противном случае, наверное, лучше пусть браузер разберется в порт, так как большинство знают несколько больше, чем ваш код.

Интерфейс Fluent

Я не особо волнуюсь о беглом интерфейсы в целом, но если вы собираетесь использовать один, я бы предпочел избавиться от set на начало большинство сеттеров. Если у вас есть что-то вроде:

auto uri = builder().scheme(HTTPS).domain("www.google.com").build();

...вы действительно не нужно set на начало каждого, чтобы сделать его очевидным, что вы намерены.

Рассмотреть уникальные виды для части URL/Ури

Я, как правило, зря что ли мы не быть лучше обслуживается набором крошечных классов, по одному для каждого типа элемента в URL-адрес, и (например) вставить себя в поток, с url класс в основном просто набор этих. По крайней мере, некоторые из очевидных случаях, я бы по крайней мере рассмотреть возможность использования перегруженных операторов вместо имени функции. Например, довольно минималистический вариант может выглядеть примерно так:

struct protocol {
enum prot { http, https } p_;

protocol(prot p) : p_(p) {}

friend std::ostream &operator<<(std::ostream &os, protocol const &p) {
switch (p.p_) {
case http: return os << "http://";
case https: return os << "https://";
}
}
};

struct query {
std::string k;
std::string v;
};

class queries {
std::vector<query> queries_;
public:
void add(query const &q) { queries_.push_back(q); }

friend std::ostream &operator<<(std::ostream &os, queries const &q) {
std::string sep = "?";
for (auto const & f : q.queries_) {
os << sep << f.k << "=" << f.v;
sep = "&";
}
return os;
}
};

class path {
std::vector<std::string> components;
public:
void add(std::string const &path) {
components.push_back(path);
}

friend std::ostream &operator << (std::ostream &os, path const &p) {
for (auto const & s : p.components)
os << s << "/";
return os;
}
};

class url {
protocol prot_;
path path_;
queries q_;
public:
url(protocol::prot p) : prot_(p) {}

url &operator/(std::string const &p) { path_.add(p); return *this; }
url &operator&(query const &q) { q_.add(q); return *this; }

friend std::ostream &operator<<(std::ostream &os, url const &u) {
return os << u.prot_ << u.path_ << u.q_;
}
};

int main() {
url u = url(protocol::http) / "www.youtube.com" / "watch" & query{"v", "tpnrd0xGRsw"};
std::cout << u;
}

Обратите внимание, как urloperator<< (примерно соответствует вашему build) сейчас почти тривиально проста, и если мы добавим в видах фрагмент, порт, имя пользователя, и так далее, он будет оставаться почти столь же тривиальным, потому что основная сложность будет делегирован в каждом из этих классов вместо того, чтобы быть слеплены вместе в один гигантский "построить", что знает все о URL и как все части должны быть разделены и все такое. Вместо этого, он должен знать порядок компонентов в URL, но это о нем. Все подробности о том, как писать каждое произведение делегируются типа за то, что часть (большинство из которых по отдельности тоже довольно тривиально).

Больше синтаксического сахара

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

url http() { return url(protocol::http); }
url https() { return url(protocol::https); }

Таким образом мы можем генерировать url что-то вроде этого:

url u = https() / "www.youtube.com" / "watch" & query{"v", "tpnrd0xGRsw"};

Мне кажется немного чище, во всяком случае.

6
ответ дан 3 апреля 2018 в 07:04 Источник Поделиться