1. Trang chủ
  2. » Công Nghệ Thông Tin

HandBooks Professional Java-C-Scrip-SQL part 93 docx

6 59 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 20,47 KB

Nội dung

This is equivalent to using boost::ref and assigning the function object ks to each instance of function. The power that is wielded with the addition of state to callbacks is tremendous, and is one of the great advantages to using Boost.Function rather than function pointers. Using Boost.Bind with Boost.Function Things become even more interesting when we combine Boost.Function with a library that supports argument binding. Boost.Bind provides argument binding for free functions, class member functions, and class variables. This is a perfect fit for Boost.Function, where we often need this type of binding because the classes that we are working with are not themselves function objects. So, we transform them into function objects using Boost.Bind, and then we can store them for later invocation with Boost.Function. When separating graphical user interfaces (GUIs) from details on how to handle actions (events) from the user, callbacks of some sort are almost always used. If this callback mechanism is based on function pointers, it is hard to avoid severe limitations of the types that can be used with the callback, which in turn increases the risk of adding coupling between the presentation and the business logic. We can avoid this altogether by using Boost.Function, and when combined with a library that supports argument binding, we can supply the context to invocations of functions with ease. This is one of the most common uses of this libraryto separate knowledge of the business logic from the presentation layer. This example includes a state-of-the-art tape recorder, which is defined thusly. class tape_recorder { public: void play() { std::cout << "Since my baby left me \n"; } void stop() { std::cout << "OK, taking a break\n"; } void forward() { std::cout << "whizzz\n"; } void rewind() { std::cout << "zzzihw\n"; } void record(const std::string& sound) { std::cout << "Recorded: " << sound << '\n'; } }; This tape recorder could be controlled from a GUI, or perhaps from a scripting client, or from any other source, which means that we don't want to couple the invocation of the functions to their implementation. A common way to create that separation is through special objects that are only responsible for executing a command, allowing the client to remain ignorant of how the command is executed. This is known as the Command pattern, and is very useful in its own right. One of the problems with the typical implementation of this pattern is the need to create separate classes to execute each command. The following snippet demonstrates how this might look: class command_base { public: virtual bool enabled() const=0; virtual void execute()=0; virtual ~command_base() {} }; class play_command : public command_base { tape_recorder* p_; public: play_command(tape_recorder* p):p_(p) {} bool enabled() const { return true; } void execute() { p_->play(); } }; class stop_command : public command_base { tape_recorder* p_; public: stop_command(tape_recorder* p):p_(p) {} bool enabled() const { return true; } void execute() { p_->stop(); } }; This is not a very attractive solution, because it leads to code bloat in the form of numerous simple command classes, all with the sole responsibility of invoking a single member function on an object. Sometimes, this can prove necessary, because the commands may need to implement business logic as well as execute a function, but it is often just a result of limitations in the tools that we are using. These command classes can be used like this: int main() { tape_recorder tr; // Using the command pattern command_base* pPlay=new play_command(&tr); command_base* pStop=new stop_command(&tr); // Invoked when pressing a button pPlay->execute(); pStop->execute(); delete pPlay; delete pStop; } Now, rather than creating an additional number of concrete command classes, we could generalize a bit if we take advantage of the fact that the commands we are implementing are all calling a member function with a void return, taking no argument (ignoring for the moment the function record, which does take an argument). Rather than creating a family of concrete commands, we could store a pointer to the correct member function in the class. This is a huge step in the right direction, [6] and it works like this: [6] Albeit slightly less efficient. class tape_recorder_command : public command_base { void (tape_recorder::*func_)(); tape_recorder* p_; public: tape_recorder_command( tape_recorder* p, void (tape_recorder::*func)()) : p_(p),func_(func) {} bool enabled() const { return true; } void execute() { (p_->*func_)(); } }; This implementation of the commands is much nicer, because it relieves us from having to create separate classes that do basically the same thing. The difference here is that we are storing a pointer to a tape_recorder member function in func_, which is provided in the constructor. The execution of the command is not something you should show all your friends, because the pointer-to-member operators do have a habit of confusing people. However, this can be considered a low-level implementation detail, so that's fine. With this class, we have performed a generalization that proves useful in that we don't have to implement separate command classes anymore. int main() { tape_recorder tr; // Using the improved command command_base* pPlay= new tape_recorder_command(&tr,&tape_recorder::play); command_base* pStop= new tape_recorder_command(&tr,&tape_recorder::stop); // Invoked from a GUI, or a scripting client pPlay->execute(); pStop->execute(); delete pPlay; delete pStop; } You may not realize it, but we're actually starting to implement a simplified version of boost::function, which already does what we want. Rather than reinvent the wheel, let's focus on the task at hand: separating the invocation and the implementation. Here is a new implementation of the command class, which is a lot easier to write, maintain, and understand. class command { boost::function<void()> f_; public: command() {} command(boost::function<void()> f):f_(f) {} void execute() { if (f_) { f_(); } } template <typename Func> void set_function(Func f) { f_=f; } bool enabled() const { return f_; } }; By using Boost.Function in the implementation, we can immediately benefit from the flexibility of being compatible both with functions and function objectsincluding function objects produced by binders. The command class stores the function in a boost::function that returns void and doesn't take any arguments. To make the class more flexible, we provide a way to change the function object at runtime, using a parameterized member function, set_function. template <typename Func> void set_function(Func f) { f_=f; } By using parameterization, any function, function object, or binder is compatible with our command class. We could also have opted to have a boost:: function as argument, and achieve the same effect using the converting constructor of function. This command class is very general, and we can use it with our tape_recorder class or just about anything else. An additional advantage over the previous approach when using a base class and concrete derived classes (which in turn makes us use pointers for polymorphic behavior) is that it becomes easier to manage lifetime issueswe don't have to delete the command objects anymore, as they can be passed and saved by value. We test whether the command is enabled or not by using the

Ngày đăng: 06/07/2014, 03:20