美文网首页
Qt源码中的设计模式:事件传播机制与责任链模式

Qt源码中的设计模式:事件传播机制与责任链模式

作者: 饮茶先啦靓仔 | 来源:发表于2023-05-13 23:27 被阅读0次

    责任链模式

    责任链模式是一种行为型设计模式,它将请求和处理请求的对象解耦,形成一个请求处理链。在该模式中,多个对象依次处理同一个请求,直到找到一个能够处理该请求的对象为止。

    责任链模式的主要作用是解决多个对象处理同一个请求时的耦合问题。在传统的设计中,如果要处理一个请求,通常需要在代码中明确指定请求由哪个对象来处理。这样,请求和处理请求的对象就存在了一定的耦合关系,这会导致系统的可扩展性和可维护性变得很差。而责任链模式则通过将请求和处理请求的对象解耦,使得系统更加灵活和可扩展。

    在责任链模式中,每个对象都有一个指向下一个对象的引用,形成了一个链式结构。当一个请求发生时,它会被依次传递给链上的每个对象,直到找到一个能够处理该请求的对象为止。每个对象都可以根据自己的状态和职责决定是否处理该请求,从而实现请求的处理与请求的发起者解耦。

    责任链模式UML类图

    在实际应用中,责任链模式有很多的使用场景。比如,在一个大型GUI应用程序中,有很多用户界面元素需要响应鼠标或键盘输入事件。如果每个用户界面元素都必须处理所有的输入事件,那么系统的性能将受到严重影响。使用责任链模式,可以将这些用户界面元素组织成一个责任链,每个用户界面元素只需要处理自己关心的输入事件,将不关心的事件交给下一个对象处理。这样可以显著提高系统的处理能力和吞吐量,同时也提高了代码的可扩展性和可维护性。

    责任链模式还可以用来处理日志记录、权限控制、异常处理、消息传递等问题。在这些应用场景中,每个对象都可以根据自己的状态和职责决定是否处理该请求,从而实现请求的处理与请求的发起者解耦,提高了系统的灵活性和可扩展性。

    下面给出责任链模式的C++实现:

    #include <iostream>  
    #include <memory>  
    #include <string>  
      
    // 抽象处理器类  
    class Handler {  
    public:  
        virtual ~Handler() {}  
      
        // 处理请求的方法  
        virtual void HandleRequest(const std::string& request) {  
            if (successor_) {  
                successor_->HandleRequest(request);  
            }  
        }  
      
        // 设置后继处理器  
        void setSuccessor(std::shared_ptr<Handler> successor) {  
            successor_ = successor;  
        }  
      
    protected:  
        std::shared_ptr<Handler> successor_;  
    };  
      
    // 具体处理器类A  
    class ConcreteHandlerA : public Handler {  
    public:  
        void HandleRequest(const std::string& request) override {  
            if (request == "A") {  
                std::cout << "ConcreteHandlerA handles the request: " << request << std::endl;  
            } else {  
                Handler::HandleRequest(request);  
            }  
        }  
    };  
      
    // 具体处理器类B  
    class ConcreteHandlerB : public Handler {  
    public:  
        void HandleRequest(const std::string& request) override {  
            if (request == "B") {  
                std::cout << "ConcreteHandlerB handles the request: " << request << std::endl;  
            } else {  
                Handler::HandleRequest(request);  
            }  
        }  
    };  
      
    // 具体处理器类C  
    class ConcreteHandlerC : public Handler {  
    public:  
        void HandleRequest(const std::string& request) override {  
            if (request == "C") {  
                std::cout << "ConcreteHandlerC handles the request: " << request << std::endl;  
            } else {  
                Handler::HandleRequest(request);  
            }  
        }  
    };  
      
    int main() {  
        std::shared_ptr<Handler> handlerA = std::make_shared<ConcreteHandlerA>();  
        std::shared_ptr<Handler> handlerB = std::make_shared<ConcreteHandlerB>();  
        std::shared_ptr<Handler> handlerC = std::make_shared<ConcreteHandlerC>();  
      
        // 设置责任链  
        handlerA->setSuccessor(handlerB);  
        handlerB->setSuccessor(handlerC);  
      
        // 发送请求  
        handlerA->HandleRequest("A");  
        handlerA->HandleRequest("B");  
        handlerA->HandleRequest("C");  
        handlerA->HandleRequest("D");  
      
        return 0;  
    }  
    

    在上述示例中,Handler是抽象处理器类,定义了处理请求的方法。ConcreteHandlerA、ConcreteHandlerB和ConcreteHandlerC是具体处理器类,分别处理请求A、B和C。在创建这些处理器对象时,按照责任链的顺序将它们连接起来。在main函数中,程序创建了一个责任链,将请求依次发送给处理器A、B、C,如果没有任何处理器能够处理请求,则请求不会被处理。

    总之,责任链模式是一种非常有用的设计模式,它可以将请求和处理请求的对象解耦,从而提高系统的灵活性和可扩展性。在实际应用中,它可以帮助我们解决很多复杂的问题,提高系统的处理能力和吞吐量,同时也提高了代码的可维护性和可读性。

    Qt事件传播机制

    在Qt中,事件传播机制是实现GUI交互的重要机制之一,而事件传播机制是基于对象树机制的。在之前的文章:Qt源码中的设计模式:对象树机制与组合模式 中说到,每个对象都有一个父对象和零个或多个子对象,构成了一个对象树。子对象的生命周期与父对象相关联。当一个父对象被删除时,它的所有子对象也会被自动删除。在事件传播过程中,Qt会按照对象树的结构依次将该事件传递给每个对象,直到找到一个能够处理该事件的对象为止。这种机制可以让Qt中的对象之间解耦,提高系统的灵活性和可扩展性。

    需要注意的是,只有一些特定的事件会被传递,其他事件则不会被传递。鼠标事件、键盘事件、绘图事件、聚焦事件和拖放事件会被传递到对象的父对象和子对象。除了这些事件,Qt还提供了很多高级事件,如定时器事件、系统事件等,需要在对象中显式地处理。

    事件传播机制可以很好地体现责任链模式。责任链模式是一种行为型设计模式,它将请求和处理请求的对象解耦,形成一个请求处理链。在该模式中,多个对象依次处理同一个请求,直到找到一个能够处理该请求的对象为止,这个过程就是责任链模式中的请求处理链。不过可能会有读者疑惑:既然对象树机制是组合模式的一种实现,而事件传播机制又体现出责任链模式,这是否有冲突。实际上,设计模式在实际工程中经常是有变体的,也会结合起来使用。在父子对象的内存管理问题上,更多的展现出组合模式的思想;在事件的传播上,则更多体现出责任链模式的思想。这是因为,一些事件可以被对象树上某一个节点处理掉,其他节点不需要关注;而父子节点的销毁工作,则是整个链条上所有节点都需要处理的事情。读者需要结合Qt源码,以及两个设计模式要解决的问题和场景,去理解其中的差异。

    事件传播机制和对象树机制共同构成了Qt中对象的一种管理方式和事件的一种传播方式。通过在对象之间建立处理请求的责任链,可以使请求的处理与请求的发起者解耦,提高代码的可扩展性和可维护性。另外,Qt还提供了QObject::installEventFilter方法,即可以安装一个事件过滤器来处理事件。事件过滤器是一个单独的对象,它可以拦截并处理一个或多个对象的事件。因此,事件过滤器也可以看作责任链模式中的一环。

    下面给出Qt源码中如何实现事件传播机制,并且体现出责任链模式的一个示例。注意,这个示例省略了比较多的细节,但作为示例去描述Qt源码中如何体现责任链模式,应该是勉强足够的。

    #include <iostream>
    
    enum EventType { UnknownEvent, MouseButtonPressEvent };
    
    class QEvent {
    public:
        explicit QEvent(EventType type) : _type(type), _accepted(false) {}
        virtual ~QEvent() {}
    
        EventType type() const { return _type; }
        void accept() { _accepted = true; }
        void ignore() { _accepted = false; }
        bool isAccepted() const { return _accepted; }
    
    private:
        EventType _type;
        bool _accepted;
    };
    
    class QObject {
    public:
        virtual ~QObject() {}
    
        virtual bool event(QEvent *event) {
            // 默认实现,不处理任何事件
            return false;
        }
        
        virtual bool eventFilter(QObject *, QEvent *) {
            // 默认实现,不处理任何事件
            return false;
        }
    };
    
    class QWidget : public QObject {
    public:
        virtual ~QWidget() {}
    
        bool event(QEvent *event) override {
            switch (event->type()) {
            case MouseButtonPressEvent:
                mousePressEvent(event);
                return true;
            default:
                return QObject::event(event);
            }
        }
    
        virtual void mousePressEvent(QEvent *event) {
            std::cout << "QWidget: Mouse button press event\n";
            event->accept();
        }
    };
    

    通过上面的示例,可以看到,QObjectQWidgetQEvent三个类的设计和实现,体现了责任链模式。作为所有Qt对象的基类,QObject通过提供event()eventFilter()两个虚函数,构建了事件处理的基础。其中event()负责处理自身接收到的事件,eventFilter()则处理需要过滤的事件。如果事件未被处理,它会被传递给父对象,形成责任链。

    QWidget,继承自QObject,重写了event()方法,专门处理用户界面事件。例如,当按键事件发生,我们可以获取按下的键码,然后调用事件的accept()方法表示处理完成,从而阻止事件继续向上传递。如果QWidget无法处理事件,它会将事件传递给父对象。

    QEvent是所有事件的基类,每个事件有独特的类型标识符。QEvent类还提供了isAccepted()accept()ignore()方法,标记事件是否被接受和处理。一旦事件被处理,我们可以通过调用accept()方法停止事件的传递,避免重复处理。

    因此,QObject作为事件处理接口,QWidget实现具体的处理逻辑,而QEvent则扮演了请求的角色。这三个类的协作为Qt提供了一种高效、灵活且可扩展的事件处理机制,帮助开发者轻松处理各种事件和场景。

    总结

    至此,我们已经解析了Qt的对象树机制,构建在对象树机制之上的事件传递机制,以及它们背后的设计思想。在后续的文章中,我们可能还会继续解析Qt源码中事件机制相关的设计模式。事件机制确实是Qt中的核心机制之一,我们能够深挖的东西,恐怕还有很多。

    相关文章

      网友评论

          本文标题:Qt源码中的设计模式:事件传播机制与责任链模式

          本文链接:https://www.haomeiwen.com/subject/pepusdtx.html