📜 ⬆️ ⬇️

Template Method

Template Method


When you have to ask a person what design patterns he has used to use, for some reason, few people call the pattern “Template Method” (Template Method). This is probably due to a gap in the knowledge of the nomenclature of patterns, because I personally can hardly imagine that a more or less experienced programmer would never use such a convenient and useful pattern. I propose to take a closer look at it.

So the template method. It has nothing to do with c ++ templates. This pattern is remarkable in that it is very simple, intuitive, and extremely useful. It belongs to the category of behavior patterns and serves one simple goal - to redefine the steps of a certain algorithm in a family of classes derived from the base one, defining the structure of this algorithm itself.

Suppose we write the class Crypt, which is designed to encrypt a string of text. The class defines the encryption function:
void encrypt() { //    setupRnd(); setupAlgorithm(); //   std::string fContent = getString(); //   std::string enc = applyEncryption(fContent); //   saveString(fContent); //     wipeSpace(); } 

Using the Template Method pattern, we can use the algorithm presented in the encrypt () function to work with strings obtained from different sources — from the keyboard, read from the disk, received over the network. At the same time, the structure of the algorithm itself and the invariable steps (setting initial parameters, cleaning up traces of work, and, if desired, applying encryption) remain unchanged. This allows us to:
  1. Reuse code that does not change for different subclasses;
  2. Determine the overall behavior of a family of subclasses using a code once defined;
  3. Differentiate access rights - when implementing variable algorithm steps, we will use private virtual functions. This ensures that such operations will only be invoked as steps of a modifiable algorithm (or, rather, will not be called by derived classes in inappropriate places).

So, let's add the Crypt class with the necessary members:
 private: void setupRnd() { //      std::cout << "setup rnd\n"; }; void setupAlgorithm() { //     std::cout << "setup algorithm\n"; }; void wipeSpace() { //    std::cout << "wipe\n"; }; virtual std::string applyEncryption(const std::string& content) { //  std::string result = someStrongEncryption(content); return result; } virtual std::string getString() = 0; virtual void saveString(const std::string& content) = 0; 

Please note that the functions are private. This is an intentional restriction on their call, which does not prevent them from being redefined in a derived class.
And, in fact, the derived class is an encrypting file on the disk:
 class DiskFileCrypt : public Crypt { public: DiskFileCrypt(const std::string& fName) : fileName(fName) {}; private: std::string fileName; virtual std::string getString() { std::cout << "get disk file named \"" << fileName << "\"\n"; //        return fileContent; } virtual void saveString(const std::string& content) { std::cout << "save disk file named \"" << fileName << "\"\n"; //     } }; 

It is already clear that when you call
 DiskFileCrypt d("foo.txt"); d.encrypt(); 

The encrypt () function algorithm will be executed and the following will be in the console:
setup rnd
setup algorithm
get disk file named "foo.txt"
save disk file named "foo.txt"
wipe

The mechanism of virtual functions in the example above serves exclusively to customize the behavior of classes. Defining them as purely virtual, you can set the requirements for inheriting classes. For example, if we wanted the heirs to define an encryption algorithm, we would have to make a pure virtual member function of the applyEncryption class.

')

Source: https://habr.com/ru/post/277295/


All Articles