Шаблонный код
Шаблонный код, boilerplate-код (англ. boilerplate code) — нетворческий программный код, который программисту приходится писать вследствие требований языка программирования, операционной системы, библиотеки подпрограмм, манеры программирования и прочего. Шаблонными, в числе прочего, будут:
- подключение модулей;
- настройка компилятора и/или системы сборки;
- организация точки входа в программу или подпрограмму;
- код инициализации и выхода.
Например, в простейшем «Hello, world» на Си все строки, кроме собственно printf(...)
, будут шаблонными.
#include <stdio.h> //< шаблонный — подключение модуля
int main() //< шаблонный — точка входа
{ //< шаблонный — точка входа
printf("Hello, world!\n"); //< творческий!!
return 0; //< шаблонный — выход
} //< шаблонный — точка входа
К шаблонному коду близок так называемый bookkeeping code — код, обеспечивающий дополнительные стороны функционирования программы наподобие загрузки-сохранения.
Этимология понятия «boilerplate»
Слово «boilerplate» по-английски означает «котельное железо». Применительно к шаблонному тексту встречается уже в 1950-х. Этимология неясна и определённо происходит из лексикона полиграфистов, основные версии.
- Местные американские газетки подписывались на новости у крупных печатных синдикатов, и те присылали готовые заметки в виде отлитых печатных форм. Эти формы и называли «котельным железом»[1][2].
- Печатные формы, которые нужны были многократно в нескольких выпусках газеты — для газетных шапок, объявлений от постоянных клиентов и т. д. — делали из железа, в то время как основная часть газеты набиралась из свинцового сплава[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)
Автогенераторы кода
Например, в системе Qt Widgets код минимальной формы с кнопкой имеет такой вид:
//== main.cpp ==
#include "FmMain.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FmMain w;
w.show();
return a.exec();
}
//== FmMain.h ==
#ifndef FMMAIN_H
#define FMMAIN_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class FmMain; }
QT_END_NAMESPACE
class FmMain : public QMainWindow
{
Q_OBJECT
public:
FmMain(QWidget *parent = nullptr);
~FmMain();
private slots:
void on_btDemo_clicked();
private:
Ui::FmMain *ui;
};
#endif // FMMAIN_H
//== 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!!"); //< Только эту строку мы пишем вручную!
}
При этом только одну строку программист пишет вручную.
Препроцессоры
В том же Qt есть две программы, создающие часть Си++-кода формы — метаобъектный компилятор (moc) и компилятор интерфейса (uic).
Создание копии объекта с учётом умных указателей Си++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)
};