设计模式汇总
1. 访问者模式 Dive IntoDESIGN PATTERNS
书很不错,还有中文版
比如看了访问者模式
会有茅塞顿开的感脚,讲得非常透彻清晰。
访问者模式主要把一个类的数据结构属性和操作分开。
访问者模式就是把与类自身的不相干的功能,不在类内部实现,把他们都统一实现到vistor类中。自己类内部只添加一个统一接口vistor调用,将自身传过去就行。
比如,程序主体会时不时执行到 accept方法。我想弄个造访者,获得到accept时的状态。我自己处理。我这个造访者隐含规定我有个visit方法。
当程序执行时,注入造访者实例。当accept执行时,将自己状态调用造访者的visit方法。这样造访者就可以处理了。
做多个造访者,就可以干不同的事情了。比如有logCanvas,也有RecordCanvas序列化绘制指令,还有playCanvas进行渲染指令。它们是不同的造访者。
c++使用 variant和visit 代替访问者模式。
c++20高级编程》p65. 双重派发。
《c++ software design》chapter 4. 讲的也非常棒。用了c++17的variant存放异构类型。
2. builder模式讲解用创建 html元素
https://www.cprogramming.com/tutorial/lesson18.html
https://dzone.com/articles/composite-design-pattern-in-modern-c
3. 策略模式和命令模式
《c++ software design》chater 5 完美演绎了图形库 draw 面临的问题。对skia canvas是个好的说明。
In Skia, the strategy pattern is implemented using the SkCanvas::Draw() method. This method takes a drawing command as its argument. The drawing command specifies the type of drawing operation that should be performed.
Skia provides a number of different drawing commands. These commands include:
- drawRect(): This command draws a rectangle.
- drawCircle(): This command draws a circle.
- drawText(): This command draws text.
The Skia team can add new drawing commands without having to change the code that calls the SkCanvas::Draw() method. This is because the drawing commands are implemented as separate classes. These classes can be added to the Skia library without having to change the code that uses the SkCanvas class.
结合skia的skcanvas,就是策略模式的应用。比如canvas本身是绘制,但如果需要将绘制命令保存下来,就实现一个RecordCanvas,在这个类中对命令进行保存。
4,观察者模式
在chromium的观察者模式应用中,观察者即被通知的对象,observable即发起通知者。
首先Observable将自己传递给 观察者,比如在观察着构造时。在构造中,观察者调用Observable的addObserver方法,将观察者自己注册进入队列中。等于将自己给了Observable。生死可以都让它控制。自己构造完后都没必要保存对象变量了。
接口定义:
InputEventObserver
// Observer for WebInputEvents.
class InputEventObserver {
public:
virtual ~InputEventObserver() {}
virtual void OnInputEvent(const blink::WebInputEvent&) {}
virtual void OnInputEventAck(blink::mojom::InputEventResultSource source,
blink::mojom::InputEventResultState state,
const blink::WebInputEvent&) {}
}
接口实现:
InputHandler::InputInjector : public RenderWidgetHost::InputEventObserver
class InputHandler::InputInjector
: public RenderWidgetHost::InputEventObserver {
public:
InputInjector(InputHandler* owner, RenderWidgetHostImpl* widget_host)
: owner_(owner), widget_host_(widget_host->GetWeakPtr()) {
widget_host->AddInputEventObserver(this);/////////////////将自己加入
}
InputInjector(const InputInjector&) = delete;
InputInjector& operator=(const InputInjector&) = delete;
private:
void OnInputEvent(const blink::WebInputEvent& event) override {
input_queued_ = true;
}
void MaybeSelfDestruct() {
if (!pending_key_callbacks_.empty() || !pending_mouse_callbacks_.empty())
return;
if (widget_host_)
widget_host_->RemoveInputEventObserver(this);
owner_->injectors_.erase(this);
}
};
RenderWidgetHost 接口
RenderWidgetHost
// A RenderWidgetHost acts as the abstraction for compositing and input
// functionality. It can exist in 3 different scenarios:
//
// 1. Popups, which are spawned in situations like <select> menus or
// HTML calendar widgets. These are browser-implemented widgets that
// are created and owned by WebContents in response to a renderer
// request. Since they are divorced from the web page (they are not
// clipped to the bounds of the page), they are an independent
// compositing and input target. As they are owned by WebContents,
// they are also destroyed by WebContents.
//
// 2. Main frames, which are a root frame of a WebContents. These frames
// are separated from the browser UI for compositing and input, as the
// renderer lives in its own coordinate space. These are attached to
// the lifetime of the main frame (currently, owned by the
// RenderViewHost, though that should change one day as per
// https://crbug.com/419087).
//
// 3. Child local root frames, which are iframes isolated from their
// parent frame for security or performance purposes. This allows
// them to be placed in an arbitrary process relative to their
// parent frame. Since they are isolated from the parent, they live
// in their own coordinate space and are an independent unit of
// compositing and input. These are attached to the lifetime of
// the local root frame, and are explicitly owned by the
// RenderFrameHost.
//
// A RenderWidgetHost is platform-agnostic. It defers platform-specific
// behaviour to its RenderWidgetHostView, which ties the compositing
// output into the native browser UI. Child local root frames also have
// a separate "platform" RenderWidgetHostView type at this time, though
// it stretches the abstraction uncomfortably.
//
// The RenderWidgetHostView has a complex and somewhat broken lifetime as
// of this writing (see, e.g. https://crbug.com/1161585). It is eagerly
// created along with the RenderWidgetHost on the first creation, before
// the renderer process may exist. It is destroyed if the renderer process
// exits, and not recreated at that time. Then it is recreated lazily when
// the associated renderer frame/widget is recreated.
class CONTENT_EXPORT RenderWidgetHost {
// Add/remove an input event observer.
virtual void AddInputEventObserver(InputEventObserver* observer) = 0;
virtual void RemoveInputEventObserver(InputEventObserver* observer) = 0;
.....
}
RenderWidgetHostImpl 实现类
RenderWidgetHostImpl
// This implements the RenderWidgetHost interface that is exposed to
// embedders of content, and adds things only visible to content.
//
// Several core rendering primitives are mirrored between the browser and
// renderer. These are `blink::WidgetBase`, `RenderFrame` and `blink::WebView`.
// Their browser counterparts are `RenderWidgetHost`, `RenderFrameHost` and
// `RenderViewHost`.
//
// For simplicity and clarity, we want the object ownership graph in the
// renderer to mirror the object ownership graph in the browser. The IPC message
// that tears down the renderer object graph should be targeted at the root
// object, and should be sent by the destructor/finalizer of the root object in
// the browser.
//
// Note: We must tear down the renderer object graph with a single IPC to avoid
// inconsistencies in renderer state.
//
// RenderWidget represents a surface that can paint and receive input. It is
// used in four contexts:
// * Main frame for webpage (root is `blink::WebView`)
// * Child frame for webpage (root is RenderFrame)
// * Popups (root is RenderWidget)
// * Pepper Fullscreen (root is RenderWidget)
//
// Destruction of the RenderWidgetHost will trigger destruction of the
// RenderWidget iff RenderWidget is the root of the renderer object graph.
//
// Note: We want to converge on RenderFrame always being the root.
class CONTENT_EXPORT RenderWidgetHostImpl
: public RenderWidgetHost,
public FrameTokenMessageQueue::Client,
public InputRouterImplClient,
public InputDispositionHandler,
public RenderProcessHostObserver,
public RenderProcessHostPriorityClient,
public SyntheticGestureController::Delegate,
public RenderFrameMetadataProvider::Observer,
public blink::mojom::FrameWidgetHost,
public blink::mojom::PopupWidgetHost,
public blink::mojom::WidgetHost,
public blink::mojom::PointerLockContext {
// Input event callbacks.
base::ObserverList<RenderWidgetHost::InputEventObserver>::Unchecked
input_event_observers_;
.....
}
void RenderWidgetHostImpl::AddInputEventObserver(
RenderWidgetHost::InputEventObserver* observer) {
if (!input_event_observers_.HasObserver(observer)) {
input_event_observers_.AddObserver(observer);
}
}
void RenderWidgetHostImpl::RemoveInputEventObserver(
RenderWidgetHost::InputEventObserver* observer) {
input_event_observers_.RemoveObserver(observer);
}
主调用:
auto* rwh= new RenderWidgetHostImpl(...); // Observable创建
new InputHandler::InputInjector( rwh, ...);//观察者创建
这时候就已经注册上了。
参考:
https://github.com/meganesu/head-first-design-patterns
《c++20设计模式》 https://github.com/Apress/design-patterns-modern-cpp20 这本书可以作为参考,缺点就是写的比较简陋,比如访问者模式才4页,策略模式才2页感觉。但是里有些言简意赅的小例子不错。