Шаблонный код

Шаблонный код, boilerplate-код (англ. boilerplate code) — нетворческий программный код, который программисту приходится писать вследствие требований языка программирования, операционной системы, библиотеки подпрограмм, манеры программирования и прочего. Название «шаблонный» говорит, что он повторяется из функции в функцию, из программы в программу с минимальными изменениями. Шаблонными, в числе прочего, будут:

  • подключение модулей;
  • настройка компилятора и/или системы сборки;
  • организация точки входа в программу или подпрограмму;
  • код инициализации и выхода.

Например, в простейшем «Hello, world» на Си все строки, кроме собственно printf(...), будут шаблонными.

#include <stdio.h>                    //< шаблонный — подключение модуля
int main()                            //< шаблонный — точка входа
{                                     //< шаблонный — точка входа
  printf("Hello, world!\n");          //< творческий!!
  return 0;                           //< шаблонный — выход
}                                     //< шаблонный — точка входа

К шаблонному коду близок так называемый bookkeeping code — творческий, но сравнительно простой код, обеспечивающий дополнительные стороны функционирования программы наподобие загрузки-сохранения.

Этимология понятия «boilerplate»

[править | править код]

Слово «boilerplate» по-английски означает «котельное железо». Применительно к шаблонному тексту встречается уже в 1950-х. Этимология неясна и определённо происходит из лексикона полиграфистов, основные версии:

  1. Местные американские газеты подписывались на получение новостей у крупных печатных синдикатов, и те присылали их в виде готовых заметок на отлитых печатных формах. Эти формы и называли «котельным железом»[1][2].
  2. Печатные формы, которые нужны были многократно в нескольких выпусках газеты — для газетных шапок, объявлений от постоянных клиентов и т. д. — делали из стали, в то время как основная часть газеты набиралась из свинцового сплава[3].
  3. Дымогарный котёл требовал сверления множества отверстий, в которые вставляли дымогарные трубы. Для сверления использовали пластины-шаблоны, и написание стандартных объявлений сравнили с шаблонным сверлением отверстий[4][5].

Методы уменьшения количества шаблонного кода

[править | править код]

Системы и библиотеки, решающие вопрос

[править | править код]

Стандартный код на Java:

public class Book {
    private String m_ISBN;
    private String m_Title;
    private String m_SubTitle;
    private String m_Autor;

    public String getISBN() { return m_ISBN; }
    public void setISBN(String pISBN) { m_ISBN = pISBN; }

    public String getTitle() { return m_Title; }
    public void setTitle(String pTitle) { m_Title = pTitle; }

    public String getSubTitle() { return m_SubTitle; }
    public void setSubTitle(String pSubTitle) { m_SubTitle = pSubTitle; }

    public String getAutor() { return m_Autor; }
    public void setAutor(String pAutor) { m_Autor = pAutor; }
}

Аналогичный код на C#

public class Book
{
    public string ISBN { get; set; }
    public string Title { get; set; }
    public string SubTitle { get; set; }
    public string Autor { get; set; }
}

…и на Kotlin

data class Book(var ISBN: String, var Title: String, var SubTitle: String, var Autor: String)

Стоит сказать, что языки программирования стараются избавляться от шаблонного кода. Примером может быть 17 версия Java, в которой появился специальный тип record, позволяющий писать меньше кода, подобно Kotlin

public record Book(String m_ISBN, String m_Title, String m_SubTitle, String m_Autor) {
}

Автогенераторы кода

[править | править код]

Например, в системе Qt Widgets код минимальной формы с кнопкой состоит из нескольких файлов, приведём один.

//== FmMain.cpp ==
#include "FmMain.h"
#include "ui_FmMain.h"

FmMain::FmMain(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::FmMain)
{
    ui->setupUi(this);
}

FmMain::~FmMain()
{
    delete ui;
}


void FmMain::on_btDemo_clicked()
{
    ui->btDemo->setText("Demo!!");    //< Только эту строку мы пишем вручную!
}

При этом только одну строку программист пишет вручную.

Адекватные настройки по умолчанию

[править | править код]

Пример 1. В Qt+MinGW для создания автономной консольной программы под Windows надо сделать:

