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
//]
|