砂風呂

いろいろなことを書く

Observerパターンの練習をしてみた

下記のページを参考にObserverパターンを作る練習をしてみたので、そのコードと重要だと思った点をまとめてみようと思います。

17.Observer パターン | TECHSCORE(テックスコア)

Subject(生徒)からObserver(新人先生)に伝える内容

enum class MessageKind{
	UPDATE_COUNT,
};

struct Message{
public:
	Message(int value, MessageKind kind)
	{
		this->value = value;
		this->kind  = kind;
	}
public:
	int value;
	MessageKind kind;
};

抽象クラス

class IObserver{
public:
	virtual void update(Message) = 0;
};

class Subject abstract{
public:
	void addObserver(IObserver* observer)
	{
		observerList.push_front(observer);
	}
	void notifyObservers()
	{

	}
	virtual void run(int count) = 0;

protected:
	std::list<IObserver*> observerList;
};

派生クラス

class Teacher : public IObserver{
public:
	Teacher()
	{
		stuScore = 0;
	}
	void update(Message message) override
	{
		if (message.kind == MessageKind::UPDATE_COUNT){
			stuScore = message.value;
		}
	}
	void display()
	{
		std::cout << "[Teacher] student's score: " << stuScore << std::endl;
	}
private:
	int stuScore;
};

class Student : public Subject{
public:
	Student(IObserver* observer)
	{
		addObserver(observer);
		this->count = 0;
	}
	Student(IObserver* observer, int count)
	{
		addObserver(observer);
		this->count = count;
	}
	void run(int count)
	{
		this->count += count;
		notifyObservers();
	}
	void display()
	{
		std::cout << "[Student]student's count: " << count << std::endl;
	}
private:
	void notifyObservers()
	{
		for (const auto& observer : observerList){
			observer->update(Message(this->count, MessageKind::UPDATE_COUNT));
		}
	}

private:
	int count;
};

main関数

int main()
{
	Teacher* teacher;
	teacher = new Teacher();
	Student* stu1;
	stu1 = new Student(teacher);

	teacher->display();
	stu1->display();
	std::cout << std::endl;

	stu1->run(2);

	teacher->display();
	stu1->display();
	std::cout << std::endl;

	stu1->run(3);

	teacher->display();
	stu1->display();
	std::cout << std::endl;

	delete teacher;
	delete stu1;

	return 0;
}

まとめ?

Message構造体は始めはclassで実装しようと考えたのですが、そうするとTeacherがMessageを受け取った時、つまりTeacher::update()が呼ばれた時にMessageのフィールド変数valueをgetする処理が、オブジェクト指向の9つのルールのうちの一つ「getterは使わない」に引っかかってしまいます。それを回避した結果として構造体にしました。デフォルトでメンバーがpublicかprivateになるかを除けば構造体もクラスとほぼ一緒じゃん!と言われればそれまでなんですけど。なんでこんなルール守ってるかというとはてなダイアリ(http://d.hatena.ne.jp/seaview_p35/20150218/p1)にも書いたんですがクラスの肥大化が起こってクラス設計を見なおさないといけないなーと思ったためです。