Dialogue with prawns: understanding design model
--Template Method / Visitor
[Translator Press] This article is compiled according to two articles published on Cuj Expert Forum. C / C User's Journal is currently the best magazine, which is currently the best C / C language, especially after C Report is closed, Cuj's status is more prominent. CUJ EXPERT Forum is the online technology column hosted by CUJ, which brings together the technical and essays of top experts in the C community since October 2000, and freely public release, exciting, is a non-missed information of each C / C learner. The Conversation Series hosted by Jim Hyslop and Herb Sutter is a must-have bise column, which is a must-have quality column in the form of a humorous dialogue, which is widely praised in the C community. The translator specially selection two articles in two design patterns, introduces you to everyone. The classic works in design patterns are GOF's Design Patterns. But the book has a shortcoming, not understanding. From the style, the book is said to be a tutorial model for the learner, but it is better to say that the academic report will be seen from the academic people, and rigorous and more vivid. This includes the author of the book and the masters like Bjarne Stroustrup never say. In fact, Design Pattern is not necessarily unknown. Through vivid examples, a medium-level C learner can fully master basic usage, use it in its own programming practice to get immediate effect. These two articles are very good examples. This paper has made many deletions and modifications in the premise of ensuring technical integrity, so that the article is more compact.
-------------------------------------------------- ------------
Character introduction:
I - a pursuit of C programmers, still in the trial period, smart but insufficient experience.
Wendy --- The technology of the company is sitting in the compartment next to me, C prawns, the most great thing is that she is a woman! She is good, it is a bit thin,
I really worship her for her.
-------------------------------------------------- ------------
I. Virtually Yours - Template Method Mode
I am studying a class written by Wendy. That is a abstract base class she written for this project, and my job is to give a concrete class from the middle school. The PUBLIC section of this class is like this:
Class mountie {public: void read (std :: istream &); void write (std :: ostream); virtual ~ mountie ();
Very normal, Virtual Destructor indicates that this class is intended. Then look at its protected section:
Protected: Virtual Void Do_read (std :: istream &); virtual void do_write (std :: ostream &) const;
It is just a kung fu, I know the trend of Wendy: She is using the template method mode. Public member functions Read and Write are non-virtual, they are definitely calling the protected section DO_READ / DO_WRITE virtual member function to complete the actual work. Ah, I am just floating for my own progress! Ha, Wendy, this time you can't help me, what is the trick? Despite relying on the horse ... Suddenly, the smile is solidified on my face, because I saw its private section: private: Virtual st: string classid () const = 0;
what is this? A private pure sequence function, can you work? I stood up,
"Wendy, your mountie class does not seem to work yeah, it has a private virtual function."
"You tried?" She didn't lift her head.
"Well, it is okay, but think about it. How can my derive class Override your private function?" I muttered.
"Hey, you are very confirmed!" Wendy's voice is very soft, "How do you always do this? It's okay, these months follow me, you haven't learned something? Small rookie."
It's awful ...
"Small vegetables, you all have forgotten, the access control level is not a virtual basis with a function. Judgment a function is a dynamic binding or static binding is the last step of the function call parsing. Read the standard 3.4 and 5.2 .2 section. "
I am completely under the wind, I have to take the interference tactics. "Well, even if you say good, I still don't understand, why bother to use it as private?"
"I asked you, if you don't want to make a member function in a class, how should you handle?"
"Of course, set it to private," I replied.
"So you go to see my Mountie class implementation, especially the implementation of the Write () function."
I can't escape the eyes of Wendy. I turned to the head to search on my screen, soon, I found:
Void Mountie :: Write (std :: ostream & dudley) const {dudley << ClassId () << std :: endl; do_write (dudley);
Hey, the recent cartoon film is really seen too much, actually made such a low-level mistake. Still always admitted: "Well, I understand .classID () is a detail of implementation, used to save the object, the type of iconic class, the derived class must overwrite it, so it must be pure. But since it is Implementing details should be set to private. "
"This is almost, the small rookie." The prawn nodded, "Now explain why do_read () and do_write () are protected?"
This problem is not difficult. I organized a try: "Because the derived class object needs to call the implementation of these two functions to read and write the base class object."
"Very good," prawn is almost satisfied, "But you explain again why I don't set them to public?"
Now I feel much: "Because you must do a specific way, you must write the type information first, then write the object information, when reading this, responsible for generating The object's module first can know what type of object to be read, and then read object information correctly from the stream. "" Smart, my little rookie! "Wendy paused," Just learn foreign speaking Same, learning C is not only mastering grammar, but also must master a lot of usa. "
"Yeah, I am going to read the book of Coplien ..."
[Translator Note: James Coplien 1992 Classic Works Advanced C Programming Style and IDioms]
The prawn waved her hand, "Calm, small rookie, I don't mean the book of the prophet Coplien, I refer to the hidden usual method behind a certain structure. For example, a class of Virtual Destructor is equivalent to telling you. : 'Hey, I am a polymorphic class class to inherit me!' And if a class's Destructor is not a virtual, it is equivalent to saying: 'I can't be used as a polymorphic class, watching the old days Don't inherit me. '"
"Similarly, the Virtual function has implied meaning. A protected virtual function tells you: 'You wrote the derived class, oh, but it is necessary to call my implementation.' And a private virtual function is It is said: 'Deleted class can be covered, or you can not cover me, follow you. But you can't call my implementation.' "
I nodded and told her that I understood it, then asked: "What about public Virtual Function?"
"Don't use public virtual function as much as possible." She wrote a pen and wrote the following code:
Class HardtoExtend
{
PUBLIC:
Virtual void f ();
}
Void HardtoExtend :: f ()
{
// Perform a specific action
}
"Suppose you have released this class. When writing the second edition, the demand changes, you must use the template method. But this is impossible, do you know why?"
"Hey, this ..., I don't know."
"From two possible methods. First, transfer F () implementation code into a new function, then set f () itself to non-virtual:
Class HardtoExtend {// POSSIBLY PROTECTED VIRTUAL VOID DO_F (); public: void f ();}; void HardtoExtend :: f () {// pre-processing DO_F (); // post-processing} void HardtoExtend :: do_f () {// perform a specific action}
However, your original derived class is an attempt to Override function f () instead of do_f (), you must change all derived class implementation, as long as you miss a class, your class level will be infected with the prophet Meyers 'Practical division of schizophrenia'. "[Translator Note: See Scott Meyers, Effective C , Item 37, Never redefine inherited non-virtual functions]
"Another way is to move f () to the Private area, introduce a new Non-Virtual function:" Class HardtoExtend {// POSSIBLY PROTECTED VIRTUAL VOID F (); public: void call_f ();
"This will lead to countless a headache problems. First, all customers are trying to call f () instead of call_f (), now their code can not be compiled. What is even more, most of the derived classes return F () Placing in the public area, which can directly use the derived class users to access the details you want to protect. "
"Treat virtual functions To treat data members, set them to Private until you need to use more loose access control again. To know from private into public, it is difficult!"
[Translator Note: The idea expressed in this article has certain disruptive, because we are too easy to set public Virtual function in the base class, and even specifically for this practice in Java, it is not necessary to say that this is not it is good! I can't accept it for a time. But carefully understand the author's meaning, he is not generally opposing public Virtual Function, just gives the above principles in the background of Template Method. Although this principle is also worth considering in the general design, the main application field is still in the Template Method mode. Of course, Template Method is a very useful and common mode, so it has also determined the principles proposed in this article. ]
-------------------------------------------------- ----------------
II. Visitor mode
I am distressed for a design problem. The trial period is over, I hope that I will solve this problem to prove my progress. Everyone remembers its first job, you should also know how important it is done at this time! I have seen other new employees who have not been frying the trial period, because they don't know how to deal with the prawns ..., don't misunderstand, I am not saying that she is not good, she is my best. Programmer, it is a bit thin. Now I worship her as a teacher, not doing anything, because I hope to reach the height of her.
I want to add a new virtual function in a class hierarch, but this class level is maintained by another part of people, others can't touch:
Class Personnel {public: virtual void pay (/ *...*/) = 0; Virtual void promote = 0; Virtual Void Accept (PersonnelV &) = 0; // ... other Functions ...}; class officer: public personnel {/ * override virtuals * /}; Class Captain: Public officer {/ * Override Virtuals * /}; Class first: public officer {/ * override virtuals * /};
I want a function, if the object is the captain, if it is a big pair (first officer). Virtual Function is a solution, declare it in Personnel or Office, and override it in Captain and First.
Worse, I can't add such a virtual function. I know that I can give a solution with RTTI: void f (officer & o) {if (Dynamic_cast
But I know that using RTTI is the behavior of the company's coding standard, I said to myself: "Yes, although I didn't like RTTI, but I have to change my view. I am obvious, in addition to using RTTI, don't use RTTI, No method. "
"Any questions can be solved by adding indirect hierarchies."
I jumped up, it was the voice of prawns, she didn't know when to ran to my back, "Ah, you are scared me ... What did you say?"
"Any question ..."
"Yes, I listened to it." I don't know where the courage, I dare to interrupt her. "I just don't know where you come out." In fact, this is just a panic. .
"Ha, forget it, small rookie," prawn is oblique to see me, "You think I don't know what you think!" She raised his voice in eight degrees, staring at me, "those poor C language Disciples will use the switch statement to handle different object types. You see: "
/ * A not-atypical c program * / void f (struct somestruct * s) {switch (S-> type) {copy apple: / * do one think * / break; case omy: / * do another think * / break ; / * ... etc. ... * /}}
"When these people learn Stroustrup's C language, the most important thing is to learn how to design a class level."
"Yes," I interrupt her again, I can't wait to let Wendy understand, I still have two minutes, "they should design a Fruit base class, derive apple and orange, and use the Virtual Function to make specific things.
"Very good, small rookie. C language disciples usually can't change. However, you should know that by using Virtual Function, you have added an indirect level." She put down the pen, "What you need is not a new Define function? "
"Yes. But I have no power to do so."
"Because you have no right to modify classes, right!"
"You finally learned the situation, we can't move it. I don't know which guy is designed by this dead class ..." I screamed.
"It's me."
"Ah ..., really?! This, 嘿嘿 ...", I am very embarrassing.
"This class must be very stable because there is a cross-platform problem. But its design allows you to add new Virtual Function, not to worry about RTTI. You can solve this problem by adding an indirect hierarchy. Excuse me, Personnel :: What is accept? "
"Well, this ..."
"This class implements a model, but unfortunately the name of this mode is not very good, it is a PNP called Visitor mode."
[Translator Note: PNP, Poor-named Pattern, does not have a good name]
"Ah, I just read Visitor mode. But that is just a mode that is allowed to access each other, isn't it?"
She sighed, "This is a popular mistake. That V, I think it is Visitor, it is better to say that Virtual is better. This PNP is the most important purpose is allowed to change the class level, Add new virtual functions in the already existing class level. First come and see the details of the ACCEPT implementation of Personnel and its derived classes. "She picked up the pen:
Void Personnel :: Accept (Personnelv & V) {v.visit (* this);} void officer :: accept (personnelv & v) {v.visit (* this);} void captain :: accept (personnelv & v) {v. Visit (* this);} void first :: accept (personnelv & v) {v.visit (* this);
"The base class of Visitor is as follows:"
Class Personnelv / * isitor * / {public: Virtual Void Visit (Personnel &) = 0; Virtual Void Visit (Offer &) = 0; Virtual Void Visit (Captain &) = 0; Virtual Void Visit (First &) = 0;
"Ah, I remember it. When I want to use the Personnel class level, I just call Personnel :: Accept (MyVisitorObject). Because Accept is a virtual function, my myvisitorObject.visit () will target the correct object Type calls, according to the overloaded rule, the compiler will pick the most appropriate Visit to call. Is this unameter of adding a new virtual function? "
"Yes, the small rookie. As long as the class level supports Accept, we can add new virtual functions without changing the level."
"Okay, I know what to do now," I wrote:
class DoSomething: public PersonnelV {public: virtual void Visit (Personnel &); virtual void Visit (Officer &); virtual void Visit (Captain &); virtual void Visit (First &);}; void DoSomething :: Visit (Captain & c) {if ( Femalegueststarispresent) c.TurnonCharm (); else c.startfight ();} void dosomething :: Visit (first & f) {f.raiseeyebrowatcaptainsbehavior ();
Void f (Personnel & P); // is equivalent to p.dosomething ()} int main () {Captain K; first S; f (k); f (s);}
The prawn smiled satisfied, "Maybe this mode for a name, it is better to understand, but unfortunately, the world is often not ä."
[Translator Note: I made a certain amount of deletion in this article. There is a little more discussion in the original text, and LINK has two technical articles. ]
Author Blog: