C# ライクなイベントハンドリングを実装する.パターンの名称がわからないので,とりあえず Observable Member という名称で記載する.
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.2 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
$ clang++ --version
Ubuntu clang version 14.0.0-1ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
design_pattern/observable_member.cpp
// Copyright (c) 2023, Kumazawa (sparrow-blue)
// This source code is licensed under the BSD 3-Clause License.
// See https://github.com/sparrow-blue/blog/blob/main/LICENSE for details.
#include "observable_member/observable_member.hpp"
#include <iostream>
using design_pattern::observable_member::EventHandler;
using design_pattern::observable_member::EventArgs;
using design_pattern::observable_member::ObservableLine;
int main() {
auto observable_line = ObservableLine(10, 10);
observable_line.HeightChanged += [](void* sender, EventArgs<int> e) {
std::cout << "Height Changed: " << e.old_value << " -> " << e.new_value << std::endl;
};
observable_line.WidthChanged += [](void* sender, EventArgs<int> e) {
std::cout << "Width Changed: " << e.old_value << " -> " << e.new_value << std::endl;
};
// 値の変更をトリガにイベントを発火する.
observable_line.height(9);
observable_line.width(0);
observable_line.width(3);
observable_line.height(7);
return 0;
}
design_pattern/observable_member/observable_member.hpp
// Copyright (c) 2023, Kumazawa (sparrow-blue)
// This source code is licensed under the BSD 3-Clause License.
// See https://github.com/sparrow-blue/blog/blob/main/LICENSE for details.
#ifndef DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLE_MEMBER_HPP_
#define DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLE_MEMBER_HPP_
#include "ObservableDiagram.hpp"
#include "ObservableLine.hpp"
#include "EventHandler.hpp"
#endif // DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLE_MEMBER_HPP_
design_pattern/observable_member/ObservableDiagram.hpp
// Copyright (c) 2023, Kumazawa (sparrow-blue)
// This source code is licensed under the BSD 3-Clause License.
// See https://github.com/sparrow-blue/blog/blob/main/LICENSE for details.
#ifndef DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLEDIAGRAM_HPP_
#define DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLEDIAGRAM_HPP_
#include "EventHandler.hpp"
namespace design_pattern::observable_member {
class ObservableDiagram {
public:
ObservableDiagram(int width, int height) : width_(width), height_(height) {}
#pragma region width
EventHandler<int> WidthChanged;
void width(int width) {
auto event_args = EventArgs<int>(width, width_);
this->width_ = width;
WidthChanged.Invoke(this, event_args);
}
int width() { return this->width_; }
#pragma endregion
#pragma region height
EventHandler<int> HeightChanged;
void height(int height) {
auto event_args = EventArgs<int>(height, height_);
this->height_ = height;
HeightChanged.Invoke(this, event_args);
}
int height() { return this->height_; }
#pragma endregion
private:
int width_;
int height_;
};
} // namespace design_pattern::observable_member
#endif // DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLEDIAGRAM_HPP_
design_pattern/observable_member/ObservableLine.hpp
// Copyright (c) 2023, Kumazawa (sparrow-blue)
// This source code is licensed under the BSD 3-Clause License.
// See https://github.com/sparrow-blue/blog/blob/main/LICENSE for details.
#ifndef DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLELINE_HPP_
#define DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLELINE_HPP_
#include "ObservableDiagram.hpp"
namespace design_pattern::observable_member {
class ObservableLine : public ObservableDiagram {
public:
ObservableLine(int width, int height) : ObservableDiagram(width, height) {}
};
} // namespace design_pattern::observable_member
#endif // DESIGN_PATTERN_OBSERVABLE_MEMBER_OBSERVABLELINE_HPP_
design_pattern/observable_member/EventHandler.hpp
// Copyright (c) 2023, Kumazawa (sparrow-blue)
// This source code is licensed under the BSD 3-Clause License.
// See https://github.com/sparrow-blue/blog/blob/main/LICENSE for details.
#ifndef DESIGN_PATTERN_OBSERVABLE_MEMBER_EVENTHANDLER_HPP_
#define DESIGN_PATTERN_OBSERVABLE_MEMBER_EVENTHANDLER_HPP_
#include <functional>
#include <memory>
#include <vector>
namespace design_pattern::observable_member {
template <typename T>
class EventArgs {
public:
EventArgs(T after, T before) : new_value(after), old_value(before) {}
T new_value;
T old_value;
};
template <typename T>
class EventHandler {
using Event = std::function<void(void*, EventArgs<T>)>;
public:
EventHandler() : subscribers_(std::make_unique<std::vector<Event>>()) {}
void Invoke(void* sender, EventArgs<T> event_args) {
std::for_each(subscribers_->begin(), subscribers_->end(),
[&sender, &event_args](Event& subscriber) { subscriber(sender, event_args); });
}
void Subscribe(Event event) { subscribers_->push_back(event); }
void operator+=(Event&& event) { Subscribe(event); }
private:
const std::unique_ptr<std::vector<Event>> subscribers_;
};
} // namespace design_pattern::observable_member
#endif // DESIGN_PATTERN_OBSERVABLE_MEMBER_EVENTHANDLER_HPP_
$ date && ./content/build/design_pattern/observable_member
Sun Jun 18 00:29:26 JST 2023
Height Changed: 10 -> 9
Width Changed: 10 -> 0
Width Changed: 0 -> 3
Height Changed: 9 -> 7