win32-g++ {
    QMAKE_CXXFLAGS += -static-libgcc -static-libstdc++
    LIBS += -static -lpthread
}

Это также считается шаблонным кодом. Поскольку мелкие учебные и экспериментальные программы обычно являются автономными и консольными, возможно, следовало бы добавлять подобный код всегда при создании пустой консольной программы — а разработчик более сложного ПО, использующего чужие динамические библиотеки, просто стирал бы его.

Пример 2. Полный HTML выглядит так:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <title>Test</title>
</head>
<body>
  <p>Hello world!</p>
</body>
</html>

Стандарт WHATWG HTML позволяет опустить html, head и body[6]. Meta charset можно опустить, если HTML пришёл от сервера и в заголовках HTTP есть кодировка. Так что остаётся…

<!DOCTYPE html>
<title>Test</title>
<p>Hello world!</p>

Пример 1. В том же Qt есть две программы, создающие часть Си++-кода формы — метаобъектный компилятор (moc) и компилятор интерфейса (uic).

Пример 2. Создание копии объекта с учётом умных указателей Си++11 выглядит так.

#include <iostream>
#include <memory>

class Abstract {
public:
    auto clone() const { return std::unique_ptr<Abstract>{ vclone() }; }
protected:
    virtual Abstract* vclone() const = 0;
};

class Abstract2 : public Abstract {
public:
    auto clone() const { return std::unique_ptr<Abstract2>{ vclone() }; }
protected:
    virtual Abstract2* vclone() const = 0;
};

class Concrete : public Abstract2 {
public:
    auto clone() const { return std::unique_ptr<Concrete>{ vclone() }; }
protected:
    Concrete* vclone() const override { return new Concrete(*this); }
};

int main()
{
    Concrete impl;

    Abstract* a = &impl;
    std::unique_ptr<Abstract> b = a->clone();
    std::unique_ptr<Concrete> c = impl.clone();

    return 0;
}

Здесь шаблонный код — реализации функций vclone и clone.

С препроцессором Си этот код будет выглядеть так, и чем больше классов в иерархии, тем больше выигрыш:

#define IMPL_CLONE_1(Class, Body)  \
    public:     auto clone() const { return std::unique_ptr<Class>{ vclone() }; } \
    protected:  virtual Class* vclone() const Body

#define IMPL_CLONE_ABSTRACT(Class) IMPL_CLONE_1(Class, override = 0;)
#define IMPL_CLONE_CONCRETE(Class) IMPL_CLONE_1(Class, override { return new Class(*this); })

class Abstract {
    IMPL_CLONE_1(Abstract, =0;)
};

class Abstract2 : public Abstract {
    IMPL_CLONE_ABSTRACT(Abstract2)
};

class Concrete : public Abstract2 {
    IMPL_CLONE_CONCRETE(Concrete)
};

На Си++ обрезка пробелов (trim) в строках разного типа «на месте» (без выделения памяти) будет выглядеть так:

std::string_view trimSv(std::string_view x) {
  // Какая-то реализация
}

std::wstring_view trimSv(std::wstring_view x) {
  // Похожая реализация
}

И то же с метапрограммированием:

namespace detail {
  template <class Ch, class Traits>
  std::basic_string_view<Ch, Traits> trimSv(std::basic_string_view<Ch, Traits> x) {
    // Реализация
  }
}

template <class S>
inline auto trimSv(const S& x)
  { return detail::trimSv(static_cast<std::basic_string_view>(x)); }

Код также многословный (вторая функция нужна, чтобы под шаблон подходил не только std::string_view, но и близкие к нему типы std::string и const char*), но как минимум без повторов.

Примечания

[править | править код]
  1. Источник. Дата обращения: 9 февраля 2022. Архивировано 9 февраля 2022 года.
  2. Boilerplate Definition & Meaning — Merriam-Webster. Дата обращения: 9 февраля 2022. Архивировано 14 декабря 2021 года.
  3. Why is it «Boilerplate text?» | Mental Floss
  4. What is the origin of the term 'boilerplate'? — Quora
  5. Boilerplate Definition. Дата обращения: 9 февраля 2022. Архивировано 9 февраля 2022 года.
  6. HTML Standard. Дата обращения: 12 ноября 2022. Архивировано 26 сентября 2021 года.