blob: 12aeae71b7950d75101894677287e659c9ca1645 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
// Copyright (C) 2008-2018 Lorenzo Caminiti
// Distributed under the Boost Software License, Version 1.0 (see accompanying
// file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt).
// See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html
//[mitchell02_subject
#ifndef SUBJECT_HPP_
#define SUBJECT_HPP_
#include "observer.hpp"
#include <boost/contract.hpp>
#include <vector>
#include <algorithm>
#include <cassert>
// Subject for observer design pattern.
class subject {
friend class boost::contract::access;
void invariant() const {
BOOST_CONTRACT_ASSERT_AUDIT(all_observers_valid(observers())); // Valid.
}
public:
/* Creation */
// Construct subject with no observer.
subject() {
// Check invariant.
boost::contract::check c = boost::contract::constructor(this);
}
// Destroy subject.
virtual ~subject() {
// Check invariant.
boost::contract::check c = boost::contract::destructor(this);
}
/* Queries */
// If given object is attached.
bool attached(observer const* ob) const {
boost::contract::check c = boost::contract::public_function(this)
.precondition([&] {
BOOST_CONTRACT_ASSERT(ob); // Not null.
})
;
return std::find(observers_.cbegin(), observers_.cend(), ob) !=
observers_.cend();
}
/* Commands */
// Attach given object as an observer.
void attach(observer* ob) {
boost::contract::old_ptr<std::vector<observer const*> > old_observers;
#ifdef BOOST_CONTRACT_AUDITS
old_observers = BOOST_CONTRACT_OLDOF(observers());
#endif
boost::contract::check c = boost::contract::public_function(this)
.precondition([&] {
BOOST_CONTRACT_ASSERT(ob); // Not null.
BOOST_CONTRACT_ASSERT(!attached(ob)); // Not already attached.
})
.postcondition([&] {
BOOST_CONTRACT_ASSERT(attached(ob)); // Attached.
// Others not changed (frame rule).
BOOST_CONTRACT_ASSERT_AUDIT(other_observers_unchanged(
*old_observers, observers(), ob));
})
;
observers_.push_back(ob);
}
protected:
// Contracts could have been omitted for protected/private with no pre/post.
/* Queries */
// All observers attached to this subject.
std::vector<observer const*> observers() const {
std::vector<observer const*> obs;
for(std::vector<observer*>::const_iterator i = observers_.cbegin();
i != observers_.cend(); ++i) {
obs.push_back(*i);
}
return obs;
}
/* Commands */
// Update all attached observers.
void notify() {
// Protected members use `function` (no inv and no subcontracting).
boost::contract::check c = boost::contract::function()
.postcondition([&] {
// All updated.
BOOST_CONTRACT_ASSERT_AUDIT(all_observers_updated(observers()));
})
;
for(std::vector<observer*>::iterator i = observers_.begin();
i != observers_.end(); ++i) {
// Class invariants ensure no null pointers in observers but class
// invariants not checked for non-public functions so assert here.
assert(*i); // Pointer not null (defensive programming).
(*i)->update();
}
}
private:
/* Contract Helpers */
static bool all_observers_valid(std::vector<observer const*> const& obs) {
for(std::vector<observer const*>::const_iterator i = obs.cbegin();
i != obs.cend(); ++i) {
if(!*i) return false;
}
return true;
}
static bool other_observers_unchanged(
std::vector<observer const*> const& old_obs,
std::vector<observer const*> const& new_obs,
observer const* ob
) {
// Private members use `function` (no inv and no subcontracting).
boost::contract::check c = boost::contract::function()
.precondition([&] {
BOOST_CONTRACT_ASSERT(ob); // Not null.
})
;
std::vector<observer const*> remaining = new_obs;
std::remove(remaining.begin(), remaining.end(), ob);
std::vector<observer const*>::const_iterator remaining_it =
remaining.begin();
std::vector<observer const*>::const_iterator old_it = old_obs.begin();
while(remaining.cend() != remaining_it && old_obs.cend() != old_it) {
if(*remaining_it != *old_it) return false;
++remaining_it;
++old_it;
}
return true;
}
static bool all_observers_updated(std::vector<observer const*> const& obs) {
for(std::vector<observer const*>::const_iterator i = obs.cbegin();
i != obs.cend(); ++i) {
if(!*i) return false;
if(!(*i)->up_to_date_with_subject()) return false;
}
return true;
}
std::vector<observer*> observers_;
};
#endif // #include guard
//]
|