You are on page 1of 17

Agile Development

with C++
Code, Culture, Context and Change

Kevlin Henney


• Intent
Š Some observations on agile development with C++
• Content
Š C++ development
Š Culture shock
Š Accidental complexity
Š Dependency management
Š Memetic engineering
Š In closing

XP Day 06 2

it is a non-trivial language Š High degree of complexity to master Š Most programmers working with it can't XP Day 06 4 .C++ Development One reason that life is complex is that it has a real part and an imaginary part. Andrew Koenig XP Day 06 3 Characterising C++ • C++ is a language that is well suited to large- scale systems-level work Š For better or for worse. it retains its C heritage. so it offers good access to underlying machine concepts Š Broad support for various programming styles Š Interfaces well with existing libraries and legacy • However.

systems programming.C++ Systems • C++ is well suited to work that requires the mixing of machine-level and other abstractions Š E. C++ is more often than not let down by the quality of libraries Š E. real-time systems.g. matched only by a corresponding fear of change Š Which leads to a conservative approach to change that accumulates further technical debt XP Day 06 6 . less capable subset of the language • The technical debt is often large. many C++ systems are classified as legacy Š They are large. file management and manipulation. untestable.g. high- performance computing. database usage XP Day 06 5 Legacy: the Other Kind of Inheritance • In part. because of the age of the language. and many domains where C is also favoured • In other domains. use divergent coding styles and are based on an older. games. GUI programming. text processing. embedded systems.

Agility and C++ • Agility is not a binary option based on programming language Š An agile process is as achievable on C++ projects as with any other language — many developers can testify to this • The challenge is often in the culture and with certain technical details Š Many C++ developers favour (or blindly use) techniques that are antagonistic to keeping a code base supple and testable XP Day 06 7 Culture Shock Design and programming are human activities. Bjarne Stroustrup XP Day 06 8 . forget that and all is lost.

as does classic Unix culture and mindset XP Day 06 9 Some Common (Problem) Attitudes • Cleverness is valued Š Template metaprogramming. etc • BUFD or 'design' by wizard Š Design horizons are typically distant or overlooked • Testing is somebody else's problem Š Debugging is seen as the typical response to defects • Maintenance does not involve refactoring Š Automated refactoring tools are not widespread XP Day 06 10 . preprocessor abuse.Culture Can Decelerate Change • Culture is perhaps the greatest challenge to undertaking agile development in C++ Š Although there are different C++ subcultures. many share a certain conservatism. cunning bit twiddling. fear of change and assumptions about what is possible and what is not • Assumptions go beyond the code face to the process model Š Sequential or chaotic development often ingrained Š Open source projects offer a more positive model. micro-optimisation.

TDD Š No shortage of testing frameworks and wisdom • Refactoring and loosely coupled design Š Lack of good refactoring tools is partly down to language and partly culture. Possibly this trend results from a mistaken belief that using a somewhat mysterious device confers an aura of power on the user. Niklaus Wirth XP Day 06 12 . which is baffling — the incomprehensible should cause suspicion rather than admiration.Some Possibilities for Change • Agile macro process with daily heartbeat Š An iterative development lifecycle based on feedback at different time scales. but a lot of refactoring is related to skill and attitude XP Day 06 11 Accidental Complexity Increasingly. ideally. people seem to misinterpret complexity as sophistication. incorporating an at least daily full integration build • Testing and.

