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

Из Википедии, бесплатной энциклопедии

Шаблонный код, 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) 

Автогенераторы кода[править | править код]

Например, в системе 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 года.