subject.hpp 5.1 KB

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