. trail) by poor example XP Day 06 13 Criticality of Knowledge • However good any other attribute of a project is. and the nature of legacy and lack of a refactoring culture makes this complexity breed • Complexity also arises from blind use of tools and following various dysfunctional memes Š Most wizards are more like "sorcerer's apprentices" Š Many libraries lead (well. hard Š And beyond the barrier to entry. greater mastery is needed than is perhaps true of other languages XP Day 06 14 . skill is still critical to success Š Knowledge of tools. knowledge of practice. etc • Getting the most out of C++ requires profound skill — knowledge of the language and beyond Š The complexity of C++ introduces a higher barrier to entry than many languages — fall short of it. and you will hit the barrier.Complexity Can Decelerate Change • Insufficient mastery of the language. libraries and associated techniques leads to complexity Š Accidental complexity on C++ projects can be very high..

} bool unlock(const std::basic_string<char> &key) { std::list<std::basic_string<char> >::iterator found = std::find(locked.begin().begin(). XP Day 06 15 Sufficient Knowledge class access_control { public: bool is_locked(const std::string &key) const { return locked. } bool lock(const std::string &key) { return locked. private: std::list<std::basic_string<char> > locked.erase(key). key).insert(locked.erase(found). }.. } return false.count(key) != 0. return found != locked. return true. } bool lock(const std::basic_string<char> &key) { std::list<std::basic_string<char> >::iterator found = std::find(locked. if(found != locked.. if(found == locked. key).... } . key). locked.Insufficient Knowledge class access_control { public: bool is_locked(const std::basic_string<char> &key) const { std::list<std::basic_string<char> >::const_iterator found = std::find(locked. }..end().end(). private: std::set<std::string> locked. locked.. } return false.begin().insert(key).end(). . XP Day 06 16 . return true.end().second.end()) { locked.end()) { locked. . } ..end(). locked. } bool unlock(const std::string &key) { return locked. key).

return *this. catch(. } delete old_body.. representation *body.. private: handle(const handle &). { } . return *this.) . private: class representation { . }. } class representation ~handle(). representation *old_body = body. }. try } } { handle(const handle &). private: body = old_body.body). } representation *body.. { { . return *this. able to address certain classes of problem and certain styles of expression easily and sustainably • The other source of complexity is 'cleverness' Š Because of the barrier to entry and the capabilities of the language. XP Day 06 17 Working with Power Tools • C++ is a very powerful and capable language Š Full mastery makes it a complete and capable tool.. class representation throw.. }. } handle(const handle &). body = new representation(*rhs..The Bad. }.body). ~handle(). the Ugly.. { { std::swap(body. }...body). ... body = new representation(*rhs. }.. copy. cleverness is valued Š Code by über-programmers is often based on speculative generality and gratuitous use of advanced techniques and language features XP Day 06 18 .. . delete body. and the Good class handle class handle class handle { { { public: public: public: handle &operator=(const handle &rhs) handle &operator=(const handle &rhs) handle &operator=(const handle &rhs) { { { if(this != &rhs) if(this != &rhs) handle copy(rhs).. representation *body. ~handle().

Source> interpreter. const std::string &>::do_cast(arg). lexical_cast_impl(std::basic_string<CharType. Source>. typename Allocator> // purpose is hereby granted without fee. provided that this copyright and struct stream_char<const wchar_t *> // permissions notice appear in all copies and derivatives. char>. Target=" + #ifndef DISABLE_WIDE_CHAR_SUPPORT typeid(Target). Source>(interpreter). Source. // Pointer as target type not supported return direct_cast_base<std::string. std::string() + "bad lexical cast: " + throw detail::no_lexical_conversion<Target. // Source==std::wstring? template<typename Target. // Source==char? // who: contributed by Kevlin Henney and enhanced by Terje Slettebø throw detail::no_lexical_conversion<Target. // #if defined(BOOST_NO_STRINGSTREAM) template<typename Type> Source==std::string? typedef std::strstream type. std::string>. } #elif defined(BOOST_NO_STD_LOCALE) std::stringstream stream. CharTraits. template<typename Target> typename mpl::if_<is_same<Source. Source>. typename Source> typename mpl::if_<is_same<Source.unsetf(std::ios_base::skipws). const char *arg) pointer_to_char_to_char_base<Target. pointer_to_char_to_char_base<Target. 2000-2003. if(!(interpreter << arg) || !(interpreter >> result) || return lexical_cast_base<wchar_t. #endif template<typename Target. { } typedef char type. wchar_t arg) ////////////////////////////////////////////////////////////////////////// inline wchar_t lexical_cast_impl(wchar_t *. typename Source> } typename mpl::if_<is_same<Source. { } // #ifdef BOOST_NO_STRINGSTREAM #else // Permission to use. wchar_t. defined(BOOST_NO_CWCHAR) else if(std::numeric_limits<Source>::is_specialized) #define DISABLE_WIDE_CHAR_SUPPORT stream. typename Source> { class no_lexical_conversion : public bad_lexical_cast #if defined(BOOST_NO_STRINGSTREAM) { stream << '\0'. Allocator> pointer_to_char_to_char_base<Target. CharTraits. const std::basic_string<CharType. Source>. Source. #include <boost/config. Allocator> struct select_base // See http://www. const wchar_t *>. typename Source> // string_to_any_base { return direct_cast_base<wchar_t. inline Target lexical_cast_impl(Target *. for most recent version including documentation.eof()) } inline Target lexical_cast_impl(Target *. } // Copyright Kevlin Henney. inline wchar_t lexical_cast_impl(wchar_t *. typename Allocator> typename mpl::if_<is_same<Source. typename Source> #ifdef BOOST_NO_STRINGSTREAM class lexical_stream #include <strstream> { #else public: #include <sstream> lexical_stream() #endif { stream. March 2003 } typename mpl::if_<is_same<Source. // Source==wchar_t }. Source. const wchar_t *>. Allocator> *.precision(std::numeric_limits<Target>::digits10 + 1). CharTraits. wchar_t>.org for most recent version including documentation.length()!=1) return string_to_any_base<Target. { ////////////////////////////////////////////////////////////////////////// // This software is provided "as is" without express or implied warranty. }. Allocator>. // mpl::apply_if doesn't work well for MSVC 6 here. Allocator> *.str(). { } public: template<typename InputStreamable> virtual ~bad_lexical_cast() throw() bool operator>>(InputStreamable &output) { { } return !is_pointer<InputStreamable>::value && }.hpp> ////////////////////////////////////////////////////////////////////////// { string_to_char_base<Target. { return direct_cast_base<std::wstring. typename Source. wchar_t> // when: November 2000. std::string>. CharTraits. const char *>. const char *arg) inline std::wstring lexical_cast_impl(std::wstring *. Source. Source=" + typeid(Source). std::string>.unsetf(std::ios::skipws). // Source==std::string? #include <boost/limits.eof(). modify. Source arg) throw detail::no_lexical_conversion<Target. char *>. CharTraits.hpp> #include <boost/limits. typename CharType> template<typename CharType. std::wstring>. Source=" + typeid(Source). 2000-2003. // Target==Source? } struct direct_cast_base template<typename Target. return direct_cast_base<std::string. return Target(1. typename Source> } >::type class bad_lexical_cast : public std::bad_cast struct string_to_char_base >::type { { template<typename Target> >::type. // static initialization fails on MSVC6 #if defined(BOOST_NO_STRINGSTREAM) }. Source>. return lexical_cast_base<Target. // Target==wchar_t? virtual ~bad_lexical_cast() throw() { { typename mpl::if_<is_same<Source. }. // Source==char? { return direct_cast_base<std::basic_string<CharType. &>::do_cast(arg). Source arg) { // { typedef typename mpl::if_<is_same<Target. wchar_t *>. modify. #include <boost/mpl/if. // static initialization fails on MSVC6 } >::type. typename mpl::if_<is_same<Source. // Beman Dawes. return result. { { return string_to_any_base<Target. template<> // and other Boosters struct widest_char<char. const Type &arg) string_to_any_base<Target. { { template<typename Target> any_to_string_base<Target.boost. CharTraits.str(). const Source &>.name()) } char_to_string_base<Target. template<typename TargetChar. typename Interpreter> return lexical_cast_base<Target. throw detail::no_lexical_conversion<Target. + ". stream >> output && (stream >> std::ws). struct stream_char<wchar_t> Target result. char *arg) inline std::string lexical_cast_impl(std::string *. char *arg) lexical_cast_base<Target. string_to_char_base<Target. // enhanced with contributions from Terje Slettebø. char>. CharType *>::do_cast(arg). Allocator> &arg) typename mpl::if_<is_same<Source. typename Source. // Source==wchar_t *? } inline Target lexical_cast_impl(Target *. Dave Abrahams.hpp> throw detail::no_lexical_conversion<Target. virtual ~no_lexical_conversion() throw() return true. typedef TargetChar type. typename CharType> return pointer_to_char_to_char_base<char. typename stream_char<Target>::type. pointer_to_char_to_char_base<Target. } } return pointer_to_char_to_char_base<wchar_t. // Copyright Kevlin Henney. } typename stream_char<Source>::type>::type char_type. // Source==const char *? #include <string> }. const wchar_t *. } throw detail::no_lexical_conversion<Target. char>.name() + ". } { } typename mpl::if_<is_same<Source. Source>. return pointer_to_char_to_char_base<char. // Source==char *? namespace detail static Target do_cast(Source arg) { string_to_any_base<Target. wchar_t>::do_cast(arg).Source) inline std::string lexical_cast_impl(std::string *. const char *>::do_cast(arg). { }. wchar_t *>::do_cast(arg). char_to_string_base<Target. Allocator> *. const Type &>::do_cast(arg). Allocator>. Source. Daryle Walker. std::strstream stream. // Target==char? // what: lexical_cast custom keyword cast if(!(interpreter >> result) || !(interpreter >> std::ws). const char *. // Source==char *? return result. Source. All rights reserved. and distribute this software for any template<> // purpose is hereby granted without fee. wchar_t *? { } lexical_cast_base<Target. }. inline Target lexical_cast(const Source &arg) ////////////////////////////////////////////////////////////////////////// return pointer_to_char_to_char_base<wchar_t. Peter Dimov. Allocator>. lexical_cast_base<Target. typename CharTraits. Allocator>. typename CharTraits. Source.hpp> namespace detail // stream wrapper for handling lexical conversions #include <boost/type_traits/is_pointer. copy. #include <typeinfo> } template<typename CharType. }. return arg[0]. CharTraits. typename CharType> inline std::wstring lexical_cast_impl(std::wstring *. std::wstring>. wchar_t>::do_cast(arg). // Target==std::string? #endif static Target do_cast(Source arg) { typename mpl::if_<is_same<Source. typename Source> !(interpreter >> std::ws). char> else if(std::numeric_limits<Source>::is_specialized) } } >::type interpreter. const std::wstring &arg) { inline wchar_t lexical_cast_impl(wchar_t *. Source>(interpreter). wchar_t>. const wchar_t *>. Allocator> &. #endif } ~lexical_stream() #ifdef BOOST_NO_INTRINSIC_WCHAR_T { #include <cwchar> #if defined(BOOST_NO_STRINGSTREAM) #endif stream. template<typename Target. return result. CharTraits. >::type } } >::type setup_interpreter<Target. char *>. March 2003 { typedef wchar_t type. char *>. public: static Target do_cast(Source arg) inline Target lexical_cast_impl(Target *. char>. { // typedef wchar_t type. // Source==const void setup_interpreter(Interpreter &interpreter) } return direct_cast_base<std::string. const std::basic_string<CharType. if(!(interpreter << arg && interpreter >> result)) }.boost. inline Target lexical_cast_impl(Target *. Target result. Source. // Target==std::wstring? "source type value could not be interpreted as target. Source arg) { { return direct_cast_base<std::wstring. typename Allocator> typename mpl::if_<is_same<Source. BOOST_STATIC_ASSERT((Target **) 0). wchar_t>::do_cast(arg). typename Source. direct_cast_base<Target. Source>. // when: November 2000. CharType *arg) typename mpl::if_<is_same<Target. const Source &. direct_cast_base<Target. Allocator> >::type. typename Source> return string_to_any_base<Target. // Source==char *? defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) setup_interpreter<Target. const std::basic_string<CharType. wchar_t> return description. >::type // exception used to indicate runtime lexical_cast failure template<typename Target.str(). template<typename Target. const Source &>. wchar_t *. Source. Source>. typename Source> template<class Target. } #endif }. interpreter. #if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && \ inline Type lexical_cast_impl(Type *. CharType>::do_cast(arg). provided that this copyright and inline CharType // Simulated partial specialisation // permissions notice appear in all copies and derivatives. // Source==wchar_t? { if(arg. Source>(). template<typename Target. Allocator> direct_cast_base<Target. #endif return interpreter. Source>. typename CharTraits. inline std::basic_string<CharType. namespace detail // actual underlying concrete exception type } { bool operator>>(std::string &output) template<typename Target.hpp> // any_to_string_base return char_to_string_base<std::basic_string<CharType. typename mpl::if_<is_same<Target. Source>. std::wstring>. >::type const std::string description. return direct_cast_base<Type. wchar_t>::do_cast(arg).precision(std::numeric_limits<Source>::digits10 + 1). Target result. copy. lexical_cast_base<Target.freeze(false). wchar_t>. Target=" + return arg[0]. const wchar_t *>::do_cast(arg). const CharType *arg) typename mpl::if_<is_same<Source. // Source==const wchar_t *? { struct char_to_string_base return string_to_any_base<Target. CharTraits. template<> } struct stream_char<wchar_t *> } { typedef wchar_t type.precision(std::numeric_limits<Target>::digits10 + 1). // Source==const template<typename CharType> return arg. // Source==std::string? } { direct_cast_base<Target. arg). const std::basic_string<CharType. Source arg) *? { inline std::string lexical_cast_impl(std::string *. Terje Slettebø. >::type type. { }. #else return string_to_char_base<CharType. typename Allocator. // Source==wchar_t? typeid(Target). Allocator> *. wchar_t>. Source>().precision(std::numeric_limits<Source>::digits10 + 1). // with additional fixes and suggestions from Gennaro Prota. : description( return true. Source>. #endif namespace boost } { bool operator<<(const Source &input) // exception used to indicate runtime lexical_cast failure { class bad_lexical_cast : public std::bad_cast return stream << input. return direct_cast_base<std::wstring. typename Source> template<typename Target. // Source==wchar_t *? }. typename CharTraits. lexical_cast_base<Target. char> ////////////////////////////////////////////////////////////////////////// } >::type #ifdef BOOST_NO_STRINGSTREAM >::type #include <strstream> template<typename Target. Source. std::wstring>. CharTraits. } } inline wchar_t lexical_cast_impl(wchar_t *. All rights reserved. #include <string> }. const char *arg) Source==std::wstring? if(std::numeric_limits<Target>::is_specialized) { { lexical_cast_base<Target. typename mpl::if_<is_same<Source. const std::wstring &>::do_cast(arg). Source>(). { { typename mpl::if_<is_same<Source. } typename mpl::if_<is_same<Source. { typedef wchar_t type. typename SourceChar> // struct widest_char // what: lexical_cast custom keyword cast { // who: contributed by Kevlin Henney. wchar_t arg) >::type : description( if(arg[0] == Target() || arg[1] != Target()) { >::type. const wchar_t *arg) }. CharTraits. char>. Allocator> &arg) // select_base // throw detail::no_lexical_conversion<Target. typename CharTraits. interpreter. std::string>. wchar_t *arg) direct_cast_base<Target. std::string() + "bad lexical cast: " + } "source type value could not be interpreted as target. >::type struct lexical_cast_base } } >::type { >::type static Target do_cast(Source arg) inline char lexical_cast_impl(char *. static Target do_cast(Source arg) return string_to_char_base<wchar_t. #if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) || \ typename stringstream<CharType>::type interpreter. CharTraits. template<typename Source> inline std::wstring lexical_cast_impl(std::wstring *. CharTraits. inline std::wstring lexical_cast_impl(std::wstring *. const wchar_t *arg) { template<typename Target. class Source> // inline std::basic_string<CharType. wchar_t>. typename Source> Target lexical_cast(Source arg) #ifndef DISABLE_WIDE_CHAR_SUPPORT { template<> detail::lexical_stream<Target. CharType>::do_cast(arg). const Source &. Source. and distribute this software for any typename stringstream<CharType>::type interpreter. template<typename Target. >::type } inline char lexical_cast_impl(char *. direct_cast_base<Target. #undef DISABLE_WIDE_CHAR_SUPPORT template<> #endif XP Day 06 20 . const char *>. template<typename CharType. char>. } template<typename Target. CharType>::do_cast(arg). const std::wstring &arg) { } { { return char_to_string_base<std::wstring. typename Source> typename mpl::if_<is_same<Target. }. typename CharTraits. wchar_t *>. // Source==std::wstring? virtual const char *what() const throw() static Target do_cast(Source arg) direct_cast_base<Target. #include <boost/static_assert. #include <typeinfo> } #include <boost/config.hpp header -------------------------------------------// setup_interpreter<Target. char>::do_cast(arg). Source>. typename Allocator> >::type #else struct any_to_string_base inline std::basic_string<CharType. char *arg) typename mpl::if_<is_same<Target. template<typename Target. CharTraits. wchar_t *arg) >::type { { { >::type typename stringstream<CharType>::type interpreter. const CharType any_to_string_base<Target. char>::do_cast(arg)..hpp> { template<typename Target.hpp> lexical_cast_impl(std::basic_string<CharType. char *>::do_cast(arg). wchar_t>::do_cast(arg).hpp> if(!(interpreter << arg)) template<typename CharType. arg). and neither does #define BOOST_LEXICAL_CAST_INCLUDED #endif } // inheriting from a metafunction // Boost lexical_cast. // Boost lexical_cast. char *. } typename mpl::if_<is_same<Source. const std::wstring &>::do_cast(arg). { } } #endif virtual const char *what() const throw() private: { typedef typename widest_char< return description. Source>. // Source==const wchar_t *? namespace detail }. const wchar_t *>::do_cast(arg). CharTraits. typename CharType. inline Target lexical_cast_impl(Target *. wchar_t>. private: const std::string description. typename Source. // Source==const char *? #include <boost/type_traits/same_traits. #include <sstream> { lexical_cast_impl(std::basic_string<CharType. lexical_cast_impl(std::basic_string<CharType. 2003.c_str(). char> { *>::do_cast(arg). { inline Target lexical_cast_impl(Target *. typename mpl::if_<is_same<Source. const wchar_t *arg) >::type } } { >::type private: }. return direct_cast_base<std::basic_string<CharType. const Source &>. wchar_t *>::do_cast(arg). Source>(). wchar_t *>. wchar_t> { { >::type public: static Target do_cast(Source arg) template<typename Target> >::type no_lexical_conversion() { inline Target lexical_cast_impl(Target *. public: #endif no_lexical_conversion() output = stream. if(!(interpreter << arg)) lexical_cast_impl(CharType *. const Source &>. CharTraits. { typename mpl::if_<is_same<Source. class no_lexical_conversion : public bad_lexical_cast struct pointer_to_char_to_char_base } lexical_cast_base<Target. char *>::do_cast(arg). wchar_t *arg) { return select_base<Target. return string_to_any_base<Target. template<typename Type> #endif struct stream_char }. char>::do_cast(arg). CharType arg) typename mpl::if_<is_same<Source. Source>(). char *? struct stringstream } } string_to_any_base<Target. // // Permission to use. Source>(). virtual ~no_lexical_conversion() throw() template<typename Target.c_str(). const Source &>. namespace boost }. const std::string &arg) >::type { { >::type template<typename Target. namespace detail // selectors for choosing stream character type #else { std::basic_stringstream<char_type> stream. direct_cast_base<Target. } { } return detail::lexical_cast_impl(static_cast<Target *>(0). // Source==wchar_t? typedef std::basic_stringstream<CharType> type. Source>. wchar_t arg) } struct string_to_any_base inline wchar_t lexical_cast_impl(wchar_t *. wchar_t>.name()) bool operator>>(std::wstring &output) { { } output = stream. Allocator> #endif XP Day 06 19 Comes Great Responsibility #ifndef BOOST_LEXICAL_CAST_INCLUDED struct stream_char<std::wstring> #define BOOST_LEXICAL_CAST_INCLUDED { typedef wchar_t type. wchar_t>::do_cast(arg). #ifndef BOOST_LEXICAL_CAST_INCLUDED typename stringstream<CharType>::type interpreter(arg). Source>(). // #endif // See http://www. #else !defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) { typename mpl::if_<is_same<Source.hpp header -------------------------------------------// }. // This software is provided "as is" without express or implied warranty. } template<typename Target> typename mpl::if_<is_same<Source. Source>::type::do_cast(arg). #if defined(BOOST_NO_STRINGSTREAM) || \ defined(BOOST_NO_STD_WSTRING) || \ if(std::numeric_limits<Target>::is_specialized) defined(BOOST_NO_STD_LOCALE) || \ stream. // See end of this header for rights and permissions. typename Allocator> direct_cast_base<Target.. inline std::basic_string<CharType.eof()) return any_to_string_base<std::basic_string<CharType. const char *>. } typename mpl::if_<is_same<Source.With Great Power. Source>(interpreter). // void lexical_cast_impl(Target **. template<typename CharType. const char *>::do_cast(arg). typename Source> { typename mpl::if_<is_same<Source. wchar_t>.

.. etc. separation of function declaration and definition. Bjarne Stroustrup XP Day 06 21 Coupling Can Decelerate Change • Dependency management is critical to agility Š This is true in general. shorter lunch times XP Day 06 22 . just more so in C++ Š Header file inclusion. visibility of private data. templates and inlines.Dependency Management Express independent ideas independently. all introduce additional (but not insurmountable) challenges • Dependency management should be pursued aggressively — even pathologically Š Dependencies have many effects — longer build and development times.

many projects are pretty good Š Clearly separated subsystems. e.cpp files • But many are pretty poor Š Arbitrary partitioning (e.Single-Responsibility Headers • When it comes to partitioning code across headers. a separate header for each nested class) XP Day 06 23 Lightweight Headers • Headers also need to be lightweight Š Avoid defining inlines unless strictly necessary Š Be judicious with definition of templates — but this does not mean avoid templates Š Use forward declarations where possible. use of anonymous namespaces rather than classes for holding and defining private static constants XP Day 06 24 .g. single-responsibility headers with corresponding . both instead of headers and to indirect private representation (à la Cheshire Cat or Pimpl) Š Push as much as possible into the . all constants bundled together in the same header) Š No partitioning (Visual C++ has a lot to answer for) Š Fragmented partitioning (e.cpp file.g.g.

cut dependencies and use delegation. many current coding recommendations do the opposite Š Class hierarchies can become so jammed with purpose that they lead to (develop)mental gridlock • Fortunately.Inheritance Tax • Inheritance is the strongest form of coupling in a C++ class system Š In spite of much advice to reduce coupling. rearranging the type system to introduce more 'space' is compiler checkable Š The absence of tests need not be a problem XP Day 06 25 Interface Classes • Fully abstract classes offer the simplest technique for fully isolating dependencies Š Isolate both internal and external dependencies Š Allows use of Mock Objects rather than preprocessor hacks for testing Š Simplifies evolution and response to change • The use of interface classes needs to be uncompromising Š No implementation means no implementation XP Day 06 26 .

even when all concerned were sure it never would • There are many other (counter)examples.g.g.g. long functions) and adds friction to refactoring Š Hungarian Notation reduces readability.Memetic Engineering Do not use Hungarian public data is still surprisingly popular — and still has a habit of needing to be changed. including NVI and Singleton XP Day 06 28 . http://msdn2.aspx XP Day 06 27 Bad Memes Can Decelerate Change • There are many unquestioned design styles and ingrained habits that cause problems Š E. distracts from the root problem (e.

}.. . virtual void do_rollback() = 0... { virtual void rollback() = 0. private: void rollback(). virtual ~command(). but has a distinct form and (in)distinct motivation • NVI lacks clear benefits and sound motivation Š Based on speculative generality and does not solve a particular problem well — the supposed need for it normally is normally a sign of a deeper problem XP Day 06 29 Decluttering class command class command { { public: public: void execute() virtual void execute() = 0.. void rollback(). XP Day 06 30 . void execute(command *).Non-Valuable Idiom • Some have proposed Non-Virtual Interfaces (NVI) as a good practice guideline Š NVI suggests virtuals should be private and wrapped in public non-virtuals Š It has structural similarities with Template Method. }. . }. void rollback() { class command_processor do_rollback(). } }. virtual void do_execute() = 0. { } public: virtual ~command(). do_execute(). class command_processor { public: void execute(command *).

It is never any use to oneself.Deglobalisation • Singleton is a popular technique — and classic symptom of design problems Š Similar statements are true of the Monostate pattern • Modifiable static state makes evolution and testing unnecessarily challenging Š Much static state implies a missing Manager object Š Context-related globals are better off passed as Context Objects and global services should follow a plug-in rather than a hardwired style. e. Oscar Wilde XP Day 06 32 .g. Strategy XP Day 06 31 In Closing The only thing to do with good advice is to pass it on.

Taming Complexity • Part of C++'s challenge is in understanding its (many varied and subtle) mechanisms... Š Of which there are more than enough to distract or detract from other programming goals • And in part in finding the simplifying assumptions and styles that marshal them Š It is these that afford responsiveness to change • But keep in mind that not all impediments to change and agility are to be found in the code XP Day 06 33 .