Skip to content

Instantly share code, notes, and snippets.

@TABETA
Created February 21, 2016 14:24
Show Gist options
  • Save TABETA/6bc956038f3b3c9f4573 to your computer and use it in GitHub Desktop.
Save TABETA/6bc956038f3b3c9f4573 to your computer and use it in GitHub Desktop.
Observerパターンの実装方法比較 ref: http://qiita.com/abedominal/items/4d0238c21ee6f53239e8
#pragma once
#include <cstdint>
#include "Observer.hpp"
// 継承でStrategy部を自分で実装する。
// judgeを自分で持っているのでポインタ経由での関数呼び出しが一回減っている。
class ChangeDetectionObserver : public Observer
{
public:
ChangeDetectionObserver() :count_(){}
void notify(const int& cur, const int& next)override
{
if (judge(cur, next))
{
++count_;
}
}
uint64_t count()override{ return count_; }
private:
uint64_t count_;
bool judge(const int& cur, const int& next)
{
return cur != next;
}
};
#include <iostream>
#include <iomanip>
#include "Timer.hpp"
#include "Observer.hpp"
#include "ObserverBySwitch.hpp"
#include "ObserverInternalStragegy.hpp"
#include "ObserverFP.hpp"
#include "ChangeDetectionObserver.hpp"
#include "TemplatedObserver.hpp"
#include "ObserverT.hpp"
#include "TemplatedSubject.hpp"
#include "Judge.hpp"
template<typename SubType, typename ObType>
class Measure
{
static const uint64_t rep = 100000000;
public:
Measure(){}
double execute(SubType* s)
{
Timer<TimerType::HighAccuracy> timer;
for (uint64_t i = 0; i < rep; ++i)
{
s->set(i);
}
return timer.elapsed();
}
};
#define measure(SubType, ObType) \
{ \
Measure<SubType, ObType> a; \
ObType o; \
SubType s; \
s.attach(&o); \
volatile double r = a.execute(&s); \
auto c = o.count(); \
cout << setiosflags(ios::left) << setw(35) << #ObType":" << fixed << setprecision(6) << r << ", " << c << endl; \
}
int main(int argv, char* argc[])
{
//measure(Subject, ObserverInnerStrategy);
//measure(SubjectFP, ObserverFP);
//measure(Subject, ChangeDetectionObserver);
//measure(Subject, TemplatedObserver<Judge::IsChanged>);
//measure(Subject, TemplatedObserver2<Judge::IsChanged>);
//measure(SubjectT<ObserverT<Judge::IsChanged> >, ObserverT<Judge::IsChanged>);
//measure(SubjectBySwitch, ObserverBySwitch);
/*おまけ。Subjectで扱う型もtemplateにした場合の検証*/
//typedef TemplatedObserverImpl<int, Judge::IsChanged> TemplatedNotifier;
//measure(TemplatedSubject<int>, TemplatedNotifier);
/*判定に対して常にfalseを返す方式と、notify処理自体を空っぽにする方式の比較*/
//typedef NullObserverTImpl<int, Judge::IsChanged> NullNotifier1;
//measure(TemplatedSubject<int>, NullNotifier1);
//typedef TemplatedObserverImpl<int, Judge::Never> NullNotifier2;
//measure(TemplatedSubject<int>, NullNotifier2);
return 0;
}
#pragma once
#include <cstdint>
class Observer
{
public:
virtual void notify(const int&, const int&) = 0;
virtual uint64_t count() = 0;
};
class Subject
{
public:
Subject() :cur_(), observer_(){}
void attach(Observer* observer)
{
observer_ = observer;
}
void set(const int& next)
{
if (observer_ != 0){ observer_->notify(cur_, next); }
cur_ = next;
}
private:
int cur_;
Observer* observer_;
};
#pragma once
#include <cstdint>
//継承なんてしない方式
class ObserverBySwitch
{
public:
enum DetectionType
{
eRised,
eFell,
eChanged,
};
ObserverBySwitch() :count_(), detectionType_(eChanged){}
void setDetectionType(DetectionType detectionType){ detectionType_ = detectionType; }
void notify(const int& cur, const int& next)
{
if (judge(cur, next))
{
++count_;
}
}
uint64_t count(){ return count_; }
private:
bool judge(const int& cur, const int& next)
{
switch (detectionType_)
{
case eRised: return !cur && next;
case eFell: return cur && !next;
case eChanged: return cur != next;
}
return false;
}
uint64_t count_;
DetectionType detectionType_;
};
class SubjectBySwitch
{
public:
SubjectBySwitch() :cur_(), observer_(){}
void attach(ObserverBySwitch* observer)
{
observer_ = observer;
}
void set(const int& next)
{
if (observer_ != 0){ observer_->notify(cur_, next); }
cur_ = next;
}
private:
int cur_;
ObserverBySwitch* observer_;
};
#pragma once
#include <cstdint>
//関数ポインタで判定部を実装。
//count_を引き渡さなくてよくなっている。
class ObserverFP
{
class NotificationStrategy;
public:
ObserverFP() :count_(), judge(isChanged){}
void setFelld() { judge = isFell; }
void setRised() { judge = isRised; }
void setChanged(){ judge = isChanged; }
void notify(const int& cur, const int& next)
{
if ((*judge)(cur, next))
{
++count_;
}
}
uint64_t count(){ return count_; }
private:
uint64_t count_;
bool (*judge)(const int& cur, const int& next);
static bool isFell(const int& cur, const int& next)
{
return cur && !next;
}
static bool isRised(const int& cur, const int& next)
{
return !cur && next;
}
static bool isChanged(const int& cur, const int& next)
{
return cur != next;
}
};
class SubjectFP
{
public:
SubjectFP() :cur_(), observer_(){}
void attach(ObserverFP* observer)
{
observer_ = observer;
}
void set(const int& next)
{
if (observer_ != 0){ observer_->notify(cur_, next); }
cur_ = next;
}
private:
int cur_;
ObserverFP* observer_;
};
#pragma once
#include <cstdint>
#include "Observer.hpp"
// Observerを継承して内部でストラテジを持っている。
// notifyの継承、judgeの継承で合計2回の仮想関数呼び出しが発生。
// また、ストラテジ部で親クラスのメンバを操作するためにcount_を引き渡すというコストも発生。
class ObserverInnerStrategy: public Observer
{
class NotificationStrategy;
public:
ObserverInnerStrategy() :count_(), notifier_(&isChanged_){}
void setFelld() { notifier_ = &isFell_; }
void setRised() { notifier_ = &isRised_; }
void setChanged(){ notifier_ = &isChanged_; }
void notify(const int& cur, const int& next)override
{
notifier_->notify(cur, next, count_);
}
uint64_t count()override{ return count_; }
private:
uint64_t count_;
class NotificationStrategy
{
public:
void notify(const int& cur, const int& next, uint64_t& c)
{
if (judge(cur, next))
{
++c;
}
}
virtual bool judge(const int& cur, const int& next) = 0;
}* notifier_;
class IsFell : public NotificationStrategy
{
public:
bool judge(const int& cur, const int& next)override
{
return cur && !next;
}
}isFell_;
class IsRised : public NotificationStrategy
{
public:
bool judge(const int& cur, const int& next)override
{
return !cur && next;
}
}isRised_;
class IsChanged : public NotificationStrategy
{
public:
bool judge(const int& cur, const int& next)override
{
return cur != next;
}
}isChanged_;
};
#pragma once
#include <cstdint>
template<typename Judge>
class ObserverT
{
public:
ObserverT() :count_(){}
void notify(const int& cur, const int& next)
{
if (Judge()(cur, next))
{
++count_;
}
}
uint64_t count(){ return count_; }
private:
uint64_t count_;
};
template<typename Observer>
class SubjectT
{
public:
SubjectT() :cur_(), observer_(){}
void attach(Observer* observer)
{
observer_ = observer;
}
void set(const int& next)
{
if (observer_ != 0){ observer_->notify(cur_, next); }
cur_ = next;
}
private:
int cur_;
Observer* observer_;
};
#pragma once
#include <cstdint>
#include "Observer.hpp"
//テンプレートで実装するパターン。
template<typename Judge>
class TemplatedObserver : public Observer
{
public:
TemplatedObserver() :count_(){}
void notify(const int& cur, const int& next)override
{
if (Judge::judge(cur, next))
{
++count_;
}
}
uint64_t count()override{ return count_; }
private:
uint64_t count_;
};
//テンプレートで実装するパターンその2。ファンクタのコンストラクタにコストがかかるのか確認。
template<typename Judge>
class TemplatedObserver2 : public Observer
{
public:
TemplatedObserver2() :count_(){}
void notify(const int& cur, const int& next)override
{
if (Judge()(cur, next))
{
++count_;
}
}
uint64_t count()override{ return count_; }
private:
uint64_t count_;
};
#pragma once
#include <cstdint>
#include "Judge.hpp"
template<typename T>
class TemplatedObserverIF
{
public:
virtual void notify(const T& cur, const T& next) = 0;
};
template<typename T, typename Judge>
class TemplatedObserverImpl : public TemplatedObserverIF<T>
{
public:
TemplatedObserverImpl() :count_(){}
void notify(const T& cur, const T& next)
{
if (Judge()(cur, next))
{
++count_;
}
}
uint64_t count(){ return count_; }
private:
uint64_t count_;
};
template<typename T, typename Judge>
class NullObserverTImpl : public TemplatedObserverIF<T>
{
public:
NullObserverTImpl(){}
void notify(const T& , const T& ){}
uint64_t count(){ return 0; }
};
template<typename T>
class TemplatedSubject
{
public:
TemplatedSubject():cur_(), observer_(){}
void attach(TemplatedObserverIF<T>* observer)
{
observer_ = observer;
}
void set(const T& next)
{
if(observer_ != 0)observer_->notify(cur_, next);
cur_ = next;
}
private:
T cur_;
TemplatedObserverIF<T>* observer_;